This document describes the current implementation of Infrascript. This is
a powerful scripting language that can be used for writing mail filters,
CGI programs and stand-alone applications.

The syntax is designed to be simple, easy to remember and unambiguous to
parse. New and amazing features will be added, though it will never grow
too big. There is no theme or big design idea behind the language, pragmatism
definitely rules. Where possible different data sources are mapped into
the same interface for consistency.


RULE  SYNTAX
------------

A rule may span multiple-lines of input, use '\' to indicate continuation...

	do {ACTION-LIST};

	set VAR [=|+=|-=|*=|/=|%=] WORD;

	select SOURCE WORD;

	foreach ITEM
		[FUNCTION|break];
	next

	if CONDITIONAL then
		FUNCTION;
	elseif CONDITIONAL then
		FUNCTION;
	else
		FUNCTION;
	endif

	include filename;
	
Where...

	SOURCE := [PROT|MBOX|file|file(char)|ldap|#odbc]
	MBOX := [mbox|mbox(userid)]
	PROT := [pop|pops|imap|imaps](userid[:passwd][@hostname])
	ITEM := [loop|field|attribute|var|attachment|split|row|result|MESSAGE]
	MESSAGE := [message|message(brief)|message(raw)]
	CONDITIONAL := [EXPRESSION1|EXPRESSION2|...EXPRESSIONn]
	EXPRESSION := PART contains {WORD-LIST}
	EXPRESSION := PART begins {WORD-LIST}
	EXPRESSION := WORD [is|not] empty
	EXPRESSION := size [<|less|>|over] [n[K|M|G]|VAR]
	EXPRESSION := age [<|less|>|over] [n[M|H|D]|VAR]
	EXPRESSION := [ADDRESS|WORD] verifies
	EXPRESSION := [ADDRESS|WORD] in(LISTNAME)
	EXPRESSION := [true|false]
	EXPRESSION := WORD [<|<=|=|<>|>=|>] WORD

	PART := [WORD|HEADER|BODY|#SERVER|#REQUEST|#RESPONSE]
	HEADER := header.[content-type|return-path|subject|date|ADDRESS]
	BODY := body.[mimetype|size|text|ATTACHMENT]
	SERVER := server.[timeout]
	REQUEST := request.[field("name")|cookie("name")]
	RESPONSE := response.[expires|cookie("name")]
	ADDRESS :- [from|replyto|to|cc]
	ATTACHMENT := attachment.[mime|name|size|data]
	WORD-LIST := WORD1[,WORD2[,...WORDn]]
	WORD := [VAR|string]
	VAR := $CHARS
	CHARS := CHAR[CHAR...CHAR]
	CHAR := [a-z|A-Z|_|0-9|SPECIAL]
	SPECIAL := [\r|\n|\t]

	ACTION-LIST := FUNCTION[;FUNCTION[;...FUNCTION]]
	RECIPIENT := [mailto:address|news:newsgroup|file:filename|tolist:filename]
	FILENAME := WORD
	LISTNAME := [file:]WORD
	PATH := WORD
	FOLDER := WORD


Where 'string' cannot contain tabs, spaces or commas unless enclosed in double
quotes. Also, 'line' is a synonym for 'row'. NOTE: all string comparisons are
case-insensitive!



And...

	K = 1024 bytes
	M = 1024 * K
	G = 1024 * M

	M = 1 minute
	H = 1 hour
	D = 1 day


All age compararisons are done in GMT/UTC and may not be the most reliable
considering how some client and/or servers are set up.


VARIABLES
---------


An ADDRESS is of the form  "$U <$A@$D>" and the built-in variables...

	$U = username
	$A = account
	$D = domain
	$E = $A@$D

are initialized whenever an ADDRESS is encountered (set to the variable $_) or
when the variable $_ is explicitly set (eg. set $_ = $f;).


Other built-in variables...

	$_TIME = seconds of current epoch (ie. C-time)
	$_SEC = current second
	$_MIN = current minute
	$_HOUR = current hour
	$_YDAY = days since jJanuary 1 (ie. 0 - 365)
	$_WDAY = days since Sunday (ie. 0 - 6)
	$_MDAY = day of the month (ie. 1 - 31)
	$_MON = months since January (ie. 0 - 12)
	$_YEAR = year since 1900 (eg. 1999 => 99)
	$_ISDST = is dayligh saving in effect
	
	$_NOW = current local time (formatted)
	$_GMT = current GMT/UTC time (formatted)
	$_UTC = current GMT/UTC time (formatted)

	$_ERRNO = I/O error code
	
	$_LDAP_EMAIL_ATTRIB = email-address attribute name (default is 'mail').
	
	$i = remote IP address
	$n = remote name

	$R = Return-Path address 
	$a = account-name delivered to or logged-in as
	$x = domain-name delivered to
	$p = PART contents
	$f = From address
	$r = Reply-To address
	$t = To address
	$c = Cc address
	$d = Date line
	$s = Subject line
	$b = body (all of it, lines are CRLF delimited)
	$y = content-type line
	$h = headers (all of them, CRLF delimited)

	$? = attachment name
	$^ = attachment CID
	$+ = attachment size (bytes, eg. 12345678)
	$- = attachment size (formated, eg. '137 bytes' or '997 Kb' or '5 Mb')
	$~ = attachment MIME type (e.g. 'text/plain')
	$@ = attachment charset (e.g. 'us-ascii')
	$= = attachment contents


With the special array variables...

	$h("name")   = the named header value (mail/CGI mode)
	$f("name")   = the named field (FORM or URL) value (CGI mode only)
	$a("name")   = the named LDAP attribute value

that can only be used inside a SET command (either LHS or RHS). These special
array variables can be iterated over by the...

	foreach header
	foreach field
	foreach attribute

loops, respectively.

Note: a field variable on a FORM can refer to an uploaded file if the input
type of the field was 'file' and the encoding on the FORM was set properly
to 'enctype="multipart/form-data"'. This is known as HTTP file upload. But
note: in this case the contents of the field is the filename it has been
saved locally as. You should delete the local file then after use.

And the special loop variables...

	$%   = the result of a 'foreach...' command
	$#   = loop count (after foreach/next command starting at 1)
	$#   = number of splits (after split() function call)



NOTE: to avoid confusion, programmers should avoid creating single character
variable names or ones that begin with an underscore '_' character.


The special syntax '$@name' means return the value of '$n' when '$name'
evaluates to the number 'n'. Which is useful for getting at a particuar split
variable which is to be programmatically determined. This construct can only
be used inside a set command.





FUNCTION SUMMARY
----------------

file(filename)               -  ⥫ 쬠/  㪠 䠩
                               (ᮤন 㤥 १ᠭ).
append(filename)             -  ⥫ 쬠/  㪠 䠩
                               (।饥 ᮤন ࠭).
exec(cmd)                    - execute specified command feeding in message
                               body/attachment. (RESTRICTED)
pipe(cmd)                    - pipe message body thru command (RESTRICTED).
forward(recipient)           - ᫠  쬠 㪠 .
addto(LISTNAME)              - add from address to list.
delfrom(LISTNAME)            - delete from address from list.
attach(filename)             - attach file to subsequent response.
reply(body)                  - send a message (specify body) in response.
dirlist(VAR,path)            - 㧨 ᯨ᮪ 䠩 ⠫  ६.
move(folder)                 - move message to named IMAP folder.
copy(folder)                 - copy message to named IMAP folder.
create(folder)               - create named IMAP folder.
delete(folder)               - delete named IMAP folder.
uid(VAR)                     -  UID ( ID ) 쬠.
flags(VAR)                   - get the IMAP flags of the message.
discard                      - 㤠 쬮/.
keep                         - does nothing really.
echo                         - ⮡ࠧ ⥫ 쬠/   (same
                               as print($b) or print($=) commands).
raw                          - entire message (including headers) is read
                               into '$b' variable.

#verify                      - verify signature.
#sign                        - sign message.
#encrypt                     - encrypt message.

noop                         -  樨 (i.e. do nothing).
break                        - break out of a 'for' loop.
			     - (ࢠ ᯮ 横 'for'    
			       ।)
skip                         - no further rule processing occurs.
exit(status)		     -   㪠  
			       (ᯮ ifscript.exe).

update(WORD-LIST)            - update current row .
insert before(WORD-LIST)     - insert row before current row.
insert after(WORD-LIST)      - insert row after current row.
add(WORD-LIST)               - add row after last row.
delete                       - delete current row.
drop                         - delete all rows.
commit                       - commit changes to file or ODBC source.

flog(string,filename)        - print string in file with timestamp.
log(string)                  - print string in log with timestamp.
print(string)                - print string in log.
println(string)              - print string in log followed by newline.
split(pat,string)            - split string into tokens ($1, $2 etc) delimited
                               by successive char(s) in pattern.
wsplit(pat,string)           - split string into tokens ($1, $2 etc) delimited
                               by string pattern.
wspliti(pat,string)          - split string into tokens ($1, $2 etc) delimited
                               by string pattern while ignoring case.
writebin(VAR)                - write VAR uninterpreted to active network connection or TTY.
write(string)                - writes string to active network connection ot TTY.
read(VAR)                    - reads line from active network connection or TTY
                               into variable.
save(hdrs,body)              - save as current message (in queue or user
                               mailbox).
load(VAR,filename)           - read named file into variable (and set $# with
                               length).
store(string,filename)       - save string to named file (previous contents are
                               overwritten).
remove(filename)             - 㤠 㪠 䠩.
post(recipient)              - send message to recipient (set appropriate vars
                               $f $s $b).
post(mailto:to,cc)           - send message to mail address and Cc: (set
                               appropriate vars $f $s $b).
post(mailto:to,cc,bcc)       - send message to mail address, Cc: (if non-blank)
                               and Bcc: (set appropriate vars $f $s $b).
post(host,to,cc,bcc)         - send message to mail address, Cc: (if non-blank)
                               and Bcc: (set appropriate vars $f $s $b).
send(recipient,hdrs,body)    - send a raw message.
rand(VAR,nbr)                - generate random number in range 1 to 'nbr'
list(VAR,folder)             - load list of IMAP mailboxes according to pattern
                               (eg. list("%"), list("inbox/*")) into variable.
auth()                       - require authentication to run this CGI script.
                               Can only be used with NPH scripts.
sendfile(filename)           - sends file to active network connection (sets
                               the Content-Length header first if CGI).
replace(VAR,string,p1,p2)    - replace in 'string' all occurences of 'p1' with
                               'p2' and set the result in 'VAR'.
getmsgbyuid(uid)             - after a select, read the message specified by
                               UID number.
getmsgbyseq(nbr)             - after a select, read the message specified by
                               sequence number.
urlencode(VAR,string)        - URL encode a string.
htmlencode(VAR,string)       - HTML encode a string.
htmlalt(VAR,string)          - Convert HTML string to plain-text.
althtml(VAR,string)          - Convert plain-text string to HTML.
sort(keylist)                - sort messages after a select.
sortex(keylist,searchlist)   - search & sort messages after a select.
fdate(VAR,days)              - format 'dd-mmm-yyyy' date string for 'days'
                               back (usefull with 'sortex()' function). Note: 0
                               days is today, >0 days is in the past and <0 days
                               is int he future.
login(userid,password)       - login with userid & password.
setpass(userid,password)     - modify a user's password (privileged access only).
passwd(oldpass,newpass)      - modify a user's password (user access).
adduser(userid,passwd)       - add a user account with all services to the
                               account system (privileged access only).
adduser(userid,passwd,owner) - variant allows you spcify owner account.
moduser(userid,srvs)         - modify a user account with specified services to the
                               account system (privileged access only).
setquota(userid,mbytes)      - set total allowed mailbox quota in megabytes.
deluser(userid)              - delete a user account from the account system
                               (privileged access only).
aliases(VAR)                 - get the list of email aliases for the current account.
sleep(nbr)                   - sleep for number of seconds.
httpproxy(host,port)         - set proxy configuration.
httpauth(userid,password)    - set credientials for HTTP login.
clearheaders()               - clear user HTTP headers.
addheader(header)            - define user HTTP header.
geturl(VAR,url)              - fetch URL and retrieve contents.
clearform()                  - clear form fields.
addform(name,value)          - define a form field and value.
posturl(VAR,url)             - post form to URL and retrieve contents.
httplog(VAR)                 - retrieve HTTP session log.
getaddr(VAR,host)            - get the IP address of a host-name.
gethost(VAR,addr)            - get the host-name of an IP address.
tcpconnect(host,port)        - open TCP-stream connection to named host and port
                               and make active network connection.
udpconnect(host,port)        - open UDP-datagram connection to named host and port
                               and make active network connection.
isconnected(VAR)             - set VAR to 0 if not connected, or 1 if connected ok (see also $_ERRNO).
timeout(seconds)	     - set the read timeout on a connection
                               (-1 = infinite, 0 = immediate, default is 300)
disconnect()                 - disconnect active network connection.
echo(string)                 - print string to TTY (stdout).
strlen(VAR,string)           - set VAR to the length of the string.
tcpserver(port)              - serve TCP connection off the named port.
udpserver(port)              - serve UDP connection off the named port.
accept()                     - accept connection request (see XXXServer above).


Note: the moduser 'srvs' param is a string consisting of combinations of basic service
names such as POP+IMAP+WEB+FTP+IM+NEWS, using the plus sign to indicate inclusion in that
service.


Note: Due to simplicity of the parsing mechanism, whenever the first parameter
to a function contains a comma or a right paraenthesis ie. ')' use a variable
instead...

	set $v1 = "a,(b),c";
	f1($v1);
	f2($v1, "blah");

Note: parsing restrictions limit the length of string params. Use variables
instead if they are long (>255 chars).






RECIPIENT DESCRIPTION
---------------------

tolist:filename     - To address-list in file (one address per line).
cclist:filename     - Cc address-list in file (one address per line).
bcclist:filename    - Bcc address-list in file (one address per line).
ranlist:filename    - To random selection from file (one address per line).
mailto:address      - named address (in user@domain format only).
news:newsgroup      - named newsgroup.
file:filename       - named file.
ldap:search         - to named people using 'mail=' attribute.


Note: the recipient string cannot be quoted. That is it MUST be of the form...

	function(mailto:address, ...);

etc. It is probably better to use a form like...

	set $addr = "user@domain";
	function(mailto:$addr, ...);


Note: LDAP recipients should be of the form...

	set $_ATTRIB = "mail";
	set $search = "ldap.yahoo.com, cn=Andrew Davison";
	function(ldap:$search, ...);




NOTES ON LIST
-------------

A list like list("%") will list the top-level folder in the accessing account.
A like like list("*") will list all folders and sub-folders.



NOTES ON SEND
-------------

Due to simplicity of the parsing mechanism, you MUST use intermediate variables
for headers and body...

	set $hdrs = "From: me@here\r\nTo:you@there\r\n\r\nSubject: this is a test\r\n";
	set $body = "Line1\r\nLine2\r\nLine3\r\n";
	send(mailto:you@there, $hdrs, $body);

The headers must contain at least a From: header. Some mail servers may require
a To: and a Date: as well.

Also, do not quote the first paramater (see RECIPIENT below).



NOTES ON SPLIT
--------------

The 'split' function works similarly to the simple PERL 'split' function.
The expression...

	split(":", "a:b:c");

is equivalent to to the following in PERL...

	($1,$2,$3) = split(":", "a:b:c");

and both will set the variable $1 = "a", $2 = "b", $3 = "c".

If the pattern is more than one char in length then the characters are used
successively as delimiters. For example...

	split("<@>", "Mr Yonki <yonki99@yonki-time.com>");

will set $1 = "Mr Yonki", $2 = "yonki99", $3 = "yonki-time.com".

The 'wsplit' (or wide-split) is a multibyte split. Only a single pattern can
be used and all the characters in the pattern are used for comparison. The
'wspliti' variant is insensitive to (upper or lower) case.

NOTE: for split operations all CR+LF pairs are converted to just LF for
comparisons. The reason is to get around differences between Windows/Unix
platforms and allow for consistent treatment.

NOTE: leading and trailing spaces on fields are trimmed.



NOTES ON SELECT
---------------

The 'pop/pops' and 'imap/imaps' sources specify a protocol with optional connection
information. It is then possible to iterate on each message and attachment.
If a userid is not specified then the current account is used. If no password
is specidied then the current account's password is used. If no host is
specified then the local server is used. The only data source operation
allowed is delete (marks as deleted current message with IMAP, deletes it
permenently with POP) and commit (purges permanently deleted messages in IMAP).
NOTE: POP and IMAP sources not available in the FREE or REGISTERED EDITION.

The 'mbox' source specifies a local mailbox. It is then possible to iterate on
each message. If a userid is specified then that account is accessed if
unrestricted access is available (not for user scripts) otherwise the
running account is used. The only data source operation allowed is delete
(which is permanent). NOTE: MBOX source not available in the FREE or
REGISTERED EDITION.

The 'ldap' source specifies a Lightweight Directory Access Protocol server to
do searches on. It is then possible to iterate on each result entry, and each
such iteration splits the result into attribute-value pairs than can be
retrieved via the $a("name") attribute array.

The 'file(char)' source specifies a 'char'-delimited text file. It is then
possible to iterate on each line, and each such iteration effectively splits
the line based on the value of 'char'.  Note: 'file' is equivalent to
'file("\t")'. Leading and trailing spaces are removed, fields are dequoted.
Allowed data source operations are outlined below.

The 'odbc' source specifies an ODBC data source. It is then possible to iterate
on each row. Each such iteration effectively splits the row based on fields.
Allowed data source operations are outlined below. (NOT AVAILABLE YET)

The 'update' function is used to write back changes to the row (in memory only)
after a set operation on a field. For example...

	select file "mailing-list.lst";
	foreach row
		update($1, $_NOW);
	next

The 'insert' function is used to add a new row either before or after the
current one. It can only be used during row processing. For example...

	select file "mailing-list.lst";
	foreach row
		if $1 = "Smith" then
			insert after("$1 - COPY", $2);
		endif
	next

The 'delete' function is used to remove the current row. It can only be used
during row processing. For example...

	select file("\t") "mailing-list.lst";
	foreach row
		if $1 contains {" - COPY"} then
			delete;
		endif
	next
	...

The 'drop' function is used to remove all rows. It can only be used before or
after row processing. For example...

	select file "mailing-list.lst";
	drop;
	...

The 'add' function is used to add a new row at the end. It can only be used
before or after row processing. For example...

	select file "mailing-list.lst";
	add("Dummy", $_NOW);
	...

The 'commit' function is used to write back changes to a data source. It can
only be used before or after row processing and permanently records the
operations so far. For example...

	select file "mailing-list.lst";
	foreach row
		...
		update ...;
	next
	commit;
	add("Dummy", $_NOW);
	commit;

If the 'commit' function is not called then any changes will be lost. It can
be used repeatedly.


Note: 'line' is acceptable as a synonym for 'row' (for backwards compatability).




NOTES ON SEARCHING AND SORTING
------------------------------

You can search and/or sort messages after a 'select' with...

	sort("[arrival|cc|date|from|reverse|size|subject|to] [...]");
	sortex("[arrival|cc|date|from|reverse|size|subject|to] [...]", "[SINCE dd-mmm-yyyy|BEFORE dd-mmm-yyyy] [...]");

for example...

	select imap "mystuff";
	sortex("subject reverse date", "since 01-Jan-1999 before 30-Jun-1999");
	foreach message
		....
	next

Also, see the 'fdate(VAR, nbr)' function to see how to format a date for 'nbr' days back.





NOTES ON SIMPLE LOOPING
-----------------------

To loop a hundred times...

	foreach loop
	
		if $# = 100 then
			break;
		endif
		
	next
	



EXAMPLE - implementing a simple mailing-list
--------------------------------------------

	set $_ = $t;
	set $listname = $A;
	set $rp = $h("Return-Path");
	set $as = $h("Auto-Submitted");
	set $_ = $f;
	set $from = $E;
	
	if $rp contains {"<>"} then
		if body.text contains {"Action: failed"} then
			wspliti("Final-Recipient: RFC822\;", $b);
			split("\n", $2);
			set $f = $1;
			delfrom("$listname.lst");
			log("$listname dropped -> '$1'");
		else
			echo;
		endif
	elseif $as contains {"auto-"} then
		noop;
	elseif $from in("$listname.lst") then
		if header.subject contains {"unsubscribe","leave"} then
			delfrom("$listname.lst");
			reply("Unsubscribed!");
			log("$listname unsubscribed -> '$f'");
		elseif header.subject contains {"subscribe","join"} then
			reply("Already subscribed!");
		elseif header.subject contains {"index", "dir"} then
			dirlist($tmp, "fetch");
			reply($tmp);
		elseif header.subject contains {"fetch", "get"} then
			split(" ", $s);
			attach("fetch/$2");
			reply("Requested file... $2");
		else
			reply("Unknown request");
		endif
	else
		if header.subject contains {"unsubscribe","leave"} then
			reply("Not already subscribed!");
		elseif header.subject contains {"subscribe","join"} then
			addto("$listname.lst");
			reply("Subscribed!");
			log("$listname subscribed -> '$f'");
		else
			reply("Not subscribed!");
		endif
	endif

	discard;



EXAMPLE - formating a spreadsheet into HTML (CGI mode) METHOD1
--------------------------------------------------------------

	write("Content-Type: text/html\n\n");
	write("<html><head><title>Spreadsheet</title></head><body>");
	write("<table border=1><tr><th>Line</th><th>Fields...</th></tr>")

	load($tmp, "spreadsheet.tab");
	split("\n", $tmp);

	foreach split
		write("<tr>");
		split("\t", $%);
		foreach split
			write("<td>$%</td>");
		next
		write("</tr>");
	next

	write("</table>");
	write("</body></html>");



EXAMPLE - formating a spreadsheet into HTML (CGI mode) METHOD2
--------------------------------------------------------------

	write("Content-Type: text/html\n\n");
	write("<html><head><title>Spreadsheet</title></head><body>");
	write("<table border=1><tr><th>Line</th><th>Field1</th><th>Field2</th></tr>")

	select file(",") "spreadsheet.csv";

	foreach row
		write("<tr><td>$#. </td><td>$1</td><td>$2</td></tr>");
	next

	write("</table>");
	write("</body></html>");



EXAMPLE - listing messages in a folder (CGI mode)
-------------------------------------------------

	write("Content-Type: text/html\n\n");

	#select pop;
	#select pop(me:secret@myisp);
	#select imap(me:secret@myisp) "inbox/personal";
	#select mbox inbox/personal;
	#select mbox "sent items";
	#select mbox "#shared/project notes";
	#select mbox "#news/alt.test";

	foreach message
		write("<li>$#. From: '$f', Subject: '$s'");
	next



EXAMPLE - listing people in an LDAP directory (CGI mode)
--------------------------------------------------------

	write("Content-Type: text/html\n\n");

	select ldap "ldap.yahoo.com, cn=Andrew Davison";

	foreach result
		set $org = $a("o");
		set $cn = $a("cn");
		set $mail = $a("mail");
		write("<li>cn = $cn, mail = $mail");
	next



EXAMPLE - list of IMAP folders (CGI mode)
-----------------------------------------

	write("Content-Type: text/html\n\n");

	list($list, "#shared/*");

	split("\n", $list);

	foreach split
		write("<li>$%");
	next



EXAMPLE - adding a custom header to a message
---------------------------------------------

	set $hh = "X-Envelope-To: $t\r\n";
	save("$h$hh", $b);



EXAMPLE - sending an arbitrary message
--------------------------------------

	set $f = "me@here";
	set $r = "reallyme@reallyhere";
	set $s = "This is a test - did you get the file?";
	set $y = "text/html; charset=us-ascii";
	set $b = "<h1>line1</h1>\n<h2>line2</h2>\n<h3>line3</h3>";
	attach(filename);
	post(mailto:"you@there");



EXAMPLE - modifying a header in a message
-----------------------------------------

	set $hh = "";
	set $found = false;

	split("\n", $h);

	foreach split
		if $% contains {"X-Counter:"} then
			split(":", $%);
			set $2 += 1;
			set $hh = "$hh\r\n$1: $2\n";
			set $found = true;
		else
			set $hh = "$hh\r\n$%\r\n";
		endif
	next

	if $found = false then
		set $hh = "$hh\r\nX-Counter: 1\r\n";
	end if

	save($hh, $b);


EXAMPLE - simple CGI forms processing
-------------------------------------

	parseCGI;

	write("Content-Type: text/html\n\n");

	set $x = $h("HTTP_USER_AGENT");
	write("<h1>Test - $x</h1>");

	set $qs = $h("QUERY_STRING");
	set $f1 = $f("field1");
	set $f2 = $f("field2");

	write("<h2>query = '$qs', field1 = '$f1', field2 = '$f2'</h2>");

	write("<form method="post" action="test.filt?field1=a">");
	write("<input type="text" name="field2" value="b"><br>");
	write("<input type="submit">");
	write("</form>");



EXAMPLE - mail an arbitrary HTML form (CGI mode)
------------------------------------------------

	write("Content-Type: text/html\n\n");

	set $b = "";

	foreach field
		set $name = $%;
		set $value = $f($name);
		set $b = "$b$name=$value\n"
	next

	set $f = "nobody@localhost";
	set $s = "test formmail2";
	set $y = "text/plain";
	post(mailto:postmaster@localhost);

	write("<p>Email sent!</p>");



EXAMPLE - connect to a POP server
---------------------------------

	set $host = "localhost";
	set $port = "110";
	set $user = "userid";
	set $pass = "secret";

	tcpconnect($host, $port);
	
	if $_ERRNO <> 0 then
		echo("Connect to '$host' failed: $_ERRNO\n");
		skip;
	endif
	
	timeout(60);

	foreach loop
		read($line);
		echo($line);	
		if $line begins {"+OK", "-ERR"} then
			break;
		endif
	next

	set $cmd = "USER $user\n";
	write($cmd);
	echo($cmd);
	read($line);
	echo($line);

	set $cmd = "PASS $pass\n";
	write($cmd);
	echo($cmd);
	read($line);
	echo($line);

	set $cmd = "LIST\n";
	write($cmd);
	echo($cmd);

	foreach loop
		read($line);
		echo($line);	
		if $line begins {"."} then
			break;
		endif
	next

	set $cmd = "QUIT\n";
	write($cmd);
	echo($cmd);
	read($line);
	echo($line);


EXAMPLE - a simple echo server
------------------------------

	tcpserver(12345);

	foreach loop

		accept();
		write("Welcome to my echo server\n");
	
		read($line);
		write($line);

		disconnect();
		
	next
	
	
