Reference Manual

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Product and documentation by Aparajita Fishman

Copyright and Trademarks

All trade names referenced in this document are the trademark or registered trademark of their respective holder.

Active4D is copyright Aparajita Fishman and Victory-Heart Productions.

4th DIMENSION, ACI, ACI US, and 4D Compiler are registered trademarks and 4D, 4D Server, 4D Client, and 4D Insider are trademarks of ACI/ACI US, Inc. or 4D, Inc.

Windows is a trademark of Microsoft Corporation.

Macintosh and Mac OS are trademarks of Apple Computer, Inc.

JavaScript and Java are trademarks of Sun Microsystems, Inc.

Acknowledgements

First of all, thanks to the makers of PHP for giving me the vision of a better way.

Thanks to the client who asked me to generate every single character of HTML in 4D code, which inspired me to come up with a better way.

Thanks to Mike Erickson for his ongoing inspiration and support and for convincing me that Active4D was worth doing. I hope he is right.

Thanks to Steve Willis and Jim Crate of Deep Sky Technologies for their invaluable assistance with the web server.

Thanks to all of the users for their feedback and encouragement.

And thank you to David Adams for so kindly including a chapter about Active4D in "The 4D Web Companion" and for allowing me to include his HTTP chapter with these docs.

Table of Contents

Acknowledgements ii

Table of Contents iii

Introduction 1

What is Active4D? 1

HTTP Web Server 1

Server-Side 2

HTML-Embedded 2

Scripting Language 2

Development Environment 3

An Example 4

What Can Active4D Do? 4

Database and Protocol Support 5

A Brief History of Active4D 5

Installation 7

Plugin Archive Contents 7

Into MAC4DX/WIN4DX Folder 7

Documentation Folder 8

Shell Archive Contents 8

Main Folder 8

Active4D Folder 8

Active4D Uploads 9

MAC4DX/WIN4DX Folder 9

"web" Folder 9

"web decoy" Folder 9

Documentation Archive Contents 9

Demo Archive Contents 9

Key Files 10

Key File Installation 10

Key File Info 10

Version Checking 10

License Types 11

Timeouts 11

Trial License 11

Developer License 11

Deployment License 11

OEM License 12

Installation Options 12

Starting a Database from Scratch 12

Installing into a Non-Active4D Database 13

Updating an Existing Active4D 2.0 Database 14

Moving an Active4D Database from 4D 6.5 to 4D 6.7+ 14

Post-Installation Configuration 15

Configuring for 4D 15

Configuring for ITK 15

Configuring 4D Client as a Web Server 16

Using the Pre- and Post-Execute Hooks 17

Pre-Execute Hook 17

Post-Execute Hook 18

Configuration 19

The Active4D Directory 19

Libraries and the Active4D Directory 19

Configuration Files 20

The Default Directory 20

Path Format 20

The Standard Search Path and Path Lists 22

Active4D.ini 22

ExtensionMap.ini 24

Realms.ini 24

VirtualHosts.ini 24

Deprecated Plugin Configuration Commands 24

Security 27

Source Code Security 27

Web Server Security = Source Code Security 27

Circumventing Active4D 27

The Whole Fortune 500 Can't Be Wrong 28

Source Code Encryption 29

Potential Attacks 29

Executing/Accessing Non-Web Files 29

The "safe script dirs" Config Option 29

Misusing Document Commands 30

The "safe doc dirs" Config Option 30

Spoofing Form Variables 30

The "auto create vars" Config Option 31

Uploading Huge Files 31

HTTP Server 33

What Is a Web Server? 33

Active4D + TCP Layer = Web Server 34

Web Server Issues 34

HTTP Fundamentals 35

Active4D HTTP Request Handling 35

Executable vs. Non-executable Files 35

Request Header Parsing 36

POST Handling with the 4D 6.5 Web Server 37

POST Handling With the 4D 6.7+ Web Server 37

POST Handling With Stream-based TCP Layers 37

File Upload Handling With 4D's Web Server 38

Executable Request Handling 38

Non-Executable Request Handling 40

Configuration 41

Active4D.ini 41

ExtensionMap.ini 43

User Authentication 44

Realms.ini 45

Virtual Hosting 46

VirtualHosts.ini 47

A Virtual Host Example 47

HTTP Error Handling 48

Invoking Active4D 51

Types of Execution 51

Request Execution 51

A4D Execute <type> request Parameters 51

A4D Execute <type> request 52

A4D Execute BLOB request 56

A4D Execute request 56

A4D Execute stream request 57

A4D Execute 4D request 57

StatusCallback 59

ReceiveCallback 59

Direct Execution 60

Uses for Direct Execution 60

Interpreter 61

Flow of Execution 61

Embedding Source Code 61

Input Parsing 62

Language Syntax 64

Source Code Structure 64

Case Sensitivity 65

Expression-based 65

Comments 65

Identifiers 66

Data Types 66

Compiler Declarations 67

Array Support 67

Pointer support 67

Literals 68

User-defined constants 68

Typing of Values 68

Operators 69

New Active4D Operators 69

Picture Operators 70

Enhanced Operators 70

Control Structures 71

for each/end for each 71

break 71

return 71

continue 71

exit 72

Examples 72

Working with Paths 73

URL-Style Paths 73

Absolute vs. Relative Paths 73

Path Utilities 74

Path Limits 74

Including Other Files 74

Uses of Included Files 75

Including only once 75

Calling 4D Methods 75

Parameter Passing 76

Indirect Method Calls (aka Poor Man's method pointers) 76

Collections 77

Collection Handles 77

Local vs. Global Collections 77

Using Collections 78

Referencing Collection Values 78

Iterating Over a Collection 79

HTTP Data Access 80

Request Data 80

Auto Variable Creation 81

Testing Form Buttons 82

Response Data 83

Working with Character Sets 83

Platform Character Set 84

Output Character Set 84

Output Encoding 85

HTTP Request Decoding 85

Working with Files 85

Error Handling 86

Script Timeout 86

Methods 87

Defining Methods 87

Method Declaration 87

Method Name 87

Method Parameter Declaration 88

Method Parameters 88

Parameter Type 88

Scope 89

Referencing "Global" Local Variables 90

Pass by Reference 90

Default Parameters 91

Returning Values 92

Libraries 95

Library Definition 95

Importing Libraries 96

Load Errors 97

The "lib dirs" Config Option 97

The "lib extension" Config Option 97

Library Namespace 97

Name Resolution 97

Library scope 98

The "global" library 99

Refreshing Libraries 99

The "auto refresh libs" Config Option 99

The "refresh interval" Config Option 100

Differences from Active4D 1.0 100

Event Handlers 103

Modifying the Active4D Library 103

Event Handler Methods 104

On Application Start 104

On Application End 104

On Request 104

On Authenticate 105

On Session Start 106

On Session End 106

On Execute Start 107

On Execute End 107

Command Reference 109

4D Commands 109

4D 6.5 Commands 109

Using a Default Table 109

4D Command List 110

4D Command List (cont.) 111

Active4D Commands 112

Active4D Command List 113

Command Syntax 115

Arrays 116

add element 117

append to array 117

clear array 118

is array 118

join array 119

multisort arrays 120

multisort named arrays 120

resize array 121

SELECTION/SELECTION RANGE TO ARRAY 121

set array 122

Collections 124

collection 125

new collection 125

new local collection 126

new global collection 126

collection to blob 127

blob to collection 127

copy collection 128

merge collections 128

clear collection 129

get collection 129

get collection array 130

get collection array size 131

get collection item 131

get collection keys 132

set collection 132

set collection array 133

is a collection 134

collection has 134

count collection items 135

delete collection item 135

Database 136

Trigger Errors 136

get field numbers 137

get field pointer 137

QUERY 138

QUERY SELECTION 138

SET QUERY DESTINATION 139

Date and Time 140

UTC Commands 140

day of year 141

get utc delta 141

local datetime to utc 141

local time to utc 142

utc to local datetime 142

utc to local time 142

week of year 143

Debugging 144

current line number 145

current method name 145

dump locals 146

write to console 146

Error Handling 147

get error page 148

get log level 148

in error 148

log message 149

set error page 149

set log level 150

File Uploads 151

How File Upload Works 151

Referencing File Uploads 151

The Importance of Filename Extensions 152

Uploads and 4D's Web Server 152

Upload Auto-Deletion 152

copy upload 153

count uploads 153

get upload content type 154

get upload encoding 154

get upload extension 154

get upload remote filename 155

get upload size 155

save upload to field 156

Form Variables 157

Form Variable Items 157

Multiple-choice Form Fields 157

form variables 158

form variables has 158

get form variable 158

get form variable choices 159

get form variable count 160

get form variables 161

count form variables 162

Globals 163

Locking and Unlocking the Globals 163

globals 165

globals has 165

get global 165

get global array 166

get global array size 167

get global item 167

get global keys 168

set global 168

set global array 169

count globals 169

delete global 170

lock globals 170

unlock globals 170

Iterators 171

Using for each 171

Using Iterators 171

Iterator Validity 172

for each/end for each 173

more items 173

next item 174

get item key 174

get item value 174

get item type 175

get item array 175

is an iterator 175

Language 176

call 4d method 177

call method 177

choose 178

define 180

EXECUTE 182

global 182

import 183

include 184

include into 184

longint to time 185

redirect 186

require 186

throw 187

time to longint 187

Math 188

max of 189

min of 189

random between 190

Pictures 191

write gif 192

write jpeg 192

write jpg 193

Queries 194

QUERY/QUERY SELECTION 195

ORDER BY 195

Query Params 196

Query Params Items 196

Duplicate Query Parameters 196

query params 197

query params has 197

get query param 197

get query param choices 198

get query param count 199

get query params 199

count query params 200

build query string 200

Request Cookies 202

request cookies 203

get request cookie 203

get request cookies 203

count request cookies 204

Request Info 205

request info 206

get request info 206

get request infos 206

count request infos 207

Request Value 208

get request value 209

Response Buffer 210

buffer size 211

response buffer size 211

clear buffer 211

clear response buffer 211

get response buffer 212

save output 212

end save output 213

set output charset 214

get output charset 214

set output encoding 215

get output encoding 216

write 216

write blob 217

writebr 218

writeln 218

writep 219

write raw 219

= 220

Response Cookies 221

Cookie Fields 221

Limitations with the 4D 6.7 Web Server 221

response cookies 222

get response cookie 222

get response cookies 223

set response cookie 223

set response cookie domain 224

get response cookie expires 224

set response cookie domain 224

get response cookie domain 225

set response cookie path 225

get response cookie path 226

count response cookies 226

delete response cookie 226

abandon response cookie 227

Response Headers 228

response headers 229

get response header 229

get response headers 229

set response header 230

count response headers 230

delete response header 230

Response Properties 231

get cache control 232

set cache control 232

get expires 232

set expires 233

get expires date 233

set expires date 233

get content type 234

set content type 234

get content charset 234

set content charset 235

Script Environment 236

current platform 237

get license info 237

get time remaining 238

get version 238

parameter mode 239

set platform charset 239

get platform charset 240

set script timeout 240

get script timeout 240

set current script timeout 241

get current script timeout 241

Selecting Records 242

Loading Related Records 242

Configuring Related Record Auto-loading 242

Compatibility with Active4D 2.0.x 243

Examples 243

auto relate 245

FIRST/LAST/NEXT/PREVIOUS RECORD 245

GOTO RECORD 246

GOTO SELECTED RECORD 246

Sessions 247

The Active4D Session Architecture 247

Session ID 247

Session Events 248

When Active4D Sends Session Cookies 248

Session Lifetime 249

Cookieless Sessions 249

Memory Caching of Sessions 250

Session Timeout and Memory Usage 250

Monitoring Memory Usage 251

Session Configuration 251

session 253

session to blob 253

blob to session 253

get session 254

get session array 254

get session array size 255

get session item 255

get session keys 256

set session 256

set session array 257

session has 257

count session items 258

delete session item 258

abandon session 259

session id 259

session internal id 259

session local 260

session query 260

hide session field 260

set session timeout 261

get session timeout 261

get session stats 262

Strings 263

URL Encoding/Decoding 263

String Commands and Multibyte Character Sets 264

capitalize 265

cell 265

compare strings 266

concat 266

enclose 267

first not of 268

first of 268

identical strings 269

last not of 269

last of 269

left trim 270

mac to html 270

param text 271

Position 273

right trim 273

split string 274

String 275

trim 275

url decode 276

url decode path 276

url decode query 276

url encode 277

url encode path 277

url encode query 277

System Documents 278

Document Paths 278

Document Command Enhancements 278

Affected Commands 279

Append document 280

Create document 280

current file 280

current path 281

default directory 281

DELETE FOLDER 282

directory of 282

directory separator 282

extension of 283

filename of 283

get root 283

native to url path 284

Open document 284

RECEIVE PACKET 284

requested url 285

SEND PACKET 285

SET DOCUMENT POSITION 285

url to native path 286

Timestamps 287

Timestamp Format 287

Timestamp Time 287

Timestamp Normalization 287

Using Timestamps 288

timestamp 290

add to timestamp 291

timestamp string 291

timestamp date 292

timestamp time 292

get timestamp datetime 293

timestamp year 293

timestamp month 293

timestamp day 294

timestamp hour 294

timestamp minute 295

timestamp second 295

timestamp millisecond 295

User Authentication 296

auth password 297

auth type 297

auth user 297

authenticate 298

current realm 298

Variables 299

defined 300

get local 301

local variables 301

set local 301

type descriptor 302

undefined 302

variable name 303

Plugin Commands 304

Plugin Command List 304

A4D FLUSH LIBRARY 305

A4D Get root 305

A4D GET SESSION DATA 305

A4D GET SESSION STATS 306

A4D GET LICENSE INFO 307

A4D Get time remaining 307

A4D Get version 307

A4D Import library 308

A4D Native to URL path 308

A4D RESTART SERVER 308

A4D SET ROOT 309

A4D STRIP 4D TAGS 309

A4D URL decode path 309

A4D URL decode query 310

A4D URL encode path 310

A4D URL encode query 310

A4D URL to native path 311

Standard Libraries 313

Using the Standard Libraries 313

a4d.web 314
a4d.utils 315
Batch 316
RowSet 317

Enter the RowSet 317

Using RowSets 319

Debugging 323

Standard Library Methods 323

a4d_web 323

dump array 324

dump collection 324

dump form variables 325

dump query params 325

dump request info 325

dump session 325

dump session stats 326

The Session Editor 327

Using the Session Editor 327

The Session Monitor 329

Displaying the Session Monitor 329

Using the Session Monitor 330

The Active4D Debugging Console 332

Using the Debugging Console 332

Filtering Console Messages 333

Error Handling 335

The Default Error Message 335

Using a Custom Error Page 336

The "error page" Option 336

The "set error page" Command 337

Error Page Variables 337

The Active4D Error Log 338

Log Levels 338

Index of Commands 341

ISO Language Codes 349

Named Constants 351

Grouped by Function 351

Alphabetical 353

 

Introduction

Welcome to Active4D! You have chosen the ultimate environment for building world-class web applications with 4D.

What is Active4D?

A dynamic web scripting environment has four layers: a TCP communications layer, an HTTP server layer, a scripting layer, and a database layer. The different layers can be pictured like this:

The HTTP server builds on top of the TCP layer, and the scripting layer builds on top of the HTTP server. The database is in a separate, independent layer.

Active4D is both an HTTP web server and a server-side HTML-embedded scripting language and development environment.

That's a mouthful. Let's break down the sentence and look at what it means.

HTTP Web Server

An HTTP (HyperText Transfer Protocol) web server is a piece of software that receives HTTP requests, processes those requests and sends an appropriate response to the origin. In essence, this is what a web server does.

4D's built-in web server handles TCP communications and handles most of the HTTP protocol.

Internet Toolkit , better known as ITK, is just what it says -- not an HTTP server, but a toolkit that allows you to write an HTTP server. ITK comes with samples which implement a web server well for most purposes.

HTTP Server Deux is a 4D component that implements a full-fledged HTTP server on top of 4D Internet Commands or ITK.

Active4D implements a full-fledged HTTP server in a plugin. As an HTTP server, it offers:

Ease of setup
  • Active4D is ready to run out of the box. There is no complicated setup needed.
Speed
  • As a plugin, Active4D always runs at native compiled speed, even in an interpreted database. This can mean huge gains in productivity during development.
Features
  • Active4D adds a wealth of advanced features like Virtual Hosting that offer you some of the benefits of dedicated web servers like WebStar.
Server-Side

Scripting languages like JavaScript and Java download their source code to the client browser and execute it there. Active4D, on the other hand, is executed on the server and the source code is removed before the page is sent to the client.

HTML-Embedded

Before the advent of Active4D, to create a dynamic web site using 4D you had to generate all or part of the HTML within 4D methods.

Active4D turns that model inside out -- dynamic HTML generation is directly embedded inside the HTML page.

4D's semi-dynamic tag system, while providing some of the benefits of embedded scripting, still requires you to write many, many 4D methods to handle simple tasks like queries and ordering. In Active4D this is handled directly within the web page. In fact, with Active4D all of your application code can (and in most cases should) exist entirely outside of 4D itself.

Scripting Language

To generate dynamic HTML you must have a programming language. Active4D is a full 4D interpreter in a plugin. To program in Active4D you don't need to learn a new syntax or specialized tag language. In many cases you can literally copy 4D code from a method and paste it into a web page to be executed by Active4D.

Active4D supports all 4D data types except for 2D arrays. It implements over 150 of the most important 4D commands, as well as over 200 new commands which add unmatched power to your web application.

Active4D also adds several often-requested extensions to the language, such as the break and return keywords, as well as pass-by-reference.

Development Environment

Active4D comes with a plethora of built-in debugging tools, both on the client side and on the 4D side. In conjunction with all of the other features Active4D provides, there simply is no more powerful or more productive web development environment for 4D, period. Nothing else comes close.

The fully dynamic, embedded scripting implementation in Active4D is the cutting edge of dynamic web technology. And by using this approach you are in good company. Here are just a few little web sites that use fully dynamic embedded scripting:

barnesandnoble.com
capitalone.com (Visa)
compaq.com
dell.com
deltaairlines.com
firstusa.com (Visa)
gateway.com
geico.com (insurance)
homedepot.com
macconnection.com / pcconnection.com
macmall.com
macwarehouse.com
microsoft.com
sears.com
staples.com
walmart.com

My research shows that over 75% of the Fortune 500 uses dynamic embedded scripting languages in their corporate, client and e-commerce web sites. And the numbers are growing.

An Example

Here's a simple example of what an Active4D page looks like:

<html>

<body>

Here are 5 good reasons to use Active4D:<br>

<%

for ($i;1;5)

writebr("It rocks!")

end for

%>

</body>

</html>

Note that the code is fully embedded in the page, and that except for the writebr command, which writes an expression to the HTML page, the embedded code is 4D code.

During execution, Active4D passes straight HTML through, executes everything inside the <% %> tags, and spits out the following:

<html>

<body>

Here are 5 good reasons to use Active4D:<br>

It rocks!<br>

It rocks!<br>

It rocks!<br>

It rocks!<br>

It rocks!<br>

</body>

</html>

The source code has been replaced with the output of the Active4D code. You can find out more about programming with Active4D in See Interpreter

What Can Active4D Do?

Active4D can do anything that 4D can do in the context of a web page, plus quite a few tricks that would be time-consuming if not impossible to implement in 4D.

If Active4D's commands don't fit the bill, you can write methods and libraries of methods completely within Active4D, using plain text files. And if you need to use a command or plugin that is not in Active4D's language, you can call any 4D method, passing parameters of any type and receiving a result of any type.

Primarily, however, Active4D is designed to act as a conduit between the database and the web page. As such, it excels at the following essential tasks:

  • Collecting information from forms and queries for easy access within your web application
  • Managing user sessions
  • Querying and manipulating data without having to write specialized 4D methods
  • Handling file uploads
  • Formatting text for output to HTML
  • Handling character set issues
Database and Protocol Support

Active4D comes with built-in support for 4D's built-in database engine. You could, without much work, write wrapper methods for the various connectivity plugins that would allow you to work with other databases such as Sybase and Oracle.

In addition, you could also easily write method wrappers around the 4D Internet Commands to allow you to access other internet protocols like SMTP.

A Brief History of Active4D

Several years ago I had the occasion to use another dynamic embedded scripting language called PHP. I was amazed at the power it provided, and more importantly I realized the advantages of the embedded scripting model.

A little later I was hired to work on a vertical market, web-based application that used 4D. I was asked to add a new module to the existing application. To my amazement, this application (which used NetLink) generated every single character of HTML programmatically in a 4D method. This was before 4D offered its web tag system.

Having been exposed to embedded scripting, I quickly saw that this was a fundamentally flawed approach, and I said to myself, "There has to be a better way." So I set about creating one.

I never intended to create Active4D -- it just sort of happened. A few months after I began with a simple 4D-based parser that replaced variable and field names, I had a full working 4D interpreter in a plugin.

A few features and a few months later, Active4D 1.0 was released. That was November of 2000. Since then I had a chance to use Active4D heavily in web projects, and decided it was time to fill in all of the holes in its feature set.

I systematically went down the list of every important feature in the world's leading embedded scripting language, Microsoft's Active Server Pages, also known as ASP (from which Active4D takes its name), and implemented each and every one. And wherever possible I added a few features that ASP doesn't have.

The result is what you see here: Active4D 2.0, the ultimate 4D web environment.

 

Installation

  1. Installation of Active4D is a fairly simple matter and should take no more than a few minutes.

First you must of course download the appropriate files from http://aparajitaworld.com/products/Active4D/downloads.html . Because Active4D supports a number of different platforms and environments, the downloads are organized so that you can easily mix and match just what you need.

There are four elements to Active4D:

Plugin
  • You must choose the platform on which you will run Active4D (Mac or Windows) and download the appropriate archive.
Shell
  • You must choose the appropriate shell for the environment in which you are running Active4D. The shell acts as a conduit between the TCP/IP communications layer and Active4D.
Documentation
  • This manual is part of the plugin archives, but you may also download the documentation separately.
Demo
  • This 4D 6.8 database and web site demonstrates many of the key techniques you will use with Active4D.

Once you have downloaded one of the above archives, you will need to decompress it. Mac archives are in Stuffit 5 format, so you will need Stuffit Expander 5 or better to decompress them. Stuffit Expander is free and readily available from www.aladdinsys.com . Windows archives are in Zip format. There are millions of free Zip expanders available for Windows.

Plugin Archive Contents

Once you decompress the plugin archive, you will find a single folder.

Into MAC4DX/WIN4DX Folder

This folder contains the Active4D plugin itself, which must be placed either in a local or shared MAC4DX folder (on Macintosh) or WIN4DX folder (on Windows).

When you download the ITK or TCP Server Deux shells, there will be other files and plugins which support those shells. They should be placed together with Active4D in a 4DX folder.

Documentation Folder

This folder contains important release information as well as this manual.

Shell Archive Contents

Once you decompress the shell archive, you will find a number of files and folders.

Main Folder

The main folder contains a complete working shell which illustrates how to integrate Active4D with either 4D's web server, ITK, or TCP Server Deux. In addition, the shell contains the forms and methods which implement the server-side debugging features of Active4D.

Active4D Folder

This folder contains libraries and config files which are used by Active4D.

a4d_utils.a4l
  • An Active4D library which implements various useful non-web-related utilities.
a4d_web.a4l
  • An Active4D library which implements various useful web-related utilities. This library makes use of the a4d_utils library.
Active4D.a4l
  • A special Active4D library that contains global event handlers for your application. For more information on event handlers, see See Event Handlers
Active4D.ini
  • A config file which sets most of the options in Active4D.
ExtensionMap.ini
  • A config file which maps filename extensions to Macintosh file types and MIME types.
Realms.ini
  • A config file which maps portions of a URL to security realms.
VirtualHosts.ini
  • A config file which provides the routing table for virtual hosts. For more information on virtual hosts, see See Virtual Hosting.

For more information on libraries, see See Libraries For more information on config files, see See Configuration

Active4D Uploads

This empty folder is used by Active4D to temporarily store uploaded files.

MAC4DX/WIN4DX Folder

This folder contains plugins which are necessary to use Active4D (in addition to the Active4D plugin).

"web" Folder

This folder is where the pages of your web site will go.

"web decoy" Folder

This purposely empty folder is a critical security device which is explained in See Circumventing Active4D.

Documentation Archive Contents

The documentation archive contains three important documents:

Active4D Reference.pdf
  • This document.
Release Notes.pdf
  • Important notes on what has been fixed and added in the most recent releases. Always read this carefully to see if the changes will affect you.
Upgrade.pdf
  • Important notes on how to upgrade from a previous release of Active4D to the current release.
Demo Archive Contents
  1. This demo archive contains a demo 4D 6.8 database and web site that illustrates many key Active4D techniques. To run the demo do this:
  2. Open the database "Active4D_Demo_68.4db".
  3. Open a browser and enter "http://127.0.0.1:8080".
  4. Explore the topics.
  5. Click the "Demo->Session-based" link at the bottom of the left sidebar.
  6. Enter "s*" in the Ingredient field and click Search to show all ingredients that begin with the letter `s'.
  7. Explore the demo and look at the underlying code.
Key Files

Active4D runs in one of several modes, depending on the license currently in force. The license is determined by a key file (or lack thereof), which contains information about you and your license.

Key File Installation

Active4D will look for a key file in two places, in the following order:

  • The same directory as the Active4D plugin
  • The System:Preferences folder on MacOS 9, the <User>/Library/Preferences folder on Mac OS X, the Windows directory on Windows 95/98/XP, the WINNT directory on Windows NT/2000.

If you have a key file in both places, the one in the plugin directory will take precedence.

  • You should always keep a backup of your key file.
Key File Info

Key files contain the following information in encrypted format:

  • The name and company of the key file purchaser
  • The license type
  • The platform for which the key is licensed
  • The IP address for which the key is licensed (only used with deployment licenses)
  • The expiration date of the key's license (keys never expire, only their licenses do)
  • An encoded serial number

You can access this information both through 4D ( A4D GET LICENSE INFO ) and through Active4D ( get license info ).

Version Checking

Encoded in the serial number is the major/minor version of Active4D for which the key is licensed. If you attempt to use an otherwise valid key file with a version of 4D newer than the version encoded in the key file, the license will be temporarily downgraded.

  • Note that version checking does not apply to bug fix releases; i.e. versions 3.0 and 3.0.1 are considered the same version, whereas 3.1 is considered newer.
License Types

Each license has certain limitations of which you should be aware. The license types Active4D supports are:

License

Limits

Trial

Times out after 1 hour of continuous use

Developer

Times out after 8 hours of continuous use

Deployment

Tied to a single IP address, never times out on that address, times out after one week on another address

OEM

No time or IP limitation, requires special 4D code

Special

Variable

Timeouts

The trial license and developer license both have a timeout period. When 4D has run continuously for the timeout period, Active4D becomes disabled. Thereafter any attempts to execute Active4D will return the text "Active4D has exceeded its time limit." At that point 4D must be restarted to reset the timeout and enable Active4D.

Trial License

If Active4D cannot find a key file, or if the key file is incorrect in some way, Active4D operates in trial mode.

Developer License

If Active4D finds a valid key file and the license type is Developer, Active4D operates in developer mode, unless the version check fails, in which case Active4D operates in trial mode.

Deployment License

If Active4D finds a key file and the license type Deployment, Active4D checks the IP address of the host machine against the IP address in the key file. If an exact match is found, Active4D operates in deployment mode with no timeout, unless the version check fails, in which case Active4D reverts to developer mode.

If the host machine is multihoming (supports more than one IP address on one physical machine), Active4D will check all of the host's IP addresses for a match.

If an IP address match cannot be made, Active4D reverts to expired mode , which times out after one week of continuous use. Until you get a new key file, you must restart 4D once a week.

Ideally you should obtain a new key file in advance if you know your application will be moved to a server with a different IP address. However, if you unable to secure a new key file before switching, this scheme allows you to operate Active4D for a full week after switching. During this time you should be able to obtain a new key file. Once you have a new key file, you must restart 4D to have Active4D run in Deployment mode once more.

If you are running Active4D on Server, checking the license status from Client will indicate the license is running in expired mode, since the IP address of the Client cannot match the Server IP address.

To check the license status of a Server-based key file, you must use the get license info command on a page served by Active4D running on Server. For more information on the get license info command, see See get license info.

OEM License

For information on OEM licensing, please contact sales@aparajitaworld.com .

Installation Options

There are several installation scenarios: starting a database from scratch, installing into a non-Active4D database, updating an existing Active4D 2.0 database, and moving an Active4D database from 4D 6.5 to 4D 6.7 or later.

Starting a Database from Scratch

The Active4D shell databases are perfect for use when you are starting a database from scratch, as they are ready to run. You just need to build your database on top of the shell appropriate to TCP/IP layer you plan to use (4D, ITK or TCP Server Deux).

Installing into a Non-Active4D Database

The Active4D environment relies on a small set of 4D methods, lists, forms and styles, all of which have the prefix "A4D_" to prevent name conflicts with existing code (I hope).

There are three different builds of the Active4D shell to choose from, corresponding to three Insider groups in the distribution shell.

Core
  • This is the minimal build necessary to have Active4D function.
Debug
  • This build contains dialogs and methods which enable debugging Active4D from 4D.
Extras
  • This build contains dialogs for executing text and files from 4D. The same functionality is possible through web pages, so you may safely ignore this build.
  • To install Active4D into an existing database, follow these steps:
  • Launch 4D Insider and select File-->Preferences-->Moving and set the preferences to replace an existing method, form, table and list, and to copy a non-existent method, form, table and list.
  • In 4D Insider, open the shell database corresponding to the TCP/IP layer you will be using (4D, ITK, or TCP Server Deux).
  • Select "Groups" from the popup in the Main panel of the Active4D shell window.
  • Open the target database in 4D Insider and move the entire "Active4D_Core" and "Active4D_Hooks" groups into it.
  • If you want to install the Debug build, move the "Active4D_Debug" group from the shell to the target database. If Insider asks you what to do about the missing "Active4D" table, select the option to copy the object.
  • Quit 4D Insider.
  • Copy the Active4D plugin (or an alias thereof) into the local or shared 4DX directory which is accessible to the target database.
  • Open the target database in 4D.
  • In your startup sequence, add a call to the method A4D_Init.
  • If you installed the debug build, also add a call to the method A4D_DebugInit in your startup sequence. Then open the Menu Bar Editor, select a menu bar and select "Connect Menu" from the "Menu" menu. Select the "Active4D" menu and click OK.
  • If you plan on using 4D Client as a web server, follow the instructions in See Configuring 4D Client as a Web Server.
  • Depending on the TCP/IP layer you are using (4D, ITK, or TCP Server Deux), there may be additional configuration to perform. See the relevant section below for instructions on what to do. In particular, if you are using 4D's TCP/IP layer and you are installing into 4D 6.7+, follow the directions in See Moving an Active4D Database from 4D 6.5 to 4D 6.7+ below.
  • Restart 4D to activate your changes.
Updating an Existing Active4D 2.0 Database
  1. To update an existing Active4D 2.0 database to Active4D 3.0, follow these steps:
  2. Make a backup copy of your structure.
  3. If you have made any changes to the Active4D shell, you will need to make note of the changes you made so they can be integrated into the new shell.
  4. Open your database and delete all Active4D methods, forms, and styles.
  5. Follow the steps above for installing into a non-Active4D database.
  6. If you made changes in the old A4D_InitHook method, note that its functionality has been split up between A4D_InitHook and A4D_DebugInitHook. You must reimplement your changes in the appropriate new hook.
  7. If you previously had patched On Web Connection or A4D_ITK_Slave in order to do custom pre- or post-processing of a request, there are now built in hooks for doing this sort of thing, and you are strongly encouraged to use them if possible. See See Using the Pre- and Post-Execute Hooks for more information on how to use the new hooks.
    • If you called any Active4D execute commands in your code, you must remove the $contentType parameter from those calls. See See A4D Execute <type> request Parameters for the correct parameter usage. Failure to do this will most certainly lead to a fiery death.
Moving an Active4D Database from 4D 6.5 to 4D 6.7+
  1. The Active4D shell for 4D comes configured for 4D 6.5. If you are installing into 4D 6.7 or later, or you need to move to those versions at a future date, follow these steps:
  2. Make a backup copy of your structure and data.
  3. Open your database in the newer version of 4D, and allow 4D to convert the structure and data.
  4. Open the A4D_Init method. You will see two clearly marked sections of code, one for 4D 6.5, one for 4D 6.7+.
  5. Remove the 4D 6.5 code section.
  6. Remove the If(False)/End if that brackets the 4D 6.7+ code section.
  7. Uncomment the lines in the 4D 6.7+ code section that are marked as needing uncommenting.
  8. Close A4D_Init and follow steps 3-6 for the On Web Connection database method.
Post-Installation Configuration

Depending on the TCP/IP layer you are using (4D, ITK or TCP Server Deux), there may be some extra configuration necessary if you are installing Active4D for the first time.

Configuring for 4D
  1. If you are using 4D's web server to handle TCP/IP communications, there are a few steps you must take:
  2. Create a directory called "web_decoy" in the database structure directory if you are web serving with 4D Server or Standalone, or in the 4D Client application directory if you are web serving with 4D Client.
  3. Install the 4D_InternetCommands plugin appropriate for your 4D version in your 4DX directory.
  4. Open your database and go to the "Web Server I" tab of the Database Properties dialog. Make sure "Publish Database at Startup" is checked.
  5. In the same tab, enter "web_decoy" in the "Default HTML Root" field.
  6. Go to the "Web Server II" tab of the Database Properties dialog and check both "Send Extended Characters Directly" and "Use 4DVAR Comments instead of Brackets".
  7. In the same tab, set the "Pages Cache Size" to zero.
  8. Click OK to accept your changes and restart 4D.
    • If you are using 4D 6.8+ under Mac OS X, you cannot use port 80 (the default HTTP port) unless you launch 4D as the root user. Discussing the available options for dealing with this is beyond the scope of this manual. You can find information about this topic by searching Monkeywerks ( http://www.pdm-inc.com/mky.htm ).
Configuring for ITK
  1. If you wish to use ITK as the TCP/IP communications layer for Active4D, do the following:
  2. Go to the "Web Server I" tab of the Database Properties dialog and uncheck "Publish Database at Startup".
  3. Click OK to save the changes.
  4. In the On Exit database method, add this line of code:
    } A4D_ExitingDB:=True

ITK's behavior as a web server can be configured using the 4D list "A4D_ITKConfig", which contains the following items:

Item

Description

IP=0.0.0.0

Defines the IP addresses on which ITK will listen. Using "0.0.0.0" will listen on any IP address. Specifying an address such as "192.168.1.13" will cause ITK to only listen on that address.

Port=80

Defines the default port on which ITK will listen.

NumSlaves=7

Number of stream handling processes to preallocate. Twice this number of HTTP listeners are created.

NumSSLStreams=4

Number of SSL listeners to preallocate. Set this to zero to disable SSL.

CloseDelay=120

How many ticks to wait when attempting to close a stream.

By changing the numbers in the various items, you can change how Active4D's shell sets up ITK.

  1. If you are using ITK 2.5 Pro and would like to use SSL streams, you must follow these steps:
  2. Open the method "A4D_InitSSLHook".
  3. Set $outCertPath, $outCertKeyPath, and $outCertPassword to the values for your SSL certificate, SSL private key, and SSL password.
  4. Close the method and restart 4D to have your changes take effect.

 

Configuring 4D Client as a Web Server

Ordinarily Active4D becomes inactive on 4D Client, since it assumes it is serving web pages from 4D Server. If you are using 4D Client as a web server, you must explicitly tell Active4D that you are doing so.

  1. To enable Active4D's web serving on Client, do the following:
  2. Install the Active4D directory onto the Client machine. The location of the Active4D directory on a Client machine must conform to the rules specified in See The Active4D Directory.
  3. Set the "client is web server" option in Active4D.ini to "true".
  4. Open the method "A4D_InitHook".
  5. Uncomment the line "`$ioClientIsWebServer->:=True". If no such line exists, add it to the method (without the quotes).
  6. Close the method and restart 4D.
Using the Pre- and Post-Execute Hooks

For some developers it is necessary to examine and perhaps modify a request before and after Active4D. The Active4D shells provide hooks to allow the developer to do so without changing the core shell methods.

Pre-Execute Hook

The pre-execute hook allows you to inspect an incoming request. Within this hook you are given access to the raw request and are free to do whatever you want with it. If you decide to handle the requset yourself, you can prevent Active4D from handling the request by returning False from the hook. If you return True Active4D will handle the request normally.

  • If your aim is to inspect and possibly change the URL, a better way to do this is through the On Request event handler. For more information, see See On Request.

Actually, there are two pre-execute hooks. The hook you will use depends on the TCP/IP layer you are using.

If you are using 4D's web server as the TCP/IP layer, you will use the hook method called A4D_PreOnWebConnectionHook . This method receives a copy of the requested URL, the first 32K of the request itself, and the remote address. Because the hook receives a copy of the request, any changes you make to your copy of the request will be ignored by Active4D.

  • By default, the call to A4D_PreOnWebConnectionHook is commented out to prevent unnecessary copying of the request. If you plan to use this hook, you must uncomment its call in On Web Connection.

If you are using ITK or TCP Server Deux as the TCP/IP layer, you will use the hook method called A4D_PreStreamExecuteHook . This method receives a stream reference which you can use to read and modify the request. Since Active4D uses the same stream reference, any changes you make to the request will be used by Active4D.

  • If you want Active4D to execute a request, you must be sure to put any data you read (or a modified version thereof) back into the stream so that Active4D will receive the entire request.
Post-Execute Hook

The A4D_PostExecuteHook method allows you to inspect and modify the response headers and response body returned by Active4D after it has executed, but before the response is sent. This method receives a pointer to the response header names and values (two parallel text arrays) and a pointer to the response body BLOB .

Within this hook you may make any changes you wish to the response headers or the response body, and those changes will be reflected in the response sent to the client.

 

Configuration

Active4D ships preconfigured to meet most needs, but you can configure virtually every aspect of Active4D to fit your specific needs.

Before discussing the various configuration (or "config") files, you must know where to find them.

The Active4D Directory

All config files must reside in a special directory called "Active4D". Active4D will look for the Active4D directory using the following search path:

  • The database directory (Standalone/Server) or application directory (Client)
  • The shared ACI directory (4D 6.5) or shared 4D directory (4D 6.7)
  • The System:Preferences directory (Mac), Windows directory (Win95/98/Me/XP) or WINNT directory (Win2K, NT)
    • Active4D will follow aliases to the Active4D folder and to the config files.

This arrangement allows you to have multiple versions of config files, with those earlier in the search path overriding those later in the search path. This is analogous to how the 4DX plugin directory is handled by 4D.

So, for example, you could establish a baseline set of configuration options which you keep in the shared 4D directory, then override that configuration for a particular database by putting a separate config file in <database directory>/Active4D .

  • If Client is being used as a web server, you must install the Active4D directory on the Client machine.
Libraries and the Active4D Directory

Active4D also follows the same search path when searching for libraries that are being imported. Thus the Active4D directory becomes a convenient place to put your custom libraries.

Because Active4D will follow the entire search path, you can structure your libraries in a hierarchy, from the most specific to the most general, by placing them at various points in the search path.

In addition, you can specify a list of library directories to search in the config file. For more information, see the documentation for the "lib dirs" setting in the sample Active4D.ini config file.

Configuration Files

Active4D has four config files: Active4D.ini , ExtensionMap.ini , Realms.ini and VirtualHosts.ini . These are plain text files which may be created and edited on any platform, as Active4D will accept Mac, Windows and Unix line endings.

Sample files which should suit most needs are shipped with Active4D. Each file is heavily commented, so if this documentation is not handy you can always read the comments. It is recommended that you keep a copy of the original config files somewhere safe in case you need to go back to a baseline configuration.

Active4D periodically checks to see if the config files have been modified. The interval is set with the "refresh interval" option in Active4D.ini . If a config file has been modified, it is reloaded automatically, so there is no need to quit and restart 4D to have the changes take effect.

  • The config files are loaded on startup and their locations are cached, so you may not move them without restarting the server.
The Default Directory

Some of the config files rely on the concept of the default directory . The default directory is the directory from which relative paths are resolved.

Under 4D Standalone or Server, the default directory is the current database directory (the one with the structure file). Under Client, the default directory is the Client application directory.

Path Format

All paths specified in config files must be in URL format (also known as Unix format), which has the following attributes:

  • The directory separator is `/'
  • A full path begins with a leading `/', a relative path does not
  • A directory path may or may not be terminated with a trailing `/'
  • A file path must not be terminated with a trailing `/'
  • The path may not contain the native directory separators `:' and `\'
  • The path may begin with a special directory token, discussed below
  • The path may contain directory movement (..)
  • Any element of a path may be an alias

There are four tokens you may use at the beginning of a path to represent special directories. The tokens are:

Token

Directory

<default>

The default directory, as defined in See The Default Directory

<web>

The web root directory

<4d volume>

The name of the volume on which the 4D application resides

<boot volume>

The name of the system boot volume

The tokens include the `<` and `>', and are replaced with the directories they represent, without a trailing `/'. This allows you to create paths that are portable across machines and 4D directories.

For example, you may want to include files from a folder called "includes" outside of the web root, perhaps at the same level as the web root folder. To do so you would specify this path:

<web>/../includes

To help you understand how URL paths map to native Mac or Windows paths, here are a few samples of full paths in native format and URL format.

Native Full Path

Platform

URL Path

Dev:site:web:

Mac

/Dev/site/web

Dev:site:web:default.a4d

Mac

/Dev/site/web/default.a4d

C:\Dev\site\web\

Win

/C/Dev/site/web

C:\Dev\site\web\default.a4d

Win

/C/Dev/site/web/default.a4d

Native Relative Path

Platform

URL Path

:site:web

Mac

site/web

site\web\

Win

site/web

The Standard Search Path and Path Lists

When Active4D searches for libraries and .ini files, it follows a standard search path :

<default directory>/Active4D
<shared 4D or ACI directory>/Active4D
<System:Preferences>/Active4D (Mac) or <Windows>/Active4D (Win)

Several config options allow you to specify a semicolon-delimited list of full directory paths to search in addition to the standard search path. If such a list is provided, it is searched after <default directory>/Active4D .

  • A path list may include a path to a directory on a network-mounted volume.

For example, to add two directories to the search path, you might use something like this:

/CKG/Development/Active4D/libs;/CKG/Development/Libs

On Windows, a search path might look like this:

/C/My Documents/Projects/Foobar

Active4D.ini

Active4D.ini controls most of the behavior of Active4D and must be in an Active4D directory in the standard search path. There are over 30 options you can set in this file. Each option is in the form:

<option> = <value>

Case is not significant either for keys or values, and you can have any amount of white space surrounding the "=". You may also use comments just as you would in 4D, either on separate lines or at the end of a line.

The options in Active4D.ini are listed below. A detailed discussion of the options can be found in the chapter relating to the option.

Option

Description

Error Handling

 

error page

The root-relative URL path to a file to execute when an error occurs

log level

A sum of the bit flags defining which type of events to log

 

 

HTTP Server

 

cache control

The default cache control for executable files

client is web server

Indicates if Client is acting as a web server

content charset

The default content charset

default page

Name of default page when directory URL is requested

executable extensions

File extensions which Active4D will consider executable

expires

The number of minutes before a page should expire

max request size

The maximum size in kilobytes (K) of a request, including the headers

parameter mode

Which collections to use for form variables and query string params

receive timeout

Maximum wait time when receiving from a TCP stream

root

Root web directory

serve nonexecutables

Whether Active4D should serve non-script files

 

 

Interpreter

auto relate one

Whether to load related records in auto-related one tables

auto relate many

Whether to load related records in auto-related many tables

output charset

The character set to convert to when writing text to the response buffer

output encoding

The encoding to perform on text written to the response buffer

platform charset

The character set from which string literals are converted to Mac Roman

script timeout

The minimum script timeout in seconds

Libraries

 

auto refresh libs

Whether to automatically refresh libraries

lib dirs

Extra search paths for libraries

lib extension

The extension for Active4D libraries

refresh interval

How often to check libraries and config files for modification

 

 

Security

 

auto create vars

Whether to create local variables from form variables and query params

safe doc dirs

Directories allowed for document commands outside of root

safe script dirs

Script directories outside of root

 

 

Sessions

 

session cache mode

Where to cache sessions

session cookie domain

The domain of the session cookie

session cookie name

The name of the cookie to store with the session ID

session cookie path

The path of the session cookie

session purge interval

The minimum interval in seconds between attempts to purge expired sessions

session timeout

The length of time in minutes that a session can live without any user interaction

session var name

The name of the local variable to automatically set to the current session ID

use session cookies

Whether Active4D should store session IDs in cookies

use sessions

A global switch for session management

ExtensionMap.ini

This config file maps filename extensions to MacOS file creator and type codes, as well as to MIME types. It is used primarily by Active4D's HTTP server. For more information on this file, see See ExtensionMap.ini.

Realms.ini

This config file specifies the mapping between realm names and portions of a URL that identify those realms. It is used primarily by Active4D's HTTP server. For more information on this file, see See User Authentication.

VirtualHosts.ini

This config file provides a routing table for virtual hosting, which allows you to create multiple independent web sites on the same machine. For more information on this file, see See VirtualHosts.ini.

Deprecated Plugin Configuration Commands

All of the plugin configuration commands present in Active4D 1.0, except for A4D Get root , have been deprecated (i.e. removed). They are:

A4D Get error page
A4D Get log level
A4D Get options
A4D Load library
A4D SET ERROR PAGE
A4D SET LOG LEVEL
A4D SET OPTIONS

You must now use the config file options in place of these commands.

  • Active4D uses its own virtual host mapping system. However, should you wish to use your own system, A4D SET ROOT has been retained to allow you to set the root directory from 4D, before calling Active4D.

 

Security

Security is something you should worry about with any web site. When a web site has direct access to a mission-critical database, security is a mission-critical issue. Active4D provides several mechanisms to ensure your site is as secure as possible.

Source Code Security

When you build a web site with Active4D, all or part of your source code is on the web server in plain text form.

This does not mean your source code is not secure.

In determining the potential security risk, you must keep in mind these two important facts:

  • A web site is only as secure as its web server.
  • Active4D removes the source code before serving the page.
Web Server Security = Source Code Security

If an attacker compromises the security of your web server, your source code is at risk -- but then, so is everything else on the web server, including the database.

If the attacker is bent on being destructive, there are far easier and more effective ways to wreak havoc than to change the embedded source code in a web page. And it is highly unlikely that the hackers of the world know the 4D language.

Circumventing Active4D

Active4D always strips out source code before sending a page to the client. But what if the page is served directly without passing through Active4D?

It is up to you to ensure that every request is passed to Active4D.

If you are using 4D's built-in web server as the TCP layer, there is a potential risk. Active4D is only invoked through the On Web Connection mechanism. On Web Connection is only invoked when the user requests a URL that does not exist. URLs that reference existing files are served directly by 4D without invoking On Web Connection . In addition, the URL must be prefixed with "/4DCGI" for form variables to be passed into On Web Connection .

  • If 4D's web server has it's default HTML root set either to nothing or to your real web root, there is nothing to prevent a casual user from reading your source code.

For example, let's suppose the user is given this URL:

www.myserver.com/4DCGI/mypages/mypage.a4d

Let us further suppose that this page exists within a directory called "web" within the database directory, and the 4D web server's default HTML root is set in the Database Properties dialog to "web". If the user then enters the same URL without "/4DCGI", like this:

www.myserver.com/mypages/mypage.a4d

4D will directly serve that page, complete with embedded source code, without going through On Web Connection . On the other hand, if 4D's default HTML root is set to nothing, the user could still access the source with this URL:

www.myserver.com/web/mypages/mypage.a4d

It is critical that you create an empty "decoy" directory in your database directory and set 4D's default HTML root to that folder, then set Active4D's root directory to something else. By doing this, you ensure that all URLs without "/4DCGI" will be to non-existent files, thus forcing 4D to route the request through On Web Connection and ultimately to Active4D.

  • Even though this scheme will protect your source code, your Active4D code will have no access to form variables, since 4D's web server does not pass form variables if the URL is not prefixed with "/4DCGI". Of course, this scheme is unnecessary if you are using ITK or TCP Server Deux as the TCP layer.

If you are using 4D's web server with Active4D and have not read through and followed the installation instructions in See Configuring for 4D, please do so now!

The Whole Fortune 500 Can't Be Wrong

Take a look around the web at just about any major corporation that provides some sort of dynamic functionality -- banks, airlines, credit card companies, online retailers, etc. You will find the vast majority have page names that end in ".asp" or ".jsp". This means they are using either Microsoft's Active Server Pages or Sun's Java Server Pages, both of which embed the source code in the web page, just as Active4D does.

These companies have a lot to lose if their embedded source code is stolen or altered. Yet they continue to use embedded scripting, because they have realized that the issue is not source code security, but web server security.

Source Code Encryption

If you are worried about the security of your web server, you can encrypt your executable files and Active4D will transparently decrypt them when they are loaded.

  • This feature is currently in alpha and will be included in a future beta release.
Potential Attacks

Even if a miscreant does not succeed in hacking into your web server, there are still many opportunities for mischief. For an excellent essay on the security issues arising from a web-based scripting language, I recommend reading the following:

http://www.securereality.com.au/studyinscarlet.txt

If you do read this essay, please note that Active4D addresses almost every potential attack outlined there.

Executing/Accessing Non-Web Files

One potential source of attack is to allow access to files outside of the web site itself. Ordinarily, Active4D requires that any requested file be within the web root directory or a subdirectory thereof. This includes files that are executed with the include or require commands.

If an attempt is made to execute a file in an unauthorized directory, Active4D aborts execution and returns the status code 403 Forbidden along with a message indicating the error.

The "safe script dirs" Config Option

If you want to execute or include script files in a directory outside of the root directory, you can provide a list of allowable script directories with the "safe script dirs" option in Active4D.ini .

If an attempt is made to execute a file outside of the root directory, this path list is checked. If the file lies within one of the specified directories, the execution is allowed to proceed. Otherwise the behavior is as specified above for unauthorized directories.

By default the "safe script dirs" path list is empty.

Misusing Document Commands

Let us suppose that you have a script that deletes a file, and that the path to the file is kept in the session. An attacker could potentially force the session to contain a path to a critical system file outside of the web root directory.

To prevent this kind of attack, the path passed to all document commands ( Open document , DELETE DOCUMENT , etc.) is checked to ensure that the path is within the web root directory or a subdirectory thereof.

If an attempt is made to use a document command on an unauthorized directory, Active4D does the following:

  • Aborts execution of that command, but continues executing the script
  • Sets OK to zero
  • Sets the variable A4D_Error to -46 (Volume is locked by an application.)

As a result of this behavior, you should at least check the OK variable when using any document commands.

The "safe doc dirs" Config Option

If you want to use document commands on files outside of the root directory, you must add the directory path to the "safe doc dirs" path list in Active4D.ini . For information on Active4D.ini , see See Configuration

If a document command is given a path outside the root directory, this path list is checked. If the path lies within one of the specified directories, the command is allowed to proceed. Otherwise it behaves as specified above for unauthorized directories.

By default the "safe doc dirs" path list is empty.

Spoofing Form Variables

Suppose you have a form which posts some critical piece of information in a hidden form variable called "f_id". In the form processing script, you ordinarily would access the local variable called $f_id to get the hidden value, then use this ID to perform some critical operation.

An attacker could execute the form processing script, passing in a query string with a parameter "f_id" and some bogus value. Because Active4D ordinarily creates local variables from both query parameters and form variables, your script would have no way of knowing that the local variable $f_id did not come from a hidden form variable. Depending on what you do with $f_id , this could present a potential security breach.

The "auto create vars" Config Option

To prevent variable spoofing attacks, you can use the "auto create vars" option in Active4D.ini to turn off the creation of local variables from query parameters and form variables. In your form processing script, you would use the get form variable command to get the value of the "f_id" field.

There are two factors to take into account before going to this length.

  • Unless you use the "separate" parameter mode option (see "parameter mode" in Active4D.ini ), this technique will not work.
  • Auto-creation of local variables from form variables and query parameters is one of the great conveniences provided by Active4D.
Uploading Huge Files

If you have a web page which provides file upload, an attacker could attempt to upload a huge file. Most web servers buffer the request, thus it is possible to compromise the stability of the web server by forcing it to run out of memory.

Active4D provides an option called "max request size" in Active4D.ini , which limits the total size of the request Active4D will accept. By setting this option at something reasonable for your particular application (the default is 64KB), you can prevent the attack outlined above.

  • Actually, if you are using 4D's web server in version prior to 4D 2003, you can't really prevent such an attack, because 4D has already attempted to load the entire file into memory by the time Active4D is called.

On the other hand, when ITK is used as the TCP layer for Active4D, it is guaranteed that no more than <max request size> bytes will be read into memory, thus protecting against upload attacks (or user stupidity).

If a request is received which is larger than <max request size> bytes, Active4D aborts execution and returns the status code 413 Request Entity Too Large , along with an error message indicating that fact.

 

HTTP Server

You may remember from the introduction the diagram of the various layers that make up a web scripting system. That diagram is reproduced here.

 

Active4D 1.0 was purely a text processor which occupied the scripting layer, leaving the implementation of the two lower layers to the host environment. This usually meant that the developer had to do a lot of work to implement the HTTP server.

Both the HTTP server layer and scripting layer are handled fully by Active4D.

What Is a Web Server?

You may be wondering how Active4D differs from 4D's built-in web server or from dedicated web servers like WebStar.

Traditionally a web server handles the following tasks:

  • TCP/IP communications between the host and the client
  • Parsing of HTTP requests and obeying the rules of the HTTP protocol based on the contents of those requests
  • Finding the resource requested and returning it in a response with the appropriate HTTP response headers
  • Providing a means of specifying realms of the web site that require user authentication
  • Error handling
  • Virtual host routing
Active4D + TCP Layer = Web Server

Active4D relies on the host to provide TCP/IP communications. Other than that, Active4D handles all of the task listed above, right out of the box, with no programming required. And because Active4D is the web server, you have full access to every aspect of both the HTTP request and the HTTP response from within Active4D.

Believe me, implementing a web server with all of those features using other tools is not a trivial exercise!

The Active4D Shell comes preconfigured to use 4D's web server, which is the preferred environment in which to run. If you already are using ITK or TCP Server Deux (4D 6.7+ only) and wish to use them with Active4D, they can be used as the TCP layer as well.

Web Server Issues

In general, 4D's built-in web server is the best TCP layer to use, as it requires no extra installation or methods and it will give you the best performance. However, there are a few issues with 4D's built-in web server which may affect you. It is unlikely they will, but just in case it is important to be aware of them.

32K limit on request size

With 4D 6.5's built-in web server, the total size of the request is limited to 32K. This may cause problems if you allow posting of forms with multiline text fields, as the user may paste more than 32K of text in the field. This also precludes the uploading of files, as the request size could easily exceed 32K.

If the request is larger than 32K, neither 4D nor Active4D would crash, but the request will not be handled properly. The best solution to this issue is to upgrade 4D to at least 6.7.

No limit on request size

With any version of 4D's built-in web server through version 6.8, file uploads are a potential problem, as 4D will try to load the entire uploaded file into memory with no limit on its size. Uploading a very large file may cause the server to crash if it runs out of memory. This problem does not exist in 4D 2003 or higher, as Active4D takes advantage of its ability to limit the size of a request.

If you cannot use 4D 2003, and you must allow file uploading, and you do not have enough memory to handle a potential large upload, then you may want to consider another TCP layer in place of 4D's built-in web server.

If you must use another TCP layer and you are using 4D 6.5, then you must use ITK. If you are using 4D 6.7 or greater, you may use either ITK or TCP Server Deux as the TCP layer, but TCP Server Deux is recommended as it provides a very robust threading model and better performance than ITK alone.

HTTP Fundamentals

To understand the functioning of a web server, it really pays to understand how HTTP requests work. David Adams has been kind enough to allow me to include Chapter 47 of his most excellent book, The 4D Web Companion. Before going on, I recommend taking the time to read through this excellent introduction to HTTP, which is included in the Active4D documentation distribution as a separate PDF document, "HTTP Fundamentals.pdf."

  • You don't need to pay attention to his coverage of 4D web commands and tags, as you will have no need for them with Active4D.
Active4D HTTP Request Handling
  1. Now that you know a little bit about HTTP requests, we can look at how Active4D handles an HTTP request. Before a request can be handled, a TCP/IP connection must have established between the host and a remote client.

Note that the descriptions below will refer to collections , which are special data structures used throughout Active4D. It is enough for now to know that a collection is an associative array of key and values. For more information on collections, see See Collections.

At any step of the request handling, if Active4D detects either corrupt data or data that does not conform to the HTTP specification, the status 400 ( Bad Request ) is returned immediately.

Executable vs. Non-executable Files

When Active4D is asked to serve a file, it first checks the file's extension against a list of executable extensions.

There are several reasons for determining if a file is considered executable or not:

Efficiency
  • It is inefficient to create and run the Active4D interpreter for a file that has no executable code.
Stability
  • If Active4D is asked to parse a file that is not textual, such as a GIF file, it is possible that it could be fooled into executing nonsense, which would lead to an error condition.
Convenience
  • Active4D can be configured to serve non-executable files (including binary files) and does all the work that an HTTP server should do, such as generating proper headers, so you don't have to worry about it.
Request Header Parsing

The first step in handling a request is to retrieve and parse the request header.

  1. The TCP layer invokes Active4D, passing in some basic context information like the host and remote IP addresses of the current connection.
  2. Two response headers are created: "X-VERSION: HTTP/1.1" and "X-STATUS: 200 OK". The response status is initialized to 200 (OK).
  3. If Active4D has been asked to retrieve the request via a TCP/IP stream, it retrieves the request header using a receive callback method. Otherwise it is given the entire request, and if the request size is greater than the size set in the "max request size" option in Active4D.ini, a status of 413 (Request Entity Too Large) is returned immediately.
  4. The request method is parsed and saved. If it is not "GET", "POST" or "HEAD", a status of 400 (Bad Request) is returned immediately.
  5. The requested URL and HTTP version are parsed and saved.
  6. If an On Request event handler has been defined, it is called with the path and query string portions of the URL. If the event handler returns a non-empty string, the URL of the request (not including the host) is replaced with that value. If the status has been set by the handler to anything other than 200 (OK), the request is aborted and the status is returned. For more information on the On Request event handler, see See On Request.
  7. If the URL contains a query string, the query string parameters are parsed and put either into the "query params" or "form variables" collection, depending on the "parameter mode" option in Active4D.ini.
  8. The request headers are parsed. Each header and its associated value is put into the "request info" collection.
  9. If a "Cookie" header is present, the cookies are parsed out and their names and values are put into the "request cookies" collection.
  10. If an "Authentication" header is present and the authentication type is "Basic", the authentication username and password are decoded and saved.
  11. If the request method is "POST" and the "Content-Length" header indicates a request size larger than the "max request size" option in Active4D.ini, a status of 413 (Request Entity Too Large) is returned immediately.
  12. If the filename of the requested URL has an extension, the extension is checked against the "executable extensions" option in Active4D.ini. If it matches an extension there, it is considered executable. Otherwise it is considered non-executable.
  13. If the requested file is considered executable, what happens next depends on the request method. If the filename has either no extension or an executable extension and the request method is "HEAD" or "GET", nothing more is done.

If the file is considered executable, what occurs next depends on the TCP layer used in conjunction with Active4D.

POST Handling with the 4D 6.5 Web Server
  1. If 4D 6.5's web server is the TCP layer, Active4D receives up to 32K of the request in a text variable.
  2. The size of the request text is checked against the size of the request header. If they are the same, Active4D assumes that a form was posted without prefixing the URL with "/4DCGI". Without that 4D omits the request body, which contains the form variables. Since the form variables cannot be retrieved, nothing more is done.
  3. If the request has a body, the form variables are parsed and put either into the "form variables" or "query params" collection, depending on the "parameter mode" option in Active4D.ini. If the POST contained file uploads, they are handled according to the steps in below.
POST Handling With the 4D 6.7+ Web Server
  1. When 4D 6.7 is used as the TCP layer, Active4D uses GET WEB FORM VARIABLES to retrieve the form variable names and values into two parallel arrays. These arrays are then passed to Active4D.
  2. The names and values in the parallel arrays are put either into the "form variables" or "query params" collection, depending on the "parameter mode" option in Active4D.ini. If files were uploaded, they are handled according to the steps in the section See File Upload Handling With 4D's Web Server below.
POST Handling With Stream-based TCP Layers
  1. When Active4D is used in conjunction with ITK, for example, the shell passes the ITK stream reference to Active4D, which it then uses to retrieve the request data as needed.
  2. Active4D uses a receive callback method to receive the entire request body.
  3. The form variable data in the request body is parsed, and the names and values are put either into the "form variables" or "query params" collection, depending on the "parameter mode" option in Active4D.ini.
  4. If files were uploaded, they are saved to disk.
File Upload Handling With 4D's Web Server

When 4D is used as the TCP layer, any uploaded files go in BLOB variables that should have been declared in the Compiler_Web method. These variables must have the same name as the form variable used to upload the file.

  1. Since you may upload files from more than one form, it is best to use a generic name, such as "A4D_Upload" for your upload BLOB variables.
  2. While scanning the form variable arrays, Active4D checks each element of the value array.
  3. If the array element begins with "; filename="", it is assumed to be the specification of an uploaded file. In that case Active4D parses the rest of the array element for information about the uploaded file.
  4. Active4D then attempts to read a BLOB variable with whose name is contained in the corresponding element of the names array.
  5. If such a BLOB can be found, it is saved to disk and the original BLOB, which is a 4D process variable declared in Compiler_Web, is cleared in order to save memory.
Executable Request Handling
  1. If an executable request has been received and the header and body successfully parsed, Active4D proceeds to the next step.
  2. The context information such as host and remote IP address are put into the "request info" collection.
  3. The cache-control, expires, and content charset settings are initialized from the configured values in Active4D.ini. The content type is set to "text/html".
  4. If virtual hosts have been defined, Active4D sets the root directory and default page from the virtual host routing table specified in VirtualHosts.ini. For more information on virtual hosts, see See Virtual Hosting.
  5. If the URL is prefixed with "/4DCGI", the prefix is removed from the URL.
  6. If the remaining URL is absolute (begins with `/'), the current root directory path is inserted at the beginning of the URL. This forms a full path to the requested resource.
  7. If the URL ends with `/', it is assumed to be a directory. If the URL was mapped by VirtualHost.ini, the default page specified there is appended to the directory. If the URL was not mapped, the next default page in the list of default pages is appended.
  8. The full path is converted from URL to native format and any aliases in the path are resolved to their targets. The resolved target path replaces the unresolved path.
  9. The full path is checked to make sure it is either in the root directory or in one of the directories specified in the "safe script dirs" option in Active4D.ini. If it is not, a status of 403 (Forbidden) is returned immediately.
  10. The full path is checked to see if it is a valid directory path. If so, a trailing `/' is added and a redirect is issued to that URL.
  11. If the full path is invalid and the request was not mapped by VirtualHosts.ini and there are more default page names to try, the server goes back to step See If the URL ends with `/', it is assumed to be a directory. If the URL was mapped by VirtualHost.ini, the default page specified there is appended to the directory. If the URL was not mapped, the next default page in the list of default pages is appended.. Otherwise 404 (Not Found) is returned immediately.
  12. The current realm (if any) is set from the realm map in Realms.ini. For more information on realms, see See User Authentication.
  13. If the full path is valid, the file is opened and the interpreter is invoked to execute the file. For information on what happens during execution, see See Interpreter
  14. If a graphic has been written to the response buffer and the response buffer is empty, something went wrong during the graphic conversion process and the response status is set to 404 (Not Found).
  15. If the status is >=400 (an error), skip to Step See The "X-STATUS" response header is set to the response status..
  16. If cache-control has been set, a "Cache-Control" header is added to the response headers. Since older browsers do not obey the "Cache-Control" header, if the cache-control setting is "no-cache", a "Pragma" response header with the value "no-cache" is added, and the expires time is set to now.
  17. If a content type has been set, a "Content-Type" response header is added.
  18. If a charset has been set, it is appended to the "Content-Type" response header.
  19. If an expires date has been set, an "Expires" response header is added.
  20. The headers in the "response header" collection are added.
  21. The cookies in the "response cookies" collection are added as "Set-Cookie" response headers.
  22. The "X-STATUS" response header is set to the response status.
  23. If the request method is "HEAD", the response buffer is cleared. In this case, or if the response status is less than 400 (no error), skip to Step See A "Content-Length" response header is added which contains the size of the response buffer..
  24. If the status is >=400 (an error), Active4D attempts to find a file in the root directory with the name <status>.html, where <status> is the numeric status. Thus with a 404 (Not Found) status, Active4D would look for a file called "404.html".
  25. If such a file is found, it is placed in the response buffer, and a "Last-Modified" response header is added. If such a file is not found and the HTTP spec says that an error message should be returned for the current status, an appropriate error message is placed in the response buffer.
  26. A "Content-Length" response header is added which contains the size of the response buffer.
  27. A "Content-Type" response header is added with the MIME type of the response.
  28. Control is returned to 4D. The shell code sends the response headers and then the contents of the response buffer. If 4D is the TCP layer and the MIME type of the response is "text/html", 4D tags are stripped from the response.
Non-Executable Request Handling

By default, Active4D operates as a traditional web server serving static content. If you would like to serve static content yourself, you can do so by changing the "serve nonexecutables" option in Active4D.ini . Active4D makes it easier by providing you with the full native path and modification date/time of the requested file.

  1. When it is determined that a request for a non-executable (static) file has been made, Active4D does the following.
  2. If the requested file is not found, a status of 404 (Not Found) is returned immediately.
  3. If the requested file is found and the "serve nonexecutables" option is turned off, Active4D returns the full native path of the file, the file's modification date and the modification time. For more information, see See outHeaderNames/outHeaderValues.
  4. If the requested file is found and the "serve nonexecutables" option is turned on, the file's extension is checked. If it has an extension and the extension was specified in ExtensionMap.ini, the content type of the file is set accordingly.
  5. If there is no extension and Active4D is running on Macintosh, and the file's creator/type combination was specified in ExtensionMap.ini, the content type is set accordingly.
  6. If no content type could be determined, the status is set to 404 (Not Found) and the file will not be served for security reasons.
  7. A "Content-Length" response header is added which contains the size of the response buffer.
  8. A "Content-Type" response header is added with the MIME type of the response.
  9. Control is returned to 4D. The shell code sends the response headers and then the contents of the response buffer. If 4D is the TCP layer and the MIME type of the response is "text/html", 4D tags are stripped from the response.
Configuration

The Active4D web server is configured through the four config files, Active4D.ini , ExtensionMap.ini , Realms.ini and VirtualHosts.ini .

Active4D.ini

There are several config options in Active4D.ini that determine the basic behavior of the web server. For information on the format of config options, see See Configuration

cache control

The default cache control for executable files. Values should be valid directives to the "Cache-Control" HTTP header, as specified in RFC 2616, section 14.9. This value can be changed at runtime with the set cache control command.

The default value for this option is "no-cache".

content charset

The default content charset. Values should be standard ISO charset names, which can be found at http://www.iana.org/assignments/character-sets . This value can be changed at runtime with the set content charset command.

The default value for this option is "ISO-8859-1" on Roman language systems, "Shift_JIS" on Japanese language systems, and "GB2312" on Chinese language systems.

default page

If a client requests a URL which specifies a directory, a default page within that directory is executed. This preference determines the name of the default page. The name must be a simple filename with no directory components.

The default value for this option is "default.a4d".

  • If a Virtual Host routing table is defined, it may override this setting. See See Virtual Hosting for more information on Virtual Hosts.
executable extensions

A list of file extensions (with leading dots) which Active4D will consider executable. The extensions may be separated by any amount of whitespace. Each extension must begin with `.' followed by a maximum of five alphanumeric characters.

There is no point in having Active4D parse and attempt to execute files which it cannot, such as graphics and static HTML pages. This list determines which files are executed by Active4D and which are not.

What Active4D does with non-executable files depends on the "serve nonexecutables" option.

The default value for this option is ".a4d".

expires

The number of minutes before an executable page should expire on the client browser. Setting to zero forces pages to expire immediately. Setting a negative value will cause this value to be ignored by Active4D. Positive values are clipped to one year (in minutes). This value can be changed at runtime with the set expires command.

The default value for this option is -1 (ignore).

max request size

The maximum size in kilobytes (K) of a request, including the headers. Requests are buffered in memory, so if files are being uploaded they will at some point be completely in memory. To prevent the user from uploading extremely large files and thus potentially crashing the server, you should set this value to something reasonable for the type of file you expect to be uploaded. Values less than 32 (K) are ignored. If the post exceeds this size, the status 413 Request Entity Too Large is returned.

The default value for this option is 64 (K).

  • This option is really only useful when using ITK, TCP Server Deux or 4D 2003's built in web server as the TCP layer for Active4D. With 4D's web server (thru 6.8.x), the request has already been buffered in memory by the time Active4D has been called, and by that time the server may have run out of memory and crashed. Such is life.
parameter mode

Determines which collection query string parameters and form variables go into. There are three possible modes: "separate", "query params", and "form variables".

In "separate" mode, query string parameter and form variables are maintained in separate collections. In "query params" mode, everything is put into the query params collection. In "form variables" mode, everything is put into the form variables collection.

Why do this? Because many scripts can be executed either through a POST or a GET . In either case the parameters they "receive" should be the same. If you manipulate the query params or form variables collection, it is a pain to constantly have to check the request method to figure out which collection to modify. By setting the parameter mode, you can force all values to go into one collection or the other. Usually you will want to do this.

The default value for this option is "separate".

receive timeout

When Active4D is invoked via A4D Execute stream request , Active4D calls a receive callback to get the request from the TCP layer. This option controls the maximum number of ticks Active4D will wait to receive. Values < 600 (10 seconds) are ignored.

The default value for this option is 3600 (1 minute).

root

This option determines the default root directory for file access. All URLs are relative to this directory. In addition, no scripts may be executed or non-executable files served outside of this directory, unless the directory is specified in the "safe script dirs" option.

The path specified must be in URL format and may be either absolute or relative. If the path is relative, it will be relative to the default directory. The path must be valid at the time the config file is read.

The default value for this option is <default directory>/web .

  • If a Virtual Host routing table is defined, it may override this setting. See See Virtual Hosting for more information on Virtual Hosts.
safe script dirs

For information on this option, see See The "safe script dirs" Config Option.

serve nonexecutables

When a non-executable file is requested, this setting determines what Active4D does, as outlined in See Non-Executable Request Handling above.

The default value for this option is "true".

ExtensionMap.ini

When a web server sends a file to the client browser, it must inform the browser of the file's type. Similarly, when a client uploads a file to the web server, the web server receives the file's type. This type is sent and received as a MIME type . MIME types are standardized and can identify a file's type independently of its filename extension.

On Windows (and to some extent MacOS X) the filename extension is sufficient to set the type and associated creator application of the file. On the MacOS, however, every file has a creator and type which is independent of its extension. For example, you would expect a file with the extension ".sit" to have its type and creator set such that you can double-click it and have it be processed by Stuffit.

ExtensionMap.ini is a config file that allows Active4D's web server to map filename extensions to Macintosh file types and creators and to MIME types. For example, let us say that Active4D receives an upload file with the MIME type "image/gif". It looks into its extension map and finds an entry which says that the corresponding filename extension is ".gif" and the creator and type are "ogle" and "GIFf" respectively, which is the creator type for the QuickTime PictureViewer application.

Each entry in ExtensionMap.ini is in the form:

<extension><WS><Mac type><WS><Mac creator><WS><MIME type>

where <extension> is the filename extension (including the dot), <WS> is any number of contiguous whitespace characters, <Mac type> and <Mac creator> are four-letter type codes, and <MIME type> is a MIME type such as "image/gif". Thus the GIF entry mentioned above would look like this:

.GIF GIFf ogle image/gif

Active4D comes with an ExtensionMap.ini file which contains 59 entries covering most of the file types you are likely to come across. However, should you need to receive or send a file not on the list, you should add it to this config file.

  • If the Macintosh creator or file type code contains extended ASCII characters, control characters, or whitespace characters, they must be URL encoded. For example, the four-character type code for Adobe PDF files is "PDF ". The trailing space must be URL encoded, resulting in an extension map entry of "PDF%20".
User Authentication

Active4D provides full support for protecting portions of your web site against intruders by using the basic HTTP authentication protocol. Currently authentication only works with executable files.

For example, you may have an admin section of your web site that only administrators should have access to. Another section of your web site is only for accounting users. By defining realms -- sections of the web site defined by a hostname or substring of a requested file's path -- you can automatically be notified when a user's credentials need to be authenticated.

  • The basic HTTP authentication protocol has a number of security problems if it is not used over a secure SSL connection. In addition, each browser handles user authentication differently. Therefore it is recommended that you use your own form-based authentication if you need maximum control over the process.

The user authentication mechanism requires three elements that work together:

  • Entries in Realms.ini
  • On Authenticate event handler
  • Use of the authenticate command

For information on the On Authenticate event handler, see See On Authenticate. For information on the authenticate command, see See User Authentication.

Realms.ini

This config file specifies the mapping between realm names and substrings of a path that identify those realms.

Each entry in Realms.ini has two fields in this format:

Realm<HT>Match string

The realm is a logical name for the section of the web site you wish to protect. It need not have any relation to an actual directory name. Realm names should not have any extended characters or spaces. If there are extended characters they are ignored. Spaces are converted to underscores.

You use the realm name internally to determine what section of the web site is being accessed. The browser caches user credentials for each realm, so subsequent authorized accesses in the same realm do not result in repeated requests for credentials.

The match string is checked against the host name and the fully resolved path of the requested URL. If any portion of the host name or path matches the match string, the corresponding realm is made the current realm. Matching is case-insensitive, but the entire match string must be found within the host name or path.

  • The realm is matched to the path which results after virtual host mapping, after the default page is added to a directory request and after any aliases have been resolved.

Because the match string could be anywhere in the path, you must ensure that your web directory names and match strings are selected such that the match strings do not inadvertently match the wrong directory.

In addition, because the realm entries are scanned in order, realms cannot be nested. For example, you may not put the directory admin in the "admin" realm and the subdirectory admin/accounting in the "accounting" realm. Only one of them will ever be selected.

If a realm is matched and the On Authenticate event handler has been defined, it is called before execution begins to allow you to authenticate the user.

If the match string is not found, there is no current realm and the authentication mechanism is not invoked.

Let's look at some example Realm.ini entries.

`Realm Match string

secure-server www.myserver.com

admin /admin/

The first entry would place the entire host www.myserver.com in the realm "www.myserver.com". That means any request made on that server would have to be authenticated for that realm.

The second entry places the directory admin in the "admin" realm. Note the use of the leading and trailing slashes to ensure only a directory is matched in the path. Don't worry if the user enters www.myserver.com/admin , because Active4D redirects that to www.myserver.com/admin/ which will match correctly.

Virtual Hosting

If you create a large database in 4D, most likely it has more than one module. Often these modules are restricted to a certain set of users.

When bringing a database to the web, you will want to carry the same structure to your web site. You could accomplish this by directing the different classes of users to separate subdirectories within the root directory. But wouldn't it be nice to have completely separate web sites for each one?

Ordinarily this is difficult, since the entire web site shares a single root folder and a single web address. However, if your host machine supports TCP/IP multihoming, you can easily set up completely different web sites on the same machine and configure Active4D to route requests to the correct folder.

VirtualHosts.ini

This config file specifies where Active4D should route HTTP requests. The criteria is uses to determine the routing are:

hostname
  • This would be used if the client enters a domain name in the URL, such as www.active4d.net .
IP address
  • This would be used if the client enters a direct IP address in the URL, such as 192.168.1.7 .
language
  • This would be used if you have partitioned a single domain/IP address into separate sites based on the preferred language the client has specified in the browser. The language is specified as an ISO language code. A complete list of codes can be found in See ISO Language Codes.

Since this is a routing table, you must specify the target of the routing. The complete format for a virtual host entry consists of five tab-delimited fields. There may be any number of tabs between fields, which are as follows:

IP Address Hostname Language Root Default

You may use a value of * in the IP Address , Hostname and Language fields to indicate any value will be accepted. Using * in the Root or Default fields indicates that the global values should be used. All fields are limited to 255 characters in length.

The entries are searched in order, so they should progress from most specific to the least specific. All matching is case-insensitive and matches the entire value. An invalid or missing value will result in that entry being ignored.

The Root should be a URL-style path, either relative to the default directory or a full path. The Default should be a simple file name with no directories.

The final entry should be terminated with a carriage return.

A Virtual Host Example

To understand how virtual host routing works, let's look at some examples for a site that has an English and French version. We are using multihoming to have multiple IP addresses on the same machine. In addition, we will define three hosts, mac.home.com , mac.admin.com and mac.home.fr . The first two are on IP address 192.168.1.7, and the third is on 192.168.1.27.

`IP address Hostname Lang. Root Default

* * fr web_fr defaut.a4d

* mac.home.com * * *

* mac.admin.com * admin *

* 192.168.1.7 * * *

192.168.1.7 * * * *

* mac.home.fr * web_fr defaut.a4d

* 192.168.1.27 * web_fr defaut.a4d

192.168.1.27 * * web_fr defaut.a4d

The first entry routes all requests where the user's browser is configured with French as the preferred language. The directory "web_fr" is made the root directory for those requests, and a URL to a directory will redirect to the default file "defaut.a4d" in that directory.

The second entry will route all requests to mac.home.com . The root directory and default file will be set to whatever they are configured to be in Active4D.ini .

The third entry will route all requests to the virtual host mac.admin.com . The root directory will be set to "admin" and the default file will be set to whatever it is configured to be in Active4D.ini .

The fourth entry will route all requests to the host 192.168.1.7 (in case the user enters the IP address directly). This entry is an alias for the second one.

The fifth entry will route all other requests to the IP address 192.168.1.7 in the same way the second entry does. For completeness, you should always have an entry for both the hostname and IP address to catch all possible requests.

  • When 4D's web server is configured to accept requests on all IP addresses, it does not properly report the IP address to which a request is made. Because of this limitation, if you are using 4D's web server as the TCP layer you must include an entry like the fourth to catch directly entered IP addresses.

If you are using ITK as the TCP layer, you may skip entries like the fourth and just use entries like the fifth.

The sixth entry routes all request to the host mac.home.fr . The directory "web_fr" is made the root directory for those requests, and a URL to a directory will redirect to the default file "defaut.a4d" in that directory.

The final two entries are analogous to the fourth and fifth, only they route requests to a different IP address.

HTTP Error Handling

If an error occurs during the processing of a request, before any scripts are executed, Active4D returns the status code indicating the error that occurred and a default error message where indicated by the HTTP specification.

You can override the default error message by creating a file called <status>.html in the root web directory, where <status> is the numeric status code representing the error. These error pages should be purely static pages with no embedded scripts.

For example, to show a custom error page for a 404 (Not Found) error, simply create a static HTML page and name it "404.html", then place it in the root directory. When a 404 error occurs, Active4D will return your custom page.

 

 

Invoking Active4D

If you are just starting to use Active4D, the Active4D shell provides everything you need to get up and running without the need to understand the details of the plugin API. If you are using the shell as is, you may safely skip this chapter.

If on the other hand you are integrating Active4D with your existing web server code, you will need to understand the plugin API.

Types of Execution

There are two different ways of executing scripts in Active4D: request execution and direct execution .

Request execution requires that you pass a valid HTTP request to Active4D. The request body contains the embedded script.

Direct execution requires only that you pass the embedded script to Active4D, although you may optionally pass a query string as well.

  • In Active4D 1.0, this was the only way to execute. In v2 I do not recommend using these commands. Use the Execute request commands instead.
Request Execution

Once the host web server receives HTTP request, you pass the request to Active4D through one of the A4D Execute <type> request commands. They are:

A4D Execute request

A4D Execute BLOB request

A4D Execute 4D request

A4D Execute stream request

A4D Execute <type> request Parameters

The request execution commands all take similar parameters.

A4D Execute <type> request
version 2
modified version 3.0

 

A4D Execute <type> request(<inRequest>; inRequestInfo; outContentType; outHeaderNames; outHeaderValues; outResponse) ! Longint

 

 

Parameter

Type

 

Description

 

<inRequest>

Varies

!

Request or means to retrieve it

 

inRequestInfo

Array Text

!

See below

 

outHeaderNames

Array Text

Response header field names

 

outHeaderValues

Array Text

Response header field values

 

outResponse

BLOB

Response body

 

Function result

Longint

HTTP status code

Discussion

The array parameters must be text arrays, otherwise an error is generated.

The inRequestInfo array provides Active4D with context information not available in the HTTP request itself. It has a very specific format as follows:

Element

4DK# Name

Description

1

A4D Request Remote Addr

IP address of client (browser)

2

A4D Request Host Addr

IP address of server on which request was made

3

A4D Request Host Port

Port on which request was made

4

A4D Request Secure

"1" if SSL request, "0" if not

Examples of how to initialize this array are contained in the shell database methods On Web Connection and A4D ITK Listener .

The <inRequest> portion of the parameter list changes depending on the command used. The parameters before the common parameters listed above are summarized in the following pages.

After handling a request, Active4D returns everything necessary to create a proper HTTP response in the last four parameters. The actual contents of those parameters depends on whether or not the request executed successfully and what happened during execution.

outHeaderNames/outHeaderValues

If Active4D is given a request to an executable file, these parallel arrays receive the HTTP response headers. The headers returned for an executable file are as follows:

Name

Notes

X-VERSION

Always "HTTP/1.1", always first element

X-STATUS

Always second element. See See Function result for possible values.

Content-Type

The MIME type of the file

Content-Length

The size of the response body (outResponse)

Cache-Control

See RFC 2616 section 14.9

Pragma

Automatically added with the value "no-cache" if Cache-Control is "no-cache".

Expires

Unless explicitly set within Active4D, is set to the current time to prevent response caching

Location

If a redirect is performed

Set-Cookie

If a session is created and session cookies are enabled, or if a cookie is set within Active4D

WWW-Authenticate

If the authenticate command is used

In addition to these headers, you can set response headers within Active4D with the set response header command.

If Active4D is given a request to an non-executable file and is configured to serve non-executables, the following headers are returned:

Name

Notes

X-VERSION

Same as above

X-STATUS

Same as above

Content-Type

Same as above

Content-Length

The size of the file, which is in outResponse

Last-Modified

The file's last modified datetime in HTTP format

If Active4D is given a request to an non-executable file and is not configured to serve non-executables, outResponse is empty and the following headers are returned:

Name

Notes

Path

Full native path to the file

ModDate

Modification date of file in MM DD YYYY Forced format

ModTime

Modification time of file in HH MM SS format

Given this information, the host web server can easily serve the file. For example, using 4D's web server you could call DOCUMENT TO BLOB followed by SEND HTML BLOB . Using ITK you could call ITK_TCPSendFile .

The modification date/time are returned to allow the host web server to implement a caching mechanism.

outResponse

This parameter will always contain the response body. The actual contents depends on the context:

Context

Contents

Executable file, successful execution

The result of the execution

Executable file, error occurred

Variable, see See Interpreter for more info

Non-executable file, no If-Modified-Since header passed in

The requested file (if it can be found)

Non-executable file, If-Modified-Since header passed in

If the file can be found and it has not been modified since the time passed in, the response body will be empty. Otherwise the response body will contain the requested file.

Function result

The status code returned by Active4D will be one of the following:

Code

Name

Description

200

OK

All is well

302

Found

A redirect has been performed in response to a request from an HTTP 1.0 client

303

See Other

A redirect has been performed in response to a request from an HTTP 1.1 client

304

Not Modified

A non-executable file is being served and the client passed an If-Modified-Since header and the file has not been modified since that time

400

Bad Request

The request is malformed in some way

401

Unauthorized

You used the authenticate command

403

Forbidden

Either Active4D has timed out or an attempt was made to execute a script outside of the safe script directories

404

Not Found

The requested resource could not be found

408

Request Timeout

One of the callbacks returned an error during execution of A4D Execute stream request

413

Request Entity Too Large

The request size limit has been exceeded

500

Internal Server Error

An unrecoverable error has occurred during execution

-1

A4D Not Executable

A non-executable file was requested and the "server non-executables" option is off

-2

A4D License Timed Out

The continuous execution time period for the current license has expired

Active4D takes care of creating the proper response headers and response body for each status code.

  • Looking through the shell code, you may notice that Active4D does not use the SEND HTTP REDIRECT command. This command does not in fact send a proper HTTP redirect to the client.

 

A4D Execute request
version 2

 

A4D Execute request(inRequest; inRequestInfo; outHeaderNames; outHeaderValues; outResponse) ! Longint

 

 

Parameter

Type

 

Description

 

inRequest

Text

!

Complete (usually) HTTP request

 

<standard params>

 

 

 

 

Function result

Longint

HTTP status code

Discussion

This command should only be used with 4D 6.5's built-in web server. It expects the complete HTTP request to be contained in the inRequest parameter.

If a form is posted with a lot of data or a large file is uploaded such that the original request was larger than 32K, inRequest will not contain the complete request and Active4D will have no choice but to return HTTP Status Request Too Large (413).

The bottom line is, if you are going to use 4D's built-in web server, use 4D 6.7. If you must use 4D 6.5, I recommend you use ITK.

For an example of how to use this command, see On Web Connection in the shell.

 

A4D Execute BLOB request
version 2

 

A4D Execute BLOB request(inRequest; inRequestInfo; outHeaderNames; outHeaderValues; outResponse) ! Longint

 

Parameter

Type

 

Description

 

inRequest

Text

!

Complete (usually) HTTP request

 

<standard params>

 

 

 

 

Function result

Longint

HTTP status code

Discussion

This command expects the entire request (including uploaded files) to be in the inRequest parameter. It could be used with ITK or TCP Server Deux if you retrieve the entire request into a BLOB first.

  • If you are using ITK or TCP Server Deux, I recommend using A4D Execute stream request instead of this command, as it performs a two-stage retrieval and relieves you of having to retrieve the request.
A4D Execute 4D request
version 2

 

A4D Execute 4D request(inRequest; inVarNames; inVarValues; inRequestInfo; outHeaderNames; outHeaderValues; outResponse) ! Longint

 

Parameter

Type

 

Description

 

inRequest

Text

!

HTTP request

 

inVarNames

Array Text

!

Result of GET WEB FORM VARIABLES

 

inVarValues

Array Text

!

Result of GET WEB FORM VARIABLES

 

<standard params>

 

 

 

 

Function result

Longint

HTTP status code

Discussion

This command is designed for use with 4D 6.7's built-in web server. It expects the header of the request to be in the inRequest parameter, and any form variables to be in the two arrays (which must be text arrays).

If you plan to allow file uploads, you must provide a Compiler_Web method which declares BLOB variables to hold the uploaded files. For an example of how to do this and how to use this command, see the shell.

 

A4D Execute stream request
version 2

 

A4D Execute stream request(inStreamRef; inRequestInfo; outHeaderNames; outHeaderValues; outResponse, outReqInfoNames; outReqInfoValues) ! Longint

 

Parameter

Type

 

Description

 

inStreamRef

Longint

!

TCP/IP stream reference

 

<standard params>

 

 

 

 

outReqInfoNames

Array Text

Receives request header names

 

outReqInfoValues

Array Text

Receives request header values

 

Function result

Longint

HTTP status code

Discussion

This command is designed for use with an ITK or TCP Server Deux-based web server. It does all the work of receiving the request from the TCIP/IP stream referenced by inStreamRef . It does so in two stages, first retrieving and parsing the request header, then retrieving the body if necessary.

A4D Execute stream request returns request info the two parallel arrays outReqInfoNames and outReqInfoValues . The content of those arrays is as follows:

Name

Value

*request method

"GET" or "POST"

*http version

"1.0" or "1.1"

*url

The requested URL without query string

<HTTP header>

<HTTP header value>

The first three elements are a parsed version of the first line of the HTTP request. From the fourth element on are the HTTP headers of the request, URL-decoded and converted to the Macintosh Roman character set.

These arrays are provided to allow you to do custom post-processing of a request without having to parse it yourself. For example, Active4D's ITK shell looks for a "Connection: Keep-Alive" header.

If you have no need for this information, you can simply not pass outReqInfoNames and outReqInfoValues.

  • If Active4D is unable to read the request header, outReqInfoNames and outReqInfoValues will be empty.
Setting the Stream Callbacks

Before using this command you must call:

A4D Set stream callbacks(inReceiveMethod; inStatusMethod)

The two parameters are strings which contain the names of callback methods used by Active4D to retrieve the request from the TCP/IP stream.

The signatures of the callbacks are as follows:

ReceiveCallback
version 2

 

ReceiveCallback(inStreamRef; inStopString; inMaxLen; inTimeout) ! BLOB

 

Parameter

Type

 

Description

 

inStreamRef

Longint

!

TCP/IP stream reference

 

inStopString

String255

!

Receiving stops at this

 

inMaxLen

Longint

!

Maximum bytes to receive

 

inTimeout

Longint

!

Maximum ticks to wait

 

Function result

BLOB

Received data

Discussion

All of the parameters are passed in according to the rules of the ITK command ITK_TCPRecvBlob . Basically, you shouldn't have to worry about how this method works, since a receive callback method is provided for you in the shell.

If the receive fails for some reason, the callback must place a single longint with the value zero in the result BLOB.

 

StatusCallback
version 2

 

StatusCallback(inStreamRef) ! Boolean

 

Parameter

Type

 

Description

 

inStreamRef

Longint

!

TCP/IP stream reference

 

Function result

Boolean

True if stream is still connected

Discussion

This callback is called by Active4D before each receive. Basically, you shouldn't have to worry about how this method works, since a status callback method is provided for you in the shell.

Direct Execution

In addition to executing HTTP requests, there are also a set of commands that allow you to directly execute a file or to execute a block of text:

A4D Execute file

A4D Execute text

A4D Execute BLOB

With these commands you do not have access to most of the HTTP context information like cookies, form variables, etc., since there is no HTTP request to parse.You can, however, pass in a query string.

The "direct" execute commands follow the same pattern as the request execute commands in terms of the parameters they take. For examples of how to use the "direct" execute commands, see the shell database.

Uses for Direct Execution

Direct execution is useful in cases where you are not responding to an HTTP request. In fact, you could easily use Active4D as a general-purpose scripting engine by executing files or blocks of text that are nothing but Active4D code. You need only enclose the scripts in the <% %> tags. If you need user interface commands that Active4D does not provide, you could easily write wrapper methods for use within Active4D.

For example, to show an alert from within Active4D, you could define a method called A4D_ALERT, which would look like this:

`A4D_ALERT(inMessage)

 

C_TEXT($1)

ALERT($1)

 

Interpreter

Much of the power of Active4D lies in its language. Unlike 4D's semi-dynamic HTML tags, which are a transmogrified subset of the 4D language, Active4D's language is a superset of 4D's language. In other words, with Active4D you just write 4D code. In fact, in most cases you can write and test code in 4D and then paste it into a text editor to use with Active4D.

Active4D's interpreter is actually quite a bit stricter than 4D's, in that syntax errors and nonsensical constructions that 4D blithely ignores (but the compiler catches, of course) are considered errors and abort execution.

Active4D extends 4D's language in some important ways. In addition to defining new operators, you can also define methods and libraries of methods. These are covered in See Methods

Flow of Execution

The Active4D interpreter is essentially a text processor. It parses a text input (which might be a file or a block of text) and writes to a response buffer . The text input may have any mixture of HTML and embedded Active4D source code. When execution begins, the response buffer is empty.

Embedding Source Code

Active4D source is separated from HTML by one of three tag pairs:

Begin tag

End tag

<%

%>

<?

?>

<!--Active4D

-->

Although any of the tags are valid, the corresponding end tag must be used with its beginning tag.

In the case of the first two tag pairs, there is no need for white space between the tags and the Active4D source. In the case of the third pair, which is actually a specialized HTML comment, you must leave at least one whitespace character between the tags and the Active4D code it encloses.

  • I recommend you use <% %>, as most visual HTML editors automatically recognize them as embedded script tags. This is because Microsoft's Active Server Pages (ASP), the world's most popular embedded scripting language, uses these tags.

Here is an example of Active4D source code embedded in the three different types of tag pairs.

<% write("hello") %> `OK

<?write("hello")?> `OK with no space around these tags

<!--Active4D write("hello") --> `OK

<% write("hello") ?> `Error: can't mix tags

<!--Active4Dwrite("hello")--> `Error: need space around these tags

Like 4D, Active4D is line-based; the end of a line or the end of a code block marks the end of a statement. However, you may have as much whitespace as you like between the Active4D tags and the actual source.

Input Parsing

Let's look at an extremely simple example which will illustrate how Active4D parses and executes a script. Suppose we execute a file which has the following contents:

<html>

I was here at <% write(Current time) %> on <% write(Current date )%>

<html>

Active4D begins execution by scanning for a source code begin tag. As it scans, the text is appended to the response buffer. When the first "<%" in the above example is reached, the response buffer contains:

<html>

I was here at

Once a source code tag is found, Active4D skips past the tag and goes into execution mode. In execution mode, Active4D continues to parses the text, but instead of passing the text through to the response buffer, it resolves the text into executable tokens and then executes the tokens.

It would be pretty useless if you could not write dynamically generated text to the response buffer within your source code, so Active4D provides a write command to do just that. The write command takes a value of any type, converts it to text, and appends the result to the response buffer.

In the example above, the write command is the first token. The write command handler is given control, which proceeds to evaluate the parenthesized expression following the command. In this case the expression is the 4D command Current time , which is converted to text as if passed to the String command. The text is appended to the response buffer, and the interpreter then continues parsing.

The next token is "%>", which is a source code end tag. Active4D skips to the end of the tag and switches back to scan mode. At this point the response buffer contains:

<html>

I was here at 19:27:13

The interpreter scans up to the next source code begin tag, appending to the response buffer. At the start of the next source code block, the response buffer contains:

<html>

I was here at 19:27:13 on

The interpreter then executes the second source code block as it did the first, after which the response buffer contains:

<html>

I was here at 19:27:13 on 10/19/2001

The interpreter scans to the end of the file and returns control to the caller. At that point the response buffer contains:

<html>

I was here at 19:27:13 on 10/19/2001

</html>

If the interpreter was invoked by Active4D's web server, the web server generates the HTTP headers necessary to describe the response to the client. Finally, control returns to 4D, and the shell sends the generated headers and response body to the client browser, which displays the text:

I was here at 19:27:13 on 10/19/2001

The client cannot see the source code because it is stripped out before being sent back from the server.

Language Syntax

For the most part Active4D's language (henceforward just "Active4D") has the same syntax as 4D's language. However, there are a few important differences and enhancements.

Source Code Structure

Because Active4D source code is plain text, you are free to use whatever indentation style you wish -- the 4D method editor is not there to do it for you. Typically you will want to use tabs in your text editor to perform indentation of control structures. For example:

<% write("hello") %>

<%

write("hello") `This is exactly equivalent to the previous block

%>

 

<%

if ($sort_ascending = "1") `Note the space around the =

order by([customers];[customers]lastname;>)

else

order by([customers];[customers]lastname;<)

end if

%>

You can have any number of Active4D code blocks within an HTML page. In addition, it is not necessary to complete control structures within the block in which they are declared. This powerful feature allows constructs like this:

<% for($i;1;3) `I'm starting the loop here... %>

Active4D rocks!<br>

<% end for %>

 

`Here is the output

Active4D rocks!

Active4D rocks!

Active4D rocks!

Using this feature with If/Else/End if, you can conditionally show entire tables, form items, etc.

  • Just because your source is embedded inside your web pages in text form does not make it inherently insecure. To understand why, please see See Security
Case Sensitivity

Like 4D, Active4D is not case sensitive, so you can just as easily write "first record" as "FIRST RECORD". But whereas the 4D method editor would change "first record" to "FIRST RECORD" when it parsed the line, in Active4D it will remain "first record" because you will be using an external text editor.

Case-insensitivity extends to table and field names, method names, and variable names.

Expression-based

One difference between 4D's language and Active4D is that Active4D is expression-based, like C/C++/Java. In other words, everything is an expression, including an assignment. So the assignment:

$i:=10

actually evaluates to the value of $i after the assignment, namely 10. This allows you to do convenient tricks like this:

first record([contacts])

 

while (($name:=[contacts]lastname) = "a@")

writebr($name)

next record([contacts])

end while

Comments

Active4D supports regular 4D comments (indicated by ` ), either on a separate line or at the end of the line, with no limit on their length. You may also use the characters "//" as a synonym for ` to indicate a 4D-style single-line comment.

In addition, Active4D supports block comments . Unlike 4D comments, block comments may be embedded within a line or may span multiple lines.

Block comments begin with the character sequence "/*" and end with the character sequence "*/". For those of you who know other languages, this is the standard syntax for block comments in C/C++ and Java.

Here are some commenting examples:

/*

This is a multi-line comment that will be rather lengthy. By using block

comments I can format it easily and it is easier to read.

*/

 

`A plain old 4D-style single-line comment

// A new-style single-line comment

writebr("hello") // Also can be used at the end of a line

 

/*---------------------------------------------------------------------

MyLibraryMethod($inFoo; $inBar) -> Text

 

$inFoo Longint A foo

$inBar Longint A bar

RESULT Text A foobar

-----------------------------------------------------------------------*/

 

writebr("Block comments can even be " + /* wow! */ "embedded!")

 

/*

writebr("Using block comments...")

writebr("is a quick and easy way...")

writebr("to comment out a block of code")

*/

Identifiers

Identifiers in Active4D follow the same rules as in 4D, both in terms of valid characters and maximum length (31 characters excluding any prefix).

Active4D maintains its own local variables which are indicated by a leading dollar sign, as in 4D. You can also access existing 4D process and interprocess variables (using "<>", not the Macintosh diamond character), as well as any fields in the database. Last but not least, all 4D named constants (4DK#) are recognized, including ones which you may have installed in your database structure.

Data Types

Active4D can operate on all 4D data types except for 2D arrays. Data types may be implicitly converted according to the same rules 4D uses, or explicitly converted using commands such as String and Num .

Compiler Declarations

All compiler declarations are supported, but you may only use them on local variables. By predeclaring local variables with compiler declarations, you can achieve better type safety in your code.

For example:

c_longint($long)

$long := 7

$long := 13.27

 

`Without the compiler declaration, $long would become a real = 13.27.

`Because it was declared a longint, its value is now 13.

 

$long := "foobar" `this generates an incompatible argument error

Active4D imitates 4D's interpreted behavior exactly in regards to variables that have been declared in compiler declarations. Once a variable has had its type fixed by a compiler declaration there are two ways it can be changed:

  • With another compiler declaration. If the new type is not assignment-compatible with the old type, the variable will be set to a null value. Otherwise a type conversion is done.
  • By passing it as a parameter to a command that sets the parameter value inline, such as GET PICTURE PROPERTIES .
Array Support

All array types except 2D arrays are now fully supported in Active4D. This includes picture and pointer arrays. You may declare and use local arrays in Active4D just as you would in 4D.

Pointer support

Active4D supports pointers to process and interprocess variables, tables, and fields. You may create and dereference pointers to any of these entities within Active4D.

As in 4D, you may not create a pointer to a local variable. However, Active4D allows you to pass local variables by reference to its own methods, which is effectively the same as using a pointer (but easier). For information on pass by reference, see See Methods

Literals

Active4D recognizes string, number date and time literals in all the standard formats except scientific notation. If the current platform charset is Shift_JIS or GB2312 , string literals are assumed to be in Shift_JIS or GB2312 and are parsed accordingly.

  • Time literals must use '?' as the delimiter.

There is one major extension to string literals in Active4D. Strings literals support backslash-prefixed embedded control characters:

Backslash combination

Resolves to

\n

Char(Line feed)

\r

Char(Carriage return)

\t

Char(Tab)

\"

Char(Double quote)

\\

\

\<char>

<char>

Here is an example of using backslashes in a literal, with the equivalent 4D code. You decide which is easier to write and read.

write("This is line 1.\r\"It's cool!\", you say.")

 

Output:

This is line 1.

"It's cool!", you say.

 

`4D code to do the same

"This is line 1."+Char(13)+Char(34)+"It's cool!"+Char(34)+", you say."

User-defined constants

In addition to supporting all installed 4DK# constants, Active4D allows you to define named constants at runtime using the define command. For more information on the define command, see See define.

Typing of Values

Typing of local variables in Active4D follows the same rules as 4D. If a local variable has not been declared with a compiler directive, it is variant: its type changes on the fly according to the type of value assigned to it. Once it has been declared within a compiler declaration, it follows the rules outlined in See Compiler Declarations above.

On the other hand, 4D variables and fields are always considered invariant. You cannot assign a value to them that would change their type.

For all variables and fields, Active4D will generate an error and abort execution if a referenced variable or field is undefined.

Operators

Active4D implements all of 4D's operators (with the exception of some picture operators), including array indexing and character references. Character references must use the syntax [[N]] , where N is a number from 1 to the length of the text being referenced.

Active4D operators have the same restrictions on the data types they will accept as 4D does. Active4D will abort with an error if an attempt is made to divide or modulo by zero.

New Active4D Operators

Active4D adds many operators from C/C++/Java which make writing code faster and easier to read. Here are the new operators:

++

prefix and postfix increment

--

prefix and postfix decrement

+=

add and assign

-=

subtract and assign

*=

multiply and assign

%=

modulo divide and assign

/=

divide and assign

\=

integer divide and assign

^=

exponentiate and assign

|=

bitwise inclusive OR and assign

&=

bitwise AND and assign

<<=

bitwise shift left and assign

>>=

bitwise shift right and assign

Here are some examples of their usage:

$i:=3 `I think $i=3 now

$j:=++$i `increments the value of $i before the assignment, $j=4

$k:=$i++ `increments $i after the assignment, $k=4, $i=5

$i*=3 `multiplies $i by 3 and assigns, $i=15

$i+=3 `adds 3 to $i and assigns, $i=18

$i>>=1 `bit shifts $i one bit and assigns, $i=9

In other words, any expression:

<assignable> <op>= <value>

is equivalent to:

<assignable> := <assignable> <op> <value>

Any of these operators will work with array elements. So, for example, you can increment the fifth element of an array by using the ++ operator:

$myArray{5}++ OR ++$myArray{5}

Picture Operators

Active4D supports horizontal and vertical concatenation for pictures. Thus the operators that may be use with pictures are:

+

horizontally concatenate

/

vertically concatenate

+=

horizontally concatenate and assign

/=

vertically concatenate and assign

Enhanced Operators

The + operator has been enhanced in Active4D to auto-convert arguments to the right to text if the argument to the left is text.

For example:

$s := "I was here at " + Current time + " on " + Current date

Notice there is no need to explicitly convert Current date and Current time to strings.

Remember that the argument to the left of the + must be text for this trick to work, so the following statement would not work, because the + operator would try to add ", " to the date returned by Current date :

$s := Current date + ", " + Current time + ": I was here"

In this case, you would have to "prime the pot" by converting Current date to a string:

$s := string(Current date) + ", " + Current time + ": I was here"

Control Structures

All of 4D's control structures and flow of control keywords are supported, with four notable additions: for each/end for each , break , return , exit and continue . If you are familiar with other languages that use these keywords, they work exactly as they do in those languages. If are aren't familiar with those languages, here's how they work.

for each/end for each

This looping control structure is the easiest way to iterate over a collection. For more information on collections, see See Collections and See Iterators.

break

If you are within any kind of loop ( For, While, Repeat ), the break keyword (used on a line by itself) will transfer execution to the first line of code after the end of the loop. The loop variable used by a For loop will not be changed. If you use break outside of a loop it will generate an error.

return

This keyword (used on a line by itself) transfers execution to the first line of code after the current code block. If you are within an Active4D method, execution will continue at the first line of code after the line that called the method. It can also take a parenthesized expression to return as the result of the method.

continue

If you are within any kind of loop ( For, While, Repeat ), the continue keyword (used on a line by itself) will transfer execution directly to the top of the loop. The loop variable in a For loop will be incremented according to the For clause. If you use continue outside of a loop it will generate an error.

exit

This keyword, used on a line by itself, immediately aborts all execution without generating an error. This is primarily useful for debugging, when you want to dump the internal state before a certain point and then stop. This keyword is also useful if you have detected an error condition from which you cannot recover and you want to immediately stop execution.

Examples

Here are some examples of how to use the new keywords:

`Using break to terminate an infinite loop

$i:=0

 

while(true) `You could never do this in 4D!

if (++$i > 10)

break `The closest loop, in this case the while loop, will be exited

end if

 

write($i) `This will NOT be executed once break is executed

end while

 

`Here's the Active4D way of breaking out of a for loop

for ($i;1;size of array($names))

if ($names{$i} = "g@")

break

end if

 

writebr($names{$i}) `This will not get executed after break is executed

end for

 

`Here's the 4D way

for ($i;1;size of array($names))

if ($names{$i} = "g@")

$i:=size of array($names)+1

else

writebr($names{$i})

end if

end for

 

`Using continue in a for loop

for ($i;1;size of array($names))

if ($names{$i}="s@")

continue `Immediately goes to next iteration of loop

end if

 

writebr($names{$i})

`Do lots of other stuff here. The effect of continue above is to skip this

end for

Working with Paths

In the course of programming a web site with Active4D, you will often need to specify file path. If you have done any work with paths in 4D, you know what a pain it can be, especially when dealing with multiple platforms.

URL-Style Paths

All commands that take a path in Active4D -- including Active4D's implementation of standard commands like Open document -- can take a URL-style path, which uses `/' as the directory separator. This allows you to program in a platform-neutral way, without having to worry about the native directory separator.

Absolute vs. Relative Paths

A path can be absolute or relative . What that means depends on the command using the path. Standard 4D document commands and Active4D's own commands treat paths differently.

4D document commands
  • As with 4D, in Active4D absolute paths are relative to the computer, allowing you to access any volume mounted on the host machine (including network volumes). Remember, however, that by default Active4D restricts document command access to the web root directory. You must use the "safe doc dirs" option in Active4D.ini to gain access to directories outside the web root directory. For more information, see See The "safe doc dirs" Config Option.
  • As with 4D, relative paths are relative to the default directory, which is the database directory under Server/Standalone or the application directory under Client.
Active4D commands
  • Active4D commands are designed for use within the context of a web page. Thus their "world," so to speak, is limited to the web root directory.
  • Absolute paths used with Active4D commands are relative to the web root directory. Thus if the root directory is:
  • /Server/db/web
  • the path
  • /accounting/default.a4d
  • is actually the path
  • /Server/db/web/accounting/default.a4d
  • Relative paths used with Active4D commands are relative to the directory of the currently executing file.
Path Utilities

Active4D provides a many utility commands for working with paths. For example, if you need to use a 4D document command on a file within the web root directory, the get root command returns the full path to the current root. In addition, there are commands to extract the filename from a path, the extension from a filename, and the directory from a path.

For more on these commands, see See System Documents.

  • Unlike Active4D v1, version 2 adds a trailing `/' to any directory paths returned by Active4D commands such as get root .
Path Limits

The maximum length of a path, including the root directory, is 255 characters. The maximum length of any component of a path is 63 characters.

You must ensure that your path lengths are within these limits, or Active4D will not be able to find your files.

Including Other Files

One of the most powerful features of Active4D is its ability to include other files during execution. To include a file, you use the include command, passing a relative or absolute URL-style path. Since include is an Active4D command, absolute paths are relative to the root, and relative paths are relative to the currently executing file.

If found, the included file is interpreted as if it were part of the source file. The scope of the included file is whatever the scope was where the include command appeared.

This means that any local variables declared before the include are available in the scope of the include file's code, and any local variables declared in the include file are available to the source file when the include file is finished executing.

Included files may in turn include other files, ad infinitum (or until stack space or memory runs out). Before including a file, Active4D checks to see if the include is a circular reference and aborts execution if it is.

  • You may also use the include into command, which allows you to include a file and place the output into a variable.
Uses of Included Files

There are many uses of included files. Some common ones are:

  • Factoring out common page elements, such as headers and footers. When the included file is modified, all pages that include it automatically update.
  • Separating functional sections of a page into separate files. This allows you to create a page design that consists of an overall framework, within which you "plug in" various pieces. By using includes, you can edit the pieces separately without affecting the overall design. This technique is analogous to splitting a large method into several smaller methods.
  • Conditionally building a page. You may put an include statement within an If/Else/Endif or Case/Endcase construct to conditionally include various files. This allows you to use the same page to display different output based on one or more criteria.
  • Using include into to build an HTML page to send via email.
Including only once

You can also include files using the require command. The require command works like include , but it guarantees that any given file will only be executed once via require within a single execution of the interpreter.

The require command is primarily useful for creating files of global constants, variables and methods that only need to be loaded once per Active4D session. All files that reference these globals can require the globals file and you don't have to worry about the overhead of executing the file or the problem of defining named constants twice, which is an error.

Calling 4D Methods

Although you can call 4D methods within Active4D, this ties you to the database structure, which is exactly what Active4D is designed to avoid. If possible, you should define and use methods within Active4D. For more information on defining methods in Active4D, see See Methods

  • There is a bug in 4D 6.5.7 and earlier that prevents 4D methods from being called from a process directly created by 4D's built in web server. This bug does not exist in 6.5.8+ or 6.7.

Nonetheless, should you need to, you can call any 4D method within Active4D using the exact same syntax you would within 4D.

Parameter Passing

When calling a 4D method from Active4D, you pass parameters just as you would within 4D. Strings are converted to Text and Longints are converted to Reals before being passed. You may return any type of value from the method.

  • If you are passing textual parameters to a 4D method from Active4D, the parameter in 4D must be declared C_TEXT. If you are passing numeric parameters, they must be declared C_REAL. Otherwise a runtime error may occur in a compiled database.

Active4D has no way of matching parameters passed to a 4D method with the actual parameters declared in that method. It is up to you to make absolutely sure that the number and type of parameters declared in the 4D method are compatible with what you pass to it. If the 4D method expects some parameters to be optional, you may of course not pass those.

In an interpreted database, if the method parameters are not assignment-compatible, 4D will initialize the parameter to a null value. In a compiled database, however, if the parameter types do not match exactly, a runtime error will be generated, which will effectively bring your application to a halt in a very unfriendly way. You do not want this to happen.

Indirect Method Calls (aka Poor Man's method pointers)

In addition to calling 4D methods as you would within 4D, by directly referencing the method name, you can also indirectly call a method by name using the Call 4D method command.

The syntax of this command is as follows:

Call 4D method(inMethodName {;inParam1 {;inParamN}})

The method name may be any valid expression that returns text. This powerful feature allows you to dynamically determine which of several methods you call, as long as their parameter lists are compatible. This is (sort of) the equivalent of a method pointer in other programming languages.

As with ordinary method calls, the method called by Call 4D method may return a value.

If the 4D method returns a value, you may ignore it if you have no use for it. You need not assign it to a dummy variable as you would in 4D. On the other hand, if the 4D method returns no value, attempting to use the result of such a method will result in an error.

Collections

Active4D adds a new data type to the language: collections . A collection is a group of key/value pairs which are stored in memory. The keys are strings (with a maximum length of 255) and the values may be any 4D data type, including arrays. The key/value pair is also referred to as an item .

In classic programming terms, a collection is also known as an associative array, a dictionary, a symbol table or a map. In 4D terms, you can conceptually think of a collection as two parallel arrays, with keys being in one array and the values in the other. Of course, you can't do this in 4D because 4D arrays can only contain a single type, and a collection value may be of any type.

Active4D provides a full suite of commands for creating your own collections. In addition, Active4D uses collections to store HTTP headers, query parameters, form variables, cookies, global variables, and sessions.

For more information on the collection commands, see See Collections.

Collection Handles

Collections are referred to by a Longint handle, much like an ObjectTools object handle or a 4D hierarchical list handle.

Active4D maintains an internal list of user-created collections. All collection commands that take a collection handle check the handle against this list and generate an error if the handle is not valid. Thus you are prevented from crashing the server by passing in a bogus handle.

Local vs. Global Collections

Collections can be either local or global . A local collection is automatically deleted when a script finishes executing. A global collection remains in memory throughout the life of the server.

Local collections are like local variables -- you use them for temporary storage within a single script. You should always use a local variable to store a local collection handle.

Global collections are like interprocess variables -- you store application-wide data in them. Typically you will create global collections in the On Application Start method and clear them in the On Application End method.

Using Collections

Collections come in two basic varieties: read-only and read-write . You can perform the following operations with read-only collections:

  • Get a collection value given a key. If the value is an array, you may retrieve an element of the array directly. Key matching is case-insensitive.
  • Get all keys into a 4D array.
  • Get all values into a 4D array if it is known they are all of the same type.
  • Get the count of key/value pairs in the collection.

Read-write collections add the following operations:

  • Set a collection value given a key. If the value is an array, you may set an element of the array directly.
  • Delete a collection item given a key. If the key contains a wildcard, all matching items are deleted.

In addition to these basic operations, some of the specialized collections defined by Active4D provide other operations as well. These operations are covered in the relevant command reference sections.

Referencing Collection Values

Active4D extends the syntax of the {} indexing operators to allow indexing a collection by keys. The syntax of this way of indexing is as follows:

collectionRef{key}

where collection is either a collection handle or collection iterator, and key is a text expression. If an item in the collection exists with the given key, the result of this expression is the item's value, and it may be treated in all respects as a variable of that type. If no item exists in the collection with the given key, the result of the expression is an empty string.

Because the result of the expression is treated as the value it resolves to, you can use collections as a natural part of the language. For example:

`Set a counter in our session

session{"counter"} := 0

 

`Now increment the counter

++session{"counter"}

 

`Store a form variable in the session

session{"username"} := form variables{"f_username"}

 

`Embed an array in a collection

array string(255; $people; 0)

set array($people; "Tom", "Dick", "Harry")

$c := new collection

$c{"people"} := $people `puts a copy of $people array in collection

 

for ($i; 1; size of array($c{"people"}))

writebr("Hi " + $c{"people"}{$i})

end for

 

append to array($c{"people"}; "Louise")

Note in the example above that you put a copy of an array into a collection by directly assigning the array to a collection item, like this:

collectionRef{key} := arrayVar

You can even embed collections in collections (by storing their handles) to any depth and reference their items by adding more indexes, like this:

$foo := $c{"level1"}{"level2"}{"level3"}{"the_key"}

Iterating Over a Collection

Very often you may need to iterate over every item in a collection. There are two ways to do so in Active4D.

The first (and easiest) way to iterate over a collection is to use the for each/end for each loop control structure. For more information on for each , see See Iterators.

The second way to iterate over a collection is through an iterator . Every collection provides a command which returns an iterator for the collection. You use this iterator to traverse the items in the collection.

In addition, some collections allow you to get an iterator for a specific item given the item's key. If no item with the given key exists, you are given an empty iterator. An empty iterator is a special iterator that has the following properties:

  • The iterator itself (a Longint ) is zero
  • is an iterator will return false
  • get item key will return an empty string
  • get item value will return an empty string
  • get item type will return Is Undefined (5)

You can identify an empty iterator either by testing equal to zero, by calling is an iterator , by testing for an empty key, or by testing the item type.

For more information on iterators, see See Iterators.

HTTP Data Access

When Active4D is used as the web server, the interpreter has full access to both the HTTP request and the response data. This data is critical when developing high-powered web sites. The commands necessary to access this information are covered in See Command Reference

Request Data

As was discussed in See HTTP Server an HTTP request consists of several headers and an optional body, in addition to the requested URL itself.

The data encapsulated in an HTTP request includes:

  • Query string parameters
  • Form Variables
  • Cookies
  • HTTP headers
  • Uploaded files

In addition, information about the host environment is passed in to Active4D.

  1. It is important to note that all of this data is encoded is some way or another. Without Active4D, to extract any meaningful information you would have to:
  2. Understand the detailed HTTP specification and format for each type of data.
  3. Write the code to parse and extract the data, making sure to follow the rules you learned in Step 1.
  4. URL-decode the data which, according to the HTTP specification, should be URL-encoded.
  5. Convert from the URL-decoded data from the ISO-8859-1 character set to the Mac Roman character set.
  6. Figure out where and how to store the data in a meaningful way.
  7. Write many methods to access that data in a simple way.

If you have never gone through this process, here's a little tip: the time it takes to do Step 1 alone will cost you more than the price of Active4D!

Fortunately Active4D does all of the above for you. You never have to deal with the particulars of the HTTP specification, which allows you to focus on the problem at hand -- building a great web site.

Auto Variable Creation

The primary way in which you "pass" parameters from one page to another in a web site is through form variables and query string parameters. Active4D places those parameters in easy-to-access collections.

For example, if the user posts a form which contains a field called "f_name" and you want to access the contents of that field, you can simply use the command:

get form variable("f_name")

Likewise, to access a query string parameter called "recnum", you could use:

get query param("recnum")

This is simple enough, but Active4D gives you an even easier way: auto-variable creation.

By default, the interpreter automatically creates a local text variable for every form variable and query string parameter in the HTTP request. If there are multiple form variables or query parameters with the same name -- which is the normal occurrence if more than one item is selected from a multiple choice form list -- a text array is created which contains all of the values.

For example:

  • If a form is posted with a field called "f_name" and the user enters the text "John Doe", Active4D will create a local text variable $f_name whose contents is the text "John Doe".
  • If a form with a multiple-choice list called "f_experience_level" is posted with the selected items "Beginner" and "Advanced", Active4D will create a local text array $f_experience_level with two elements that contain the text of the selected items.

This feature allows you to conveniently access form variables and query string parameters as if they were "passed" to your script as named method parameters.

Testing Form Buttons

If a submit button is clicked on a form, the browser posts the button's form name and value. Buttons which appear on a form but are not clicked are not included in the posted form data.

Frequently you need to test a posted form to see which button was clicked. For example, your form may have "Search", "Previous" and "Next" buttons. There are two ways to test which button was clicked. The first way is to check the form variables collection. If a button was clicked, it will be in the collection and its value will be returned. If the button was not clicked, it will not be in the collection and an empty string will be returned. Using this technique, your code would look something like this:

Case of

:(get form variable("f_search") # "")

`Do the search

:(get form variable("f_previous") # "")

`Go to the previous group of records

:(get form variable("f_next") # "")

`Go to the next group of records

End case

Because Active4D only creates local variables for fields that are posted, a variable will only be created for the button that was clicked. Thus you can also use the defined command to test which button was clicked, like this:

Case of

:(defined($f_search))

`Do the search

:(defined($f_previous))

`Go to the previous group of records

:(defined($f_next))

`Go to the next group of records

End case

Response Data

You control the response body indirectly with the write command and its peers. In addition, Active4D gives you dedicated commands for setting the response headers.

The data encapsulated in an HTTP response includes:

  • Cache control
  • Character set
  • Content type
  • Cookies
  • Expires date
  • Other headers

Some HTTP response headers are simple in their format. Others require a specific format which you must follow. As with the request headers, without Active4D you would have to know the HTTP specification for each format. For those headers that require special formatting, Active4D gives you simple commands that relieve you of having to know the HTTP specification.

Working with Character Sets

Text comes into Active4D from several sources:

String literals
  • These may be in the native platform charset (if you are using a text editor) or ISO-8859-1 (if you are using an HTML editor).
Database
  • 4D uses the Macintosh Roman charset internally and expects text to be in this charset.
HTTP requests
  • Query string parameters and form variables are URL encoded and may be in any charset.
Files
  • Text files may be in any charset. It is up to you to know which one.

Text can go out from Active4D to several destinations:

Web browser
  • Text sent to the browser via the write commands should be in the target HTML charset, usually ISO-8859-1 (Latin1). In addition, you may need to HTML-encode reserved characters such as `<'.
Database
  • 4D expects the Macintosh Roman charset.
Files
  • You can use whatever charset you want.

Clearly, it would be a real pain -- not to mention error-prone -- if you had to remember to do all of the character set conversions yourself. Fortunately, Active4D allows you to configure the various charsets and then takes care of most of these conversions for you.

Internally Active4D uses the Macintosh Roman charset, since that is what 4D expects. The character sets you configure determines which charset Active4D converts from on input and which charset it converts to on output.

Platform Character Set

The platform charset determines what charset Active4D converts from when parsing string literals. When converting from a Roman language charset, characters are converted to Macintosh Roman. When converting from a non-Roman charset, no conversion is performed.

For example, if you are using BBEdit on the Mac to write your embedded scripts, string literals would be in Macintosh Roman. On the other hand, if you are writing your scripts with Dreamweaver, string literals would be in the charset of the page you creating.

You can set the platform charset with the "platform charset" config option in Active4D.ini . The possible options are "mac", "win", "latin1", "ISO-8859-1", "shift_jis" or "gb2312". Note that "latin1" and "ISO-8859-1" are equivalent. You may also use the set platform charset command.

On a Roman language system, the default platform charset is the native charset for the platform on which Active4D is running ("mac" or "win"). On a Japanese language system, the default platform charset is "shift_jis". On a Chinese language system, the default platform charset is "gb2312".

Output Character Set

The output charset determines what charset Active4D converts to when text is written to the response buffer. If the output charset is a Roman language charset, it is assumed text is always converted from Macintosh Roman. If the output charsest is a multibyte charset, no conversions are performed.

You can set the output charset with the "output charset" config option in Active4D.ini . The possible options are the same as for the platform character set. You may also use the set output charset command.

The default output charset on Roman language systems is ISO-8859-1 , which is the default charset for HTML. The default output charset on Japanese language systems is Shift_JIS . The default output charset on Chinese language systems is GB2312 .

Output Encoding

The output encoding determines how Active4D converts special characters to HTML character entities when text is written to the response buffer. Output encoding is performed before the output character set conversion, since the encoding tables are based on the Mac Roman character set.

You can set the output encoding with the "output encoding" config option in Active4D.ini . You specify one or more bit flags to indicate which characters to encode. More than one flag can be specified by separating them by `+' and any number of spaces. The bit flags are "none", "quotes", "tags", "ampersand", "extended", "html" and "all" (without the quotes). Note that "extended" and "html" are synonymous.

You may also use the set output encoding command at runtime. For more information on output encoding, see See set output encoding.

The default output encoding on Roman language systems is "html". The default output encoding on Japanese language systems is "none".

HTTP Request Decoding

When parsing an HTTP request, the headers are left as is. This is not a problem, since all headers except cookies will be in US ASCII and the character set is not an issue.

Query string parameters and form variables are automatically URL decoded, and the name portion of their name/value pairs is converted from ISO-8859-1 to Macintosh Roman.

If the platform charset is a Roman charset, query string parameter and form variable values are converted from ISO-8859-1 to Macintosh Roman. If the platform charset is a multibyte charset (like Shift_JIS), no conversion is performed.

Working with Files

Active4D cannot know the source platform of a file or its charset. It's up to you to know the charset used by a file, then convert to the appropriate character set when saving to the database or writing to the browser.

You can convert between character sets using Win to Mac , ISO to Mac and Mac to ISO .

Error Handling

Active4D goes to considerable lengths to catch errors and display meaningful error messages. If any errors occur during the execution of an Active4D program, execution is immediately aborted and the error handler takes over.

For complete information on error handling within Active4D, see See Error Handling

Script Timeout

Despite our best intentions, sometimes our scripts may do bad things like going into infinite loops or waiting an inordinate amount of time for a shared resource.

Every script is given a set amount of time to execute. Before executing each line, Active4D checks to see if the timeout has been reached, and if so it generates an error and aborts execution.

You may set the script timeout with the "script timeout" config option in Active4D.ini . The value set with this option is the minimum script timeout in seconds and is the default value for all subsequent script executions.

The actual timeout can be set higher within Active4D with the set script timeout command. This command will affect the next execution of Active4D, not the one in which the command is used, and in no case can it be set lower than the minimum value set in Active4D.ini .

 

Methods

As you build your web site with Active4D, you will undoubtedly come across chunks of code that can be reused in many different contexts, just as you would reuse code in 4D. Fortunately, Active4D has a powerful system for defining its own methods.

Defining Methods

The simplest way to define a method in Active4D is to declare it inline with the rest of your code, then reference it sometime later in the flow of execution.

Inline methods "live" only as long as the current invocation of Active4D. This means that you incur a small performance penalty each time the method is used, because it has to be parsed and stored in temporary memory before it can be used.

In general, you will only want to use inline methods during testing and debugging, because there is a much better way of defining and using methods: libraries . Libraries have many advantages over inline methods and allow you to group many methods together into one logical unit. They are covered completely in the next chapter, See Libraries.

Method Declaration

Whether a method is defined inline or in a library, the syntax for declaring a method is the same. If you are familiar with AppleTalk, the syntax may look familiar:

method "<name>" {({&}$arg1{=expression} {...{&}$argN{=expressionN}})}

<statements>

end method

I know this looks complex, but it is actually quite simple. Keep reading.

Method Name

The method name must be a double-quoted literal string which follows the rules for 4D method names in terms of allowable characters and maximum length (31). The double-quotes are necessary to allow you to include spaces as part of the method name, and to differentiate the method name from the method keyword.

Here is a simple Active4D method definition:

method "SayHello"

write("Hello world!")

end method

Method Parameter Declaration

If a method takes parameters, the parameter list must follow the method name enclosed in parentheses. Unlike 4D, where method parameters are numbered, Active4D method parameters are declared by name, with each parameter becoming a local variable with that name within the body of the method. Parameter names must begin with `$'and follow the rules for local variable names.

method "Concat"($inString1; $inString2)

$result:=$inString1

 

if (length($inString1) > 0)

$result + =" "

end if

 

$result += $inString2

return ($result)

end method

Note that there is currently no facility in Active4D for passing a variable number of parameters. You can simulate this technique either by passing a reference to an array and accessing the elements of the array, or by using default parameters . Both of these techniques are covered later in this chapter.

Method Parameters

Parameters to Active4D methods differ from 4D method parameters in several important ways.

Parameter Type

There is no typing of parameters, as they are simply local variables with a variant type. This means you can pass values of different types in the same parameter, as long as the use of the parameter in the method would not cause any type incompatibilities. If you want to ensure a parameter is of a particular type, you would have to test its type within the method using the Type command.

Here is an example of how you can take advantage of the variant parameter typing:

method "WriteMany"($inValue; $inHowMany)

for ($i;1;$inHowMany)

writebr($inValue) `writebr takes any type, thus $inValue can be anything

end for

end method

 

WriteMany("Hello world!";3)

WriteMany(7.13;2)

 

The output:

Hello world!

Hello world!

Hello world!

7.13

7.13

Scope

Parameters passed to an Active4D method become a local variable within the scope of the method. Thus, like local variables in 4D, they have no existence outside of the method body.

method "ParamTest"($inParam)

$inParam:="bar" `The global $inParam is still "foo"

writebr($inParam)

end method

 

$inParam:="foo"

writebr($inParam)

ParamTest($inParam)

writebr($inParam)

 

The output:

foo

bar

foo

Likewise, local variables defined outside of an Active4D method cannot ordinarily be referenced within the method.

method "LocalTest"

write($local) `This generates an 'unknown identifier' error

end method

 

$local:="foobar"

LocalTest

Referencing "Global" Local Variables

Just as in 4D, it is best to pass whatever values a method needs as parameters. But what if you want to change the value of a local variable which was declared outside of a method? There are two ways to accomplish this: pass by reference and the global keyword.

Pass by reference allows you to effectively pass a pointer to a local variable. This is the preferred way of modifying locals external to the method, and is covered below. But at times it is preferable to reference an external local variable directly. For example, you may need to reference or modify many external variables within a method, and it would be too cumbersome to pass many parameters.

In these cases you may use the global keyword to declare a list of local variables that you want to make accessible to the method, as in this example:

method "WriteNames"

global($names)

 

for($i;1;size of array($names))

writebr($names{$i})

end for

end method

 

array string(80;$names;0)

set array($names; "Tom"; "Dick"; "Harry")

`set array is covered in the Commands chapter

Pass by Reference

Normally, parameters passed into Active4D methods are passed by value . In other words, the expression passed into the given parameter is evaluated and the constant value resulting from the expression is assigned to the parameter.

In addition to pass by value, Active4D also allows you to pass by reference . This powerful feature is activated by prefixing a parameter in the method argument list with an ampersand (&).

Reference parameters are essentially pointers to the entity that was passed into them. You can pass 4D pointers into Active4D methods, but there are two important ways in which reference parameters are unlike 4D pointers:

  • You need not explicitly dereference the parameter to access the underlying value
  • You can pass local variables by reference

Any entity that is assignable -- including variables, fields, array elements, and character references -- may be passed by reference. You can even pass a character reference to an array element by reference!

Here is some examples of passing by reference:

method "ParamTest"($inByValue; &$ioByReference)

++$inByValue

++$ioByReference

end method

 

$byValue:=7

$byReference:=7

ParamTest($byValue;$byReference)

`At this point $byValue still = 7, but $byReference = 8

 

method "ShowArray"(&$inArray)

for ($i;1;size of array($inArray))

writebr($inArray{$i})

end for

end method

 

array longint($longs;0)

set array($longs; 7; 13; 27)

ShowArray($longs)

 

array text($names;0)

set array($names; "Tom"; "Dick"; "Harry")

ShowArray($names)

As you can see, the combination of 4D pointer support (mainly for tables and fields) and pass by reference allows you to write highly generic, reusable code within Active4D.

Default Parameters

In 4D, method parameters are numbered. Thus it is easy to pass a variable number of parameters. In Active4D, parameters are named, so there is another technique for passing variable number of named parameters. This technique is called default parameters and is another feature of Active4D borrowed from C++.

To create a default parameter, append the parameter name with `=' and any expression which is valid at the point of method declaration. The expression is evaluated once when the method is first parsed, and the expression's value is stored with the method parameter. Within a default parameter expression, you can reference variables, methods, named constants, etc., as long as they are valid at the point of declaration.

Here is an example:

method "ShowArray"(&$inArray;$inSuffix="<br>")

for ($i;1;size of array($inArray))

writeln($inArray{$i} + $inSuffix)

end for

end method

 

ShowArray($myArray) `Appends "<br>" to each line

ShowArray($myArray;"<p>") `Appends "<p>" to each line

In the first call to ShowArray , the $inSuffix parameter is not passed, so Active4D uses its default value of "<br>". In the second call to ShowArray , the $inSuffix parameter was passed, so the passed value is used.

This is how you would have accomplished the same thing in 4D:

C_POINTER($1)

C_STRING(255;$2)

 

If (Count parameters < 2)

$2:="<br>"

End if

 

`And so on

Returning Values

To return a value from a method, use the return keyword, like this:

method "Concat"($inFirst;$inSecond)

return ($inFirst + $inSecond)

end method

The return keyword can also be used without a value, but if you return a value the expression must be enclosed in parentheses. Whether or not you return a value, the return keyword will immediately exit the method, no matter where in the method it occurs. This allows you to quickly exit a method when a termination condition is reached, much as the break keyword allows you to immediately exit a loop. It turns out this is extremely useful, as it saves you from having to set some kind of "success" flag which you test after the loop.

Here is an example:

method "FindFirstOver"(&inArray;$inCompare)

for ($i;1;size of array($inArray))

if ($inArray{$i}>$inCompare)

return ($i) `The method immediately terminates, returning $i

end if

end for

 

return (-1) `We only get here if the for loop completes without a match

end method

 

 

Libraries

It is common for programmers to continually build up their own set of utility methods which they can call upon when needed. In 4D, these methods can be grouped together either by a name prefix or by placing them in a component.

In Active4D, you can group methods together into a named logical unit called a library . The library in effect serves as both the component and the name prefix.

Library Definition

Libraries are special files that can only contain the following entities:

  • import keyword
  • define keyword
  • library keyword
  • end library keyword
  • Method definitions
  • Comments

Any methods within a library file must be enclosed by a library/end library pair. The library keyword must be followed by a literal string which gives the name of the library. The library name must match the name of the library's definition file, minus the file extension. Libraries may not be nested.

  • The names "Active4D" and "global" are reserved and may not be used as library names.

Here is an example library file called "mylib.a4l":

library "mylib"

define(kMyConstant; 7)

 

method "MyMethod"($inMyParam)

writebr($inMyParam)

end method

 

method "MyConcat"($inText1; $inText2)

return ($inText1 + $inText2)

end method

end library

Importing Libraries

To use the methods in a library, you must first import it with the import command.

  1. The import command takes a library name as its argument -- not a path, but simply the library name with no filename extension. The import command does the following:
  2. Checks to see if the named library is already loaded into memory. If it has been nothing more is done.
  3. If the library is not in memory, the library search path is followed to find a file called <lib>.<ext>, where <lib> is the library name passed to the import command, and <ext> is the currently configured library extension. For information on the library search path, see See Libraries and the Active4D Directory.
  4. If the library file is found, it is parsed. Active4D strips out all comments between method definitions, storing only the body of each method along with information about the method parameters. In addition, if any constants are defined in the library, they are stored as well.
  5. If the library is parsed successfully, the library name is added to a list of imported libraries.
  6. If any of the above steps fails, an error is generated and execution is aborted.

If you read the above steps closely, you will notice the following advantages that libraries have over inline methods:

  • Libraries are only loaded and parsed once during the life of the server (unless you modify them). Inline methods are loaded and parsed every time their page is executed.
  • Comments within an inline method's page must be parsed every time. Comments outside of the method body in a library are not stored. Thus you may liberally comment library methods with no performance penalty.
  • The methods in a library are scoped to the library's name, whereas inline methods are global in scope. See the next section for more on this.
Load Errors

When parsing a library, if Active4D encounters a error in the syntax of the methods definitions, it displays an error message with the exact location and type of syntax error.

The "lib dirs" Config Option

Active4D follows a standard search path when attempting to locate a library. If you wish to put your libraries in a directory other than one in the standard search path, you may add the directory to the "lib dirs" path list in Active4D.ini .

By default the "lib dirs" path list is empty.

The "lib extension" Config Option

By default, when importing a library Active4D appends ".a4l" to the library name and searches for a file with that name. If you wish to use a different extension for libraries, you may change it by setting the "lib extension" option in Active4D.ini . The extension must be a dot followed by no more than five alphanumeric characters.

Library Namespace

Methods and constants defined in a library are stored in memory under the library's name. The library name then becomes a namespace that encapsulates all of the methods defined within it. This allows you to define method names without worrying whether another developer has chosen to use the same method name. As long as they are in libraries with different names, you can access each method separately.

Name Resolution

When Active4D encounters a non-4D method name, it first searches all of the imported libraries for a method with that name. If there is more than one match, an error is generated and execution is aborted. If there is one and only one match that method is executed.

If two or more libraries are imported that have a method with the same name, you must disambiguate the method name by prepending the library name and a dot before the method name. Otherwise Active4D will stop and tell you that there is an ambiguous reference to a local method.

For example, suppose you have these two libraries:

library "foo"

method "DoSomething"

return ("Hello from foo")

end method

end library

 

library "bar"

method "DoSomething"

return ("Hello from bar")

end method

end library

To call the two "DoSomething" methods, you must do the following:

writebr(foo.DoSomething) `Output is "Hello from foo"

writebr(bar.DoSomething) `Output is "Hello from bar"

Library scope

Methods and constants defined within a library may be referenced anywhere within that library without using the library.entity syntax. In fact, all entity names within a library take precedence over names external to the library.

This rule leads to the following type of problem:

method "ShowArray"(&$inArray)

`Do something here

end method

 

`Imagine the following is within a separate library file

library "MyUtils"

 

method "ShowArray"(&$inArray)

`Do something here

end method

 

method "ShowArrayAndSomethingElse"(&$inArray, $inSomethingElse)

ShowArray($inArray) `This will call MyUtils.ShowArray()

write($inSomethingElse)

end method

end library

In this example, the library method ShowArrayAndSomethingElse calls the method ShowArray . Because ShowArray is defined within the library, that version of the method takes precedence over the inline ShowArray method defined outside the library.

The "global" library

In the example above, what if you wanted to reference the inline ShowArray method within the library? Fortunately there is a way.

All methods that are defined inline, i.e. in global scope, are placed in a special library called "global" which is implicitly imported. The global library exists only as long as the current execution of the interpreter, and cannot be flushed programmatically. Any attempt to import a library with the name "global" will result in an error, as that name is reserved.

To reference a global method or constant within a library, you can (and probably should) use the form global.method or global.constant . This will ensure that the global version of the entity will be referenced.

Going back to the example above, to ensure the method ShowArrayAndSomethingElse calls the global method ShowArray , you would write it like this:

method "ShowArrayAndSomethingElse"(&$inArray, $inSomethingElse)

global.ShowArray($inArray) `This calls the inline ShowArray()

write($inSomethingElse)

end method

Refreshing Libraries

Because libraries are so useful, you will want to use -- and thus modify them -- all of the time.

Active4D checks every library at a regular interval to see if it has been modified. Any library that has been modified is flushed from memory and then imported again. This relieves you from having to manually flush and reload your libraries.

  • Because Active4D caches the path of imported libraries, if you move a library and then modify it, it will not be refreshed because Active4D will not be able to find it.
The "auto refresh libs" Config Option

Once your web site is in production and the code is stable, you may wish to turn off auto-refresh. Possible reasons for doing this include:

  • Preventing any accidental and unwanted changes being loaded.
  • Eliminating the small performance hit incurred by checking the libraries for modification.
  • While libraries are being loaded, no scripts are allowed to execute.

You control library auto-refresh with the "auto refresh" option in Active4D.ini . If this option is set to "true" or "on", the auto refresh mechanism is active. If this option is set to "false" or "off", the only way to refresh a library is to manually use the A4D FLUSH LIBRARY command from 4D or to quit and restart the server.

  • Because libraries are auto-refreshing, the flush library command has been deprecated.
The "refresh interval" Config Option

If auto-refresh is on, you may configure the amount of time Active4D waits between checks for modified libraries. You control this interval with the "refresh interval" option in Active4D.ini . The interval is specified in seconds and must be in the range 5 to 60 inclusive.

Differences from Active4D 1.0

In Active4D 1.0, there were two ways of loading a library: import and load library . Many developers used load library because:

  • It took a full path, allowing libraries to be placed in any directory, whereas the import command would only look in the "Active4D Libs" directory
  • It allowed the loading of libraries with an extension other than ".a4l", which was assumed by the import command.

In Active4D 2, all libraries must be loaded with the (vastly enhanced) import command. The load library command has been deprecated. The changes to the way libraries work can be summarized as follows:

  • import takes as its argument an unadorned library name (no extension or path). In Active4D 1.0, the name passed to import had to be a literal string. In v2 it can be any text expression.
  • Ordinarily libraries must be in an Active4D directory (see See Libraries and the Active4D Directory). If you previously used the load library command to load libraries in a different directory, you can still use import with this directory by adding the directory to the "lib dirs" search path in Active4D.ini .
  • If you used the load library command to allow libraries with an extension other than ".a4l", you may still use that extension by changing the "lib extension" option is Active4D.ini .
  • Because Active4D locates a library by name, the library filename must match the library name as specified by the library keyword.
  • You may only define one library per library file.
  • It is recommended that you prefix your library names with some kind of company and/or project identifier. This will help to avoid any name conflicts should you use third-party libraries in the future.

 

Event Handlers

In the course of executing a script, there are well-defined points at which a developer would like have some control.

Active4D recognizes special event handler methods which are executed before and after various "events." To be activated, these event handler methods must be defined in a special library called "Active4D.<lib>", which must reside in an Active4D directory. The "<lib>" extension must be whatever you have configured the library extension to be in the Active4D.ini file. By default the library extension is ".a4l".

As with the config files, you may have multiple copies of the Active4D library in different directories in the search path. An Active4D library at the beginning of the search path will override one later in the search path.

Modifying the Active4D Library
  1. Active4D periodically checks to see if the Active4D library has been modified. If so, Active4D does the following:
  2. Waits until all requests have been processed
  3. Executes the On Session End method (if defined) for each session that is still alive
  4. Purges all sessions from memory if memory caching is active, deletes all sessions from the database if database caching is active
  5. Executes the On Application End method if defined
  6. Flushes all libraries
  7. Resets the configuration options to their defaults
  8. Reloads the config files
  9. Reloads the Active4D library
  10. Executes the On Application Start method if defined

In other words, it restarts the server.

Event Handler Methods

The event handler methods are outlined below. In addition to these, you may define and call other methods within the Active4D library just as you would with any library.

  • You need not define an event handler if you are not going to use it.

Each handler is executed within a certain context which determines what data is accessible within the handler. For example, handlers that execute within the context of an HTTP request have access to all HTTP request and response collections which Active4D creates.

On Application Start

This handler is executed when 4D first starts up or after the Active4D library has been modified. This handler is analogous to the On Startup database method in 4D.

The only Active4D collection you have access to in this handler is the globals collection.

On Application End

This handler is executed just before the server shuts down, either when 4D is shutdown or when the Active4D library is modified. This handler is analogous to the On Exit database method in 4D.

The only Active4D collection you have access to in this handler is the globals collection.

On Request

This handler is executed just before the Active4D HTTP server handles a request. The handler is passed the path and query string portions of the URL.

If you wish to change the URL, you may do so by returning a non-empty string. Note that what you return must include a query string if the request should have one.

You may also refuse the request altogether by calling set response status with a status of something other than 200 (OK), such as 404 (Not Found). If you set the response status to something other than 200, you do not need to return a result.

Here is what an On Request handler might look like:

`We want to change "/products/13" into "/products.a4d?id=13"

 

method "On Request"($inURL; $inQuery)

if (extension of($inURL) = "")

$pos := position("/"; $inURL; *) `reverse search

 

if ($pos > 0)

$url := substring($inURL; 1; $pos - 1) + ".a4d"

$url += "?id=" + substring($inURL; $pos + 1)

return ($url)

end if

end if

end method

The request info and request cookies collections are accessible within this handler. This allows you to check things like the host, etc.

On Authenticate

When Active4D determines that the current request is in a protected realm, if this event handler is defined it is invoked before the On Session Start and On Execute Start event handlers.

Here is what a sample On Authenticate handler might look like:

method "On Authenticate"

if (auth user = "")

authenticate

else

query([security];[security]realm = current realm;*)

query([security];&;[security]username = auth user;*)

query([security];&;[security]password = auth password)

 

case of

: (records in selection([security]) = 0)

authenticate

: (not(identical strings([security]password; auth password)))

authenticate

end case

end if

end method

In this example we are using a table that defines all of the users and passwords for each realm. If we find a match, we check the passwords to make sure the capitalization is exactly the same. If there is no match, we authenticate again.

Be sure to pass along an authentication failure message with the authenticate command, either by writing directly to the response buffer, by including another file, or by creating a file called "401.html" in the root directory, which will automatically be served when the 401 (Unauthorized) status is returned.

Within this handler you may access all of the Active4D collections.

On Session Start

This handler is executed when a session is created within the context of an HTTP request, before the On Execute Start handler. Typically you would use this handler to initialize some items in the session.

For example, here is an On Session Start handler that initializes three items in a session:

method "On Session Start"

set session("start"; 1)

set session("recsPerPage"; 10)

set session("sortAscending"; true)

end method

Within the first page the user requests, these three session items will be set. For more information on sessions, see See Selecting Records.

You can use the requested url command to determine which part of your web site was accessed, thus determining how to initialize the session. You may also use the redirect command to force all new sessions to go to a login page, for example.

  • For the redirect trick to work, if you are using session cookies the user must have cookies on, otherwise you will end up in an endless loop. To prevent this, in the redirect page you must check for the presence of the session cookie, and if it is not there you must then redirect to a static HTML page (not an executable page!) which tells the user they must turn cookies on.

Within this handler you may access all of the Active4D collections.

On Session End

This handler is executed when a session goes to heaven (if it has been good), either because it timed out, it was expired with the abandon session command, the Active4D library has been modified, or because the server is shutting down.

Typically you would use this handler to clean up data that was stored during the course of the user's session. For example, if the user uploaded a file and you stored the path to the file in the session, when the session times out you would use this handler to delete the file.

Because this handler is executed at idle time, the only Active4D collection you have access to is the globals collection, as there is no request context. However, the about-to-be-purged session is made current and you can access all of its data one last time before kissing it goodbye.

There are a couple of important points to note in regards to this handler:

  • If you wish to identify a session persistently, always use session internal id , as the result of session id is undefined in this handler.
  • There is no deterministic way of knowing when this handler will run. The only thing you know for sure is that it will run sometime after a session expires.
  • If multiple sessions have expired when a session purge cycle begins, this handler will be run once for each session. However, there is no deterministic way of knowing the order in which the sessions will be handled.
On Execute Start

This handler is executed before Active4D begins execution. Typical uses for this handler would include dumping some debugging information, or perhaps executing code that must be executed at the top of every page, such as a check for a timed out session.

Because this handler is executed before the requested file is parsed, any HTML you write to the response buffer will appear before the opening <html> tag. It is possible that some browsers may not like this, although both Internet Explorer and Netscape seem to handle it without problems.

Within this handler you may access all of the Active4D collections.

On Execute End

This handler is executed after Active4D completes execution, unless the redirect command was called.

Within this handler you may access all of the Active4D collections.

 

 

Command Reference

Active4D implements over 350 commands that provide unparalleled power and simplicity to web site programming.

4D Commands

Active4D implements over 150 4D commands which cover the majority of tasks one might perform within the context of a web site. Of these, about 70% are implemented by Active4D directly, 15% are implemented using the 4D plugin API, and the remaining 15% had to be implemented using the equivalent of EXECUTE . The result is that the vast majority of the commands you use will run at full compiled speed.

Following is a list of the 4D commands implemented by Active4D. Unless indicated by the formats noted below, they take the same parameters and work exactly as they do in 4D.

  • Commands in bold have been enhanced by Active4D.
  • Commands in italics have a limitation relative to the 4D version.
  • Commands followed by an asterisk (*) are implemented using the equivalent of EXECUTE .
    • Whatever you do, spend the time to learn Active4D's enhancements to 4D's commands and its own commands. There are many "goodies" that will significantly enhance your productivity.
4D 6.5 Commands

You may notice that some commands which are not implemented in 4D 6.5 are implemented in Active4D. In addition, any commands which were extended in 4D 6.7, such as GET FIELD PROPERTIES , are implemented in their extended form in Active4D.

Using a Default Table

Simply put, you can't use a default table in Active4D. All commands that may take a table in 4D must be given one in Active4D.

4D Command List

 

 

Abs

Add to date

ADD TO SET

ALL RECORDS*

Append document

ARRAY BOOLEAN

ARRAY DATE

ARRAY INTEGER

ARRAY LONGINT

ARRAY PICTURE

ARRAY POINTER

ARRAY REAL

ARRAY STRING

ARRAY TEXT

Ascii

AUTOMATIC RELATIONS*

Before selection

BLOB size

BLOB TO DOCUMENT

BLOB to text

C_BLOB

C_BOOLEAN

C_DATE

C_LONGINT

C_PICTURE

C_POINTER

C_REAL

C_STRING

C_TEXT

C_TIME

CANCEL TRANSACTION*

Char

CLEAR NAMED SELECTION

CLEAR SEMAPHORE*

CLEAR SET

CLOSE DOCUMENT

COPY ARRAY

COPY DOCUMENT

COPY NAMED SELECTION

COPY SET*

Count fields

Count tables

Create document

CREATE EMPTY SET

CREATE FOLDER

CREATE RECORD

CREATE SET

Current date

Current method name

Current process

Current time

 

CUT NAMED SELECTION

Date

Day number

Day of

Dec

DELAY PROCESS

DELETE DOCUMENT

DELETE ELEMENT

DELETE FOLDER

DELETE RECORD*

DELETE SELECTION

Delete string

DIFFERENCE*

DOCUMENT LIST

DOCUMENT TO BLOB

End selection

EXECUTE

False

Field

Field name

Find in array

FIRST RECORD

FOLDER LIST

Get document position

GET FIELD PROPERTIES

Get indexed string

GET PICTURE FROM LIBRARY

Get pointer

GOTO RECORD

GOTO SELECTED RECORD

INSERT ELEMENT

Int

INTERSECTION*

Is in set*

LAST RECORD

Length

LOAD RECORD*

Locked

Lowercase

Mac to ISO

Mac to Win

Milliseconds

Month of

MOVE DOCUMENT

NEXT RECORD

Nil

Not

Num

ONE RECORD SELECT*

Open document

ORDER BY

 

ORDER BY FORMULA*

PICTURE PROPERTIES

Picture size

Position

PREVIOUS RECORD

QUERY

QUERY SELECTION*

Random

READ ONLY

READ PICTURE FILE

READ WRITE

RECEIVE PACKET

Record number

Records in selection

Records in set*

Records in table*

REDUCE SELECTION*

RELATE MANY

RELATE MANY SELECTION

RELATE ONE

RELATE ONE SELECTION

REMOVE FROM SET*

Replace string

RESOLVE POINTER

Round

SAVE RECORD

SCAN INDEX*

Selected record number

SELECTION RANGE TO ARRAY

SELECTION TO ARRAY

Semaphore*

SEND PACKET

Sequence number*

SET DEFAULT CENTURY*

SET DOCUMENT POSITION

SET QUERY DESTINATION*

SET QUERY LIMIT*

Size of array

SORT ARRAY

START TRANSACTION*

String

STRING LIST TO ARRAY

Structure file

Substring

Table

Table name

Test path name

Test semaphore*

Tickcount

Time

Time string

4D Command List (cont.)

 

 

True

Type

Undefined

UNION*

UNLOAD RECORD*

Uppercase

USE NAMED SELECTION

USE SET

VALIDATE TRANSACTION*

Win to Mac

WRITE PICTURE FILE

Year of

 

 

 

Active4D Commands

Active4D implements over 200 new commands. Most are focused towards web programming. Some are additions to the 4D language that we have always wanted. The bottom line is this: if you learn these new commands, you will be far more productive. So please take the time to learn them!

 

 

Active4D Command List

 

 

abandon response cookie

abandon session

add element

add to timestamp

append to array

auth password

auth type

auth user

authenticate

auto relate

base64 decode

base64 encode

blob to collection

blob to session

buffer size (deprecated)

call 4d method

call method

capitalize

cell

choose

clear array

clear buffer (deprecated)

clear collection

clear response buffer

collection

collection has

collection to blob

compare strings

concat

copy collection

copy upload

count collection items

count form variables

count globals

count query params

count request cookies

count request infos

count response cookies

count response headers

count session items

count uploads

current file

current line number

current path

current platform

current realm

day of year

default directory

define

defined

 

delete collection item

delete global

delete response cookie

delete response headerdelete session item

directory of

directory separator

dump locals

enclose

end save output

extension of

filename of

first not of

first of

form variables

form variables has

get cache control

get collection

get collection array

get collection array size

get collection item

get collection keys

get content charset

get content type

get current script timeout

get error page

get expires

get expires date

get field numbers

get field pointer

get form variable

get form variable choices

get form variable count

get form variables

get global

get global array

get global array size

get global item

get global keys

get item array

get item key

get item type

get item value

get license info

get local

get log level

get output charset

get output encoding

get platform charset

get query param

 

 

get query param choices

get query param count

get query params

get request cookie

get request cookies

get request info

get request infos

get request value

get response buffer

get response cookie

get response cookie domain

get response cookie expires

get response cookie path

get response cookies

get response header

get response headers

get root

get script timeout

get session

get session array

get session array size

get session item

get session names

get session stats

get session timeout

get time remaining

get timestamp datetime

get upload content type

get upload encoding

get upload extension

get upload remote filename

get upload size

get utc delta

get version

global

globals

globals has

hide session field

identical strings

import

in error

include

include into

is a collection

is an iterator

is array

join array

last not of

last of

local datetime to utc

 

local time to utc

local variables

lock globals

log message

longint to time

mac to html

max of

merge collections

min of

more items

multisort arrays

multisort named arrays

native to url path

new collection

new global collection

new local collection

next item

param text

parameter mode

query params

query params has

random between

redirect

request cookies

request info

requested url

require

resize array

response buffer size

response cookies

response headers

save upload to field

session

session has

session id

session internal id

 

session local

session query

session to blob

set array

set cache control

set collection

set collection array

set content charset

set content type

set current script timeout

set error page

set expires

set expires date

set global

set global array

set local

set log level

set output charset

set output encoding

set platform charset

save output

set response cookie

set response cookie domain

set response cookie expires

set response cookie path

set response header

set response status

set script timeout

set session

set session array

set session timeout

split string

throw

time to longint

timestamp

timestamp date

 

timestamp day

timestamp hour

timestamp millisecond

timestamp minute

timestamp month

timestamp second

timestamp string

timestamp time

timestamp year

trim

trim left

trim right

trunc

type descriptor

unlock globals

url decode

url decode path

url decode query

url encode

url encode path

url encode query

url to native path

utc to local datetime

utc to local time

variable name

week of year

write

write blob

write gif

write jpeg

write jpg

write raw

write to console

writebr

writeln

writep

Command Syntax

The commands in this chapter are listed with the same basic format that the 4D documentation uses, with a few small differences:

  • Commands unique to Active4D are all lowercase to distinguish them from 4D commands which have been implemented by Active4D.
  • Parameters have a prefix to indicate what happens to them within the body of the method. The prefix "in" means the value of the parameter is read but not written. The prefix "io" means the value is both read and written. The prefix "out" means the value is written, replacing any existing value.

Arrays

Active4D adds several commands which make working with arrays much easier. It will pay many times over for you to learn and use these commands.

In addition, there are some important issues to understand in the implementation of SELECTION TO ARRAY and SELECTION RANGE TO ARRAY .

 
add element
version 1

 

add element(ioArray {; inHowMany})

 

 

Parameter

Type

 

Description

 

ioArray

Array

!

The array to which you want to append elements

 

inHowMany

Number

!

How many elements to append

Discussion

This command appends one or more elements to ioArray . If inHowMany is omitted, one element is appended to the end of ioArray . The element appended to the array is initialized to the default value for the array's type. The add element command is essentially shorthand for the following standard 4D statement:

INSERT ELEMENT ($ioArray; Size of array ($ioArray) + 1; $inHowMany)

 
append to array
version 1 (modified v2)

 

append to array(ioArray; inValue {; inValueN})

 

 

Parameter

Type

 

Description

 

ioArray

Array

!

The array to which you want to append elements

 

inValue

<any>

!

Elements to append

Discussion

This command appends one or more values to the existing contents of ioArray . If a value is not assignment compatible with ioArray , an error is generated and execution is aborted.

This command is my solution to the insanity of appending elements to arrays in 4D:

ARRAY LONGINT ($array;0)

 

`The 4D way

INSERT ELEMENT($array;Size of array($array)+1)

$array{Size of array($array)}:=7

 

`The Active4D way

append to array($array; 7)

The real power of this command comes when you want to append multiple values at once, like this:

append to array($array; "one"; "two"; "three")

 
clear array
version 2

 

clear array(ioArray {; ...ioArrayN})

 

 

Parameter

Type

 

Description

 

ioArray

Array

!

Array to clear

Discussion

This command resizes one or more arrays to zero elements.

 
is array
version 2

 

is array(inType) ! Boolean

 

 

Parameter

Type

 

Description

 

inType

Longint

!

The type to test

 

Function result

Boolean

True if given type is an array type

Discussion

This command returns true if the given variable type is an array type. It is shorthand for the following test:

$isArray := ((Type($var) >= Array 2D) & (Type($var) <= Boolean array))

 

`Active4D way

$isArray := is array(type($var))

 
join array
version 2

 

join array(inArray; inSeparator {; inStart {; inPrefixNum {; inQuoteText}}}) ! Text

 

 

Parameter

Type

 

Description

 

inArray

Array

!

The array to join

 

inSeparator

Text

!

The text to insert between elements

 

inStart

Number

!

The element to begin joining from

 

inPrefixNum

Boolean

!

True to prefix the element number

 

inQuoteText

Boolean

!

True to quote-enclose elements of Text or String arrays

 

Function result

Text

Concatenation of array elements

Discussion

This command joins the elements of inArray together into a single string. Non-textual array elements are automatically converted to text.

If inStart is not specified, it defaults to 1.

If inPrefixNum is not specified, it defaults to false . If it is specified and true , each element is prefix by "{#} ", where # is the element number.

If inQuoteText is not specified, it defaults to false . If it is specified and true , elements of text or string arrays are surrounded by double quotes.

Here are some examples:

array longint($longs;0)

set array($longs; 7; 13; 27)

writebr(join array($longs; ", "))

 

array text($nums;0)

set array($nums; "one"; "two"; "three")

writebr(join array($nums; "<br>"; 1; true; true))

 

`Here is the output in the browser

7, 13, 27

{1} "one"

{2} "two"

{3} "three"

This command is especially useful for writing the contents of an array to the Active4D debugging console. Use this form:

write to console(join array($array; "\r"; 1; true; true))

 
multisort arrays
version 3.0

 

multisort arrays(inArray1; inDirection1 {; ...inArrayN; inDirectionN})

 

 

Parameter

Type

 

Description

 

inArray

Array

!

The array to sort

 

inDirection

<>= or Text

!

The sort direction

Discussion

This command performs a multilevel sort on the elements of inArray1 through inArrayN . The direction of the sort for each array is specified by the the inDirection argument following the array:

Character

Direction

>

Ascending

<

Descending

=

Don't care, follow array to left

You may sort any array type except for picture arrays and pointer arrays. The arrays may be local, process, or interprocess variables.

The sort direction may be one of the operators `>', `<` and `=', or may be any text expression which resolves to one of those characters. This allows you to programmatically set the direction of the sort.

 
multisort named arrays
version 3.0

 

multisort named arrays(inArrayName1; inDirection1

{; ...inArrayNameN; inDirectionN})

 

 

Parameter

Type

 

Description

 

inArrayName

Text

!

The name of an array to sort

 

inDirection

<>= or Text

!

The sort direction

Discussion

This command is identical to multisort arrays , except that instead of passing direct array references, you pass text expressions which resolve to the names of arrays. This allows you to programmatically determine both the order and direction of the sort.

The arrays names should begin with `$' to indicate a local array, `<>' to indicate an interprocess array, and no prefix to indicate a process array.

 
resize array
version 3.0

 

resize array(ioArray; inSize)

 

 

Parameter

Type

 

Description

 

ioArray

Array

$

The name of an array to sort

 

inSize

Number

!

The new size of the array

Discussion

This command resizes ioArray to the given size. If inSize is less than zero, the array will be resized to zero.

 

SELECTION/SELECTION RANGE TO ARRAY
(modified 4D) version 2

 

SELECTION TO ARRAY

SELECTION RANGE TO ARRAY

Discussion

These commands have the same parameters as their counterparts in 4D, but there are important limitations in the Active4D implementation.

Running under 4D Standalone or 4D Server, the performance of Active4D's implementation will in most cases be more or less the same as 4D's. But if Active4D is running on 4D Client, the performance of Active4D's implementation will always be worse than 4D's, and in some cases dramatically worse.

The reason for this difference is that there is no built-in support for these commands, so Active4D has to implement them itself. To do so requires loading each record in the selection and extracting the requested fields. If there are related fields being loaded, each record in those tables must be loaded as well. If Active4D is running on the same machine as the database -- i.e. under 4D Standalone or 4D Server -- this all happens very quickly and there is no performance hit.

When these commands are executed from 4D Client, the data is gathered into arrays on the server and then the arrays are sent to 4D Client. On the other hand, if Active4D is running on Client, each record in the selection and each related record must be sent to the Client over the network in its entirety. If the records contain a lot of data which is not going into the target arrays, there will be a large performance hit. This performance hit gets worse as the number and size of related records increases.

Optimizing

To lessen the effect of the performance hit, the Active4D 3.0 implementation of these commands does not load related one records by default. This is in contrast to the version 2.0.x implementation which did load related one records by default. But the new implementation does follow the current "auto relate one" setting as configured in Active4D.ini or as set by the auto relate command. For more information on auto relating, see See Selecting Records.

If your SELECTION/SELECTION RANGE TO ARRAY command does not use fields from related tables, the new behavior will improve performance under Client if auto relate one is off. Thus in these cases you will want to ensure auto relate one is off when executing SELECTION/SELECTION RANGE TO ARRAY.

You can do so with the auto relate command, like this:

auto relate(false)

SELECTION TO ARRAY([ingredients]id; $ids; [ingredients]name; $names)

Of course if you are relying on auto relate one being on in other code, you would have to add this line after the SELECTION TO ARRAY :

auto relate(true)

If, on the other hand, you are loading related data in SELECTION/SELECTION RANGE TO ARRAY , you will need to ensure that auto relate one is on, like this:

auto relate(true)

SELECTION TO ARRAY([ingredients]id; $ids; [vendors]name; $vendors)

Since this will automatically load all automatically related one records, you must be careful to use automatic relations only when absolutely necessary.

 

set array
version 2

 

set array(ioArray; inValue {; inValueN})

 

 

Parameter

Type

 

Description

 

ioArray

Array

!

The array which you want to set to the given elements

 

inValue

<any>

!

Elements to set

Discussion

This command replaces the existing contents of ioArray with one or more values. If a value is not assignment compatible with ioArray , an error is generated and execution is aborted.

This command is the fastest way to initialize an array to a known set of values. For example:

array longint($primes;0)

set array($primes; 2; 3; 5; 7; 11; 13)

Collections

The collection commands allow you to create, manipulate, examine and destroy your own local (temporary) or global (persistent) collections within your scripts.

For more information on collections, see See Collections.

  • Beginning with version 3.0, many of the collection commands are no longer necessary as they have been replaced with the extended indexing syntax, which is much easier to use. For more information, see See Referencing Collection Values.
 
collection
version 2

 

collection(inHandle) ! Longint

 

 

Parameter

Type

 

Description

 

inHandle

Longint

!

Collection handle

 

Function result

Longint

Iterator reference

Discussion

Given a collection, this command returns an iterator to the first item in the collection.

For more information on iterators, see See Iterators.

 
new collection
version 2
modified version 3.0

 

new collection{(* {; inKey; inValue {; inKeyN; inValueN}})} ! Longint

 

 

Parameter

Type

 

Description

 

*

*

!

Pass to create a global collection

 

iinKey

Text

!

Item key

 

inValue

<any>

!

Item value

 

Function result

Longint

Collection handle

Discussion

This command creates a new local or global collection and returns a handle to the collection. You then use this handle with the other collection commands.

If no * is passed, this command is exactly equivalent to new local collection . If * is passed, this command is exactly equivalent to new global collection .

You may also initialize the collection with key/value pairs by passing pairs of parameters. If an array is passed as the value, it is stored in its entirety in the item.

For example, this code would create a local collection and initialize it with two items:

$person := new collection("name"; [People]Name; "age"; [People]Age)

 
new local collection
version 2

 

new local collection{(inKey; inValue {; inKeyN; inValueN})} ! Longint