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. Some ideas were originally borrowed from an early draft of the SIEVE mail filter language. Both languages have changed a lot since then. SIEVE includes neither variables nor loop constructs and is thus not a general purpose programming language. 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; unset VAR; select SOURCE WORD; foreach ITEM [FUNCTION|break|breakall]; next if CONDITIONAL then FUNCTION; elseif CONDITIONAL then FUNCTION; else FUNCTION; endif include userfilename; sysinclude systemfilename; userinclude userfilename; return; exit; Where... SOURCE := [PROT|MBOX|ACCOUNTS|FOLDERS|FILE|ldap|#odbc] MBOX := [mbox|mbox(userid)] FILE := [file|file(char) ACCOUNTS := [accounts|accounts(owner)] FOLDERS := [folders|folders(userid)] PROT := [pop|pops|popt|imap|imaps|imapt](userid[:passwd][@hostname]) ITEM := [loop|field|attribute|var|attachment|split|row|result|bodypart|account|MESSAGE] MESSAGE := [message|message(brief|raw|uidonly)] CONDITIONAL := [EXPRESSION1|EXPRESSION2|...EXPRESSIONn] EXPRESSION := PART [not] contains [allof] {STRING-LIST} EXPRESSION := PART [not] matches {PATTERN-LIST} EXPRESSION := PART [begins|ends] {STRING-LIST} EXPRESSION := STRING [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] STRING-LIST := STRING1[,STRING2[,...STRINGn]] PATTERN-LIST := PATTERN1[,PATTERN2[,...PATTERNn]] PATTERN := STRING WORD-LIST := WORD1[,WORD2[,...WORDn]] WORD := [VAR|STRING] VAR := $CHARS NUMERAL :- [0-9] CHARS := CHAR[CHAR...CHAR] CHAR := [a-z|A-Z|_|NUMERAL|SPECIAL] HEX := [a-f|A-F|NUMERAL] SPECIAL := [\r|\n|\t|\xHEX(2)|\0NUMERAL(3)] STRING "= "CHARS" 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! Special characters can be inserted: eg "\xFF" or "\0255" are equivalent, as is '\x00' and '\0000' The character set is assumed to be 8-bit (eg. us-ascii or iso-8859-N). Special operators in pattern: ? - match exactly one character ! - match exactly one alpha $ - match zero or more alphas # - match exactly one numeric @ - match zero or more numerics The following matches: if "abc" matches {"a?c"} then if "abcd" matches {"a*d"} then if "test1test" matches {"*1*"} then if "test1test" matches {"*test"} then if "test1test" matches {"*#*"} then all produce positive results. The '<=' operator is both less-than-or-equals and string concatenation. When specifying filename paths use '/' rather than '\' as the file-syetem hierarchy delimiter. 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 clients and/or servers are set up. Note: things with a # in front of them are not implemented. 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 $_). Other built-in variables... $_NULL = an empty string $_TIME = seconds of current epoch (ie. C-time) $_SEC = current second $_MIN = current minute $_HOUR = current hour $_YDAY = days since January 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 daylight 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 $_AGE_DAYS = age in days after an 'if age' test $_AGE_SECS = age in seconds after an 'if age' test $_LDAP_EMAIL_ATTRIB = email-address attribute name (default is 'mail'). $i = remote ip-address of a connection $n = remote name of a connection $_REMOTE_IP = remote ip-address of a connection $_REMOTE_NAME = remote name of a connection $_LOCAL_IP = local ip-address of a connection $_LOCAL_NAME = local name of a connection S_APPROOT = script path $R = Return-Path address $u = userid logged in as $a = account delivered to $x = domain delivered to (from envelope address) $p = PART contents $f = From address $r = Reply-To address $e = Errors-To address $t = To address(es) $c = Cc address(es) $d = Date line $s = Subject line $b = body (all of it, lines are CRLF delimited) $y = content-type line $+ = message size (in bytes, eg. 12345678) $h = headers (all of them, CRLF delimited) $< = all internal enevelope recipients (for global filters, CRLF delimited) $> = all external enevelope recipients (for global filters, CRLF delimited) $? = attachment name $^ = attachment CID $+ = attachment size (in 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 $? = bodypart name $! = bodypart encoding (e.g. 'base64', "quoted-printable", "7bit", "8bit") $^ = bodypart CID $+ = bodypart encoded size (bytes, eg. 12345678) $- = bodypart encoded size (formated, eg. '137 bytes' or '997 Kb' or '5 Mb') $~ = bodypart MIME type (e.g. 'text/plain') $@ = bodypart charset (e.g. 'us-ascii') $= = bodypart contents (after 'getpartbyuid() call) $+ = bodypart decoded size (bytes, eg. 12345678) 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 also be iterated over by the following... foreach header ... next foreach field ... next foreach attribute ... next loop controls, 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/next command $# = loop count (after foreach/next command starting at 1) $# = number of splits (after split() function call) $# = number of results/fields/lines/rows/messages etc (after a select command) $# = number of bodyparts (after a bodystructure() 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 '$@var' means return the value of the variable named in $var. It provides a level of indirection useful for obtaininng variables of variables which are programmatically determined. Can be used to make hash/map tables. This construct can only be used inside a set command. An 'include scriptname' name can contain the sequence {WORD-LIST} where the value of WORD-LIST is usually taken from the HTTP_ACCEPT_LANGUAGES CGI environment variable, and can be used to give a customized language response. For example: set $lang = $h("HTTP_ACCEPT_LANGUAGES"); include "strings-{$lang}.inc" and the actual file included will be the most preferred match against existing files (eg. strings-en-us.inc, strings-en-uk.inc, strings-en.inc, strings-fr.inc etc.). The actual language selected is set in the $_LANGUAGE variable. The 'sysinclude scriptname' name refers to a script in the path of the currently executing application, only available in unrestricted (non-user) mode. It is used to reference a script outside the chroot'ed environment. The 'userinclude scriptname' name refers to a script in the path of the chrooted environment, only available in unrestricted (non-user) mode. It is used to process the include file in retsricted mode. If the variable '$_WARNINGS' is set then access to undefined variables causes a warning message to be output and the program continues. If the variable '$_ERRORS' is set then access to undefined variables causes a warning message to be output and the program aborts. FUNCTION SUMMARY ---------------- file(filename) - write message body/attachment to named file (previous contents are overwitten). append(filename) - append message body/attachment to named file (previous contents are preserved). system(cmd) - execute the command specified. (RETSTRICTED) exec(cmd) - execute specified command passing filename containing $b as argument 1. (RESTRICTED) Sets the $_STATUS variable with the ret code. pipe(cmd) - pipe $b thru command setting $b to the result. (RESTRICTED). Sets the $_STATUS variable with the ret code. forward(recipient) - send copy-of message to 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) - load directory-listing into var. move(folder) - move new message to named IMAP folder. copy(folder) - copy new message to named IMAP folder. moveto(folder) - move old message to named IMAP folder (preserve flags). copyto(folder) - copy old message to named IMAP folder (preserve flags). create(folder) - create named IMAP folder. delete(folder) - delete named IMAP folder. uid(VAR) - get the UID (Unique ID number) of the message. getflags(VAR) - get the IMAP flags of the message. setflags(string) - set the named IMAP flags for the message. clearflags(string) - clear the named IMAP flags for the message. undelete - undelete IMAP message. reject - forcefully delete message/attachment. discard - quietly delete message/attachment. keep - does nothing really, but does undo a prior 'discard' echo - display message-body/attachment on log (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 - no-operation (i.e. do nothing). break - break out of a 'for' loop. return - return from current include, function or processing. skip - no further rule processing occurs. exit - same as skip exit(status) - exit with status code (used in ifscript.exe). lock - enter a criticial section unlock - leave a criticial section 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 text file (previous contents are overwritten). storebin(VAR,filename) - save variable to named binary file (previous contents are overwritten). append(string,filename) - save string to named text file (previous contents are appended to). appendbin(VAR,filename) - save variable to named binary file (previous contents are appended to). remove(filename) - delete named file. copyfile(filefrom,fileto) - copy first file to (or over) second. movefile(filefrom,fileto) - move first file to (or over) second then delete first. mkdir(pathname) - make a directory rmdir(pathname) - remove an empty directory rmtree(pathname) - remove a directory and all of it's contents 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. lsub(VAR,folder) - load list of IMAP subscriptions according to pattern (eg. lsub("%"), lsub("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' (not case sensitive). lreplace(VAR,string,p1,p2) - replace in 'string' leading occurence of 'p1' with 'p2' and set the result in 'VAR' (not case sensitive). getuid(nbr) - return the uid associated with a sequence number (after sorting). gethdrsbyuid(uid) - after a select, read the message headers specified by UID number. gethdrsbyseq(nbr) - after a select, read the message headers specified by sequence number. getmsgbyuid(uid) - after a select, read the full message specified by UID number. getmsgbyseq(nbr) - after a select, read the full message specified by sequence number. bodystructurebyuid(uid) - load IMAP message BODYSTRUCTURE (then do a 'foreach part ... getpartbyuid(uid,$%,$!); next' loop). getpartbyuid(uid,part,enc) - get the named and encoded IMAP message part according to MIME-IMB specification into $= var (sets $+). imapalert(VAR) - return the IMAP response ALERT text if present. parse(filename) - parse message from file. parse - parse the current message. 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 in the future. getuser(userid) - check if account exists: see $_ERROR response (privileged access only). login(userid,password) - login as userid & password (if 'root' then sets privileged access). 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 to specify owner of account (privileged access only). adduser(userid,passwd,owner,cn) - variant allows you to specify username of account (privileged access only). moduser(userid,srvs) - modify a user account with specified services use +SRV/-SRV (privileged access only for non-self). moduser(userid,srvs,flags) - variant that also sets flags use +FLAG/-FLAG (privileged access only for non-self). setlevel(userid,n) - set account level (see USERLEVELn in 'ifmail.txt') (privileged access only). settype(userid,n) - set account type (privileged access only) gettype(VAR) - get account type of current account. gettype(VAR,userid) - get account type of specified account (privileged access only). setquota(userid,size) - set total allowed mailbox quota in Mbytes. deluser(userid) - delete a user account from the account system (privileged access only). getname(VAR,userid) - get name for specified account. getname(VAR) - get name for this account. getsrvs(VAR,userid) - get allowed services for specified account. getsrvs(VAR) - get allowed services for this account. getflgs(VAR,userid) - get allowed flags for specified account. getflgs(VAR) - get allowed flags for this account. setidentity(string) - set default replyto address for account getaliases(VAR) - get the list of email aliases for the this account. setaliases(string) - set the list of email aliases for the this account. sleep(nbr) - sleep for number of seconds. getaddr(VAR,host) - get the IP address of a host-name. gethost(VAR,addr) - get the host-name of an IP address. getmx(VAR,domain) - get the mail exchange (MX/A) for a given domain. echo(string) - print string to TTY (stdout). strlen(VAR,string) - set VAR to the length of the string. strlenbin(VAR,VAR2) - set VAR to the length of the value of VAR2. tcpconnect(host,port) - open user TCP-stream connection to named host and port and make active network connection. udpconnect(host,port) - open user 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 user network connection. tcpserver(port) - serve TCP connection off the named port. udpserver(port) - serve UDP connection off the named port. accept() - accept user connection (see XXXServer above). sendbin(VAR) - write VAR uninterpreted to user network connection or TTY. send(string) - writes string to user network connection ot TTY. recv(VAR) - reads line from user network connection or TTY getquota(VAR) - get allowed mbox quota (in Mbytes). getused(VAR) - get used mbox quota (in Mbytes). default(VAR) - return the default domain. domains(VAR) - return comma-separated list of domains. md5(VAR,string) - calculate the 128-bit MD5 hash of the string. sha1(VAR,string) - calculate the 160-bit SHA1 hash of the string. unique(VAR) - a number unique since the program started running. chars(VAR,string1,string2) - how many times any character in 'string2' occurs in 'string1'. charsbin(VAR1,VAR2,string2) - how many times any character in 'string2' occurs in VAR2. dow(VAR,y,m,d) - day of week (SUN=0,...SAT=6) given year (YYYY), month (1-12) and day (1-31). incdate(VAR,y,m,d) - returns the next date (yyyy/m/d) after given date. parsedate(VAR,datestring) - parse datestring into C-time. getlist(VAR,listname) - load persistent list into VAR (comma-separated). addlist(VAR,listname) - add VAR to persistent list. dellist(VAR,listname) - delete VAR from persistent list. restricted - go to restricted/non-privileged mode (from unrestricted/privileged). loaddata(filename) - read a file where each line is of the form 'var = string' and set the variable $d("var") to the value of string (which may be quoted). loaddata(filename,prefix) - read a file where each line is of the form 'var = string' and set the variable $d("prefixvar") to the value of string (which may be quoted). left(VAR,string,len) - get leftmost part of a string. right(VAR,string,len) - get rightmost part of a string. mid(VAR,string,pos,len) - get middle part of a string. instr(VAR,s1,s2,pos) - return position of 's2' in 's1' starting at 'pos'. // The old sort function names are still available but use is deprecated. Instead use: sort.set(dups) - set sort box with duplicates (dups=1) or not (dups=0) sort.box(string) - add value to sort box sort.get() - get sort box values into $1, $2, ... $# variables. // The old http function names are still available but use is deprecated. Instead use: http.proxy(host,port) - set proxy configuration. http.auth(userid,password) - set credentials for HTTP login. http.log(VAR) - retrieve HTTP session log. http.clearheaders() - clear user HTTP headers. http.addheader(header) - define user HTTP header. http.geturl(VAR,url) - fetch URL and retrieve contents. http.clearform() - clear form fields. http.addform(name,value) - define a form field and value. http.posturl(VAR,url) - post form to URL and retrieve contents. http.setraw(mime,string) - set MIME-type and payload for raw post. http.postraw(VAR,url) - post raw data to URL and retrieve contents. // The old xmlrpc function names are still available but use is deprecated. Instead use: xmlrpc.parse(string) - explicitly parse string (in CGI mode this is done for you). xmlrpc.echoparams(VAR) - print out all the params into a string. xmlrpc.echoparam(VAR,nbr) - print out param 'nbr' (nbr = 1, ...) into a string. xmlrpc.ismethodcall(VAR) - is it a XML-RPC method request? xmlrpc.ismethodresponse(VAR) - is it a XML-RPC method response? xmlrpc.isfault(VAR) - did the response generate an error? xmlrpc.isend(VAR) - at the end of params/array/or struct processing? xmlrpc.getmethodname(VAR) - if a request then return the method name. xmlrpc.getfaultcode(VAR) - if a fault then return the fault code. xmlrpc.getfaultstring(VAR) - if a fault then return the fault string. xmlrpc.getparam(VAR,nbr) - get value of parameter 'nbr' (nbr = 1, ...) xmlrpc.getnamed(VAR,name) - get value of named struct member. xmlrpc.getnextparam(VAR,nbr) - get value of parameter 'nbr' (nbr = 1, ...) xmlrpc.getnextnamed(VAR,name) - get value of named struct member. // Process pure XML: xml.parse(string) - parse XML string xml.print(VAR) - output XML source string to a varaible. xml.get(VAR,xpointer) - use the specified XPointer to retrieve any node xml.set(string,xpointer) - use the specified XPointer to change #text node xml.drop(xpointer) - use the specified XPointer to delete any node xml.add(xpointer,xmltext) - add node(s) under XPointer. Note: the moduser 'srvs' param is a string consisting of combinations of basic service names such as POP+IMAP+WEB+FTP+IM+NEWS+CHAT+CAP+TEAM+WEBMAIL+READONLY, using the plus sign as a separator. Note: the moduser 'flags' param is a string consisting of combinations of basic flag names such as FORWARDCOPY+SPAMFOIL+LOCALONLY, using the plus sign as a separator. 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); 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\nTo:you@there\n\nSubject: this is a test\n"; set $body = "Line1\nLine2\nLine3\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 "); will set $1 = "Mr Yonki", $2 = "yonki99", $3 = "yonki-time.com". The 'wsplit' (or wide-split) is a multi character 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 'pop3/pop3s/pop3t' and 'imap4/imap4s/imap4t' sources specify a protocol (pop3, pop3+ssl, pop3+tls, imap4, imap4+ssl or imap4+tls) 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 'accounts' source specifies the list of accounts. It is then possible to iterate on each account. If an owner is specified then the search mask is applied to accounts with that owner, otherwise the search mask is applied to all accounts. NOTE: MBOX source not available in the FREE or REGISTERED EDITION. The 'folders' source specifies the list of folders for an account. It is then possible to iterate on each folder. If an account is specified then the search mask is applied to folders in that account, otherwise the search mask is apllied to the current account. NOTE: MBOX source not available in the FREE or REGISTERED EDITION. The 'ldap' source specifies a Lightweight Directory Access Protocol (LDAP) 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 imap4 "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("Spreadsheet"); write("") load($tmp, "spreadsheet.tab"); split("\n", $tmp); foreach split write(""); split("\t", $%); foreach split write(""); next write(""); next write("
LineFields...
$%
"); write(""); EXAMPLE - formating a spreadsheet into HTML (CGI mode) METHOD2 -------------------------------------------------------------- write("Content-Type: text/html\n\n"); write("Spreadsheet"); write("") select file(",") "spreadsheet.csv"; foreach row write(""); next write("
LineField1Field2
$#. $1$2
"); write(""); EXAMPLE - listing messages in a folder (CGI mode) ------------------------------------------------- write("Content-Type: text/html\n\n"); #select pop3; #select pop3(me:secret@myisp); #select imap4(me:secret@myisp) "inbox/personal"; #select mbox "inbox/personal"; #select mbox(fred) "sent items"; #select mbox "#shared/project notes"; #select mbox "#news/alt.test"; foreach message write("
  • $#. 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("
  • 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("
  • $%"); next EXAMPLE - adding a custom header to a message --------------------------------------------- set $hh = "X-Envelope-To: $t\n"; save("$h$hh", $b); EXAMPLE - adding a custom signature to a message ------------------------------------------------ set $html_sig = "\n
    Proudly sponsored by QWERTYUIOP\n"; set $plain_sig = "\nProudly sponsored by QWERTYUIOP (http://qwertyuiop.test)\n"; if $y contains {"text/html"} then set $body = "$b$html_sig"; elseif $y contains {"text/plain"} then set $body = "$b$plain_sig"; else set $body = $b; endif save($h, $body); 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 = "

    line1

    \n

    line2

    \n

    line3

    "; 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\n$1: $2\n"; set $found = true; else set $hh = "$hh\n$%\n"; endif next if $found = false then set $hh = "$hh\nX-Counter: 1\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("

    Test - $x

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

    query = '$qs', field1 = '$f1', field2 = '$f2'

    "); write("
    "); write("
    "); write(""); write("
    "); 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("

    Email sent!

    "); 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 EXAMPLE - using a sort box -------------------------- sset(0); # remove duplicates for ... some loop ... set $unsorted_value = "some value"; sbox($unsorted_value); next sget(); set $count = $#; foreach loop set $sorted_value = $@#; if $# = $count then break; endif next Good examples of Infrascript usage are in the webmail, freemail and filter example scripts.