/*****************************************************************************/ /* SSI.c This module implements a full multi-threaded, AST-driven, asynchronous HTML Server Side Includes pre-processor. The AST-driven nature makes the code a little more difficult to follow, but creates a powerful, event-driven, multi-threaded server. All of the necessary functions implementing this module are designed to be non-blocking. The SSI functionality in the WASD server includes the basics of NCSA SSI, extensions similar to Apache's XSSI (eXtended SSI), as well as WASD, OSU and VMS idiosyncratic elements. The module has become an even bigger nightmare as the compatibility between WASD, OSU and "vanilla" SSIs has attempted to be implemented and maintained. It eventually provides a reasonably capable (but somewhat overly complex) server-side document engine including: o inclusion of server information (e.g. date, name, software) o inclusion of document information (e.g. path, last modification) o inclusion of custom layout directory listings o innocuous DCL command execution (e.g. "SHOW TIME") o privileged DCL command execution (e.g. DCL procedures) o expression evaluation and user variable assignment o flow control in document output (e.g. if-then-else) o document access counter An SSI statement CAN BE SPLIT OVER MULTIPLE LINES, up to a maximum dictated by the size of the statement buffer. Occasionally this buffer is exhausted resulting in a buffer overflow error. It can be set by the document using the directive. The default is the longest record length of the file but can be set to anything up to 32767. This directive should be the first in the SSI document. The '\' character may be used to escape characters reserved or forbidden inside of SSI directives. Here are some common examples '\#', '\"', '\{', '\}'. The flow control statements and associated evaluations were designed for simplicity, both for the author (who didn't want to spend a huge amount of time building a complex expression parser, etc.) and also for the server, with a simple scheme presumably resulting in a lower execution overhead. It is assumed most documents will have simple internal flows so this shouldn't be an issue. If very complex decision making is required it is probably better exported to a legitimate script. To ease the clutter often present in XSSI documents (lots of SSI statements all over the place) the statement allows multiple SSI statements to be grouped where the Ellipsis is shown. See the example below. Of course only SSI statements may be included in this structure! No plain HTML! The leading '#' of new SSI statement ends any previous statement. The "-->" of the " comment (not in resulting document!) synonym for echo accesses of document OSU-compliant, #include delimiter backward-compatibility integer SSI error message header set file size output format set default time format "force" to be processed as OSU (or not) set trace statements on or off OSU-compliant, include commented tags synonym for #exec directory (index of) file spec directory (index of) virtual spec synonym for the above synonym for the above OSU-compliant, accesses of document OSU-compliant, accesses of document current document creation date/time current local date/time current GMT date/time current document VMS file path current document VMS file path OSU-compliant, symbol or logical append this string to response header OSU-compliant, hardware name current document modified date same as CGI query_string OSU-compliant, HTTPd server name OSU-compliant, HTTPd server software OSU-compliant, VMS version synonym for any of the above echos any CGI scripting variable name OSU-compliant, #include delimiter NOTE: the "#dcl" and "#exec" statements are identical execute CGI/NPH script absorbing header DIRECTORY file-spec [qualifiers] DIRECTORY virtual-file-spec [qualifiers] SHOW command WRITE SYS$OUTPUT 'anything' execute CGI/NPH script absorbing header *PRIVILEGED* execute any DCL command *PRIVILEGED* execute command procedure *PRIVILEGED* execute image *PRIVILEGED* execute command procedure *PRIVILEGED* execute virtual image stop processing the current file specified file creation date/time specified URL document creation date specified file last modified date/time specified URL document last modified specified file size (bytes, Kb, Mb) specified URL document size include file's contents include URL document contents OSU-compliant, include only part OSU-compliant, include only part flow control flow control flow control flow control flow control get the RDT of this file get the RDT of this file 304 response returned if not modified "Last-Modified:" response field added "Expires:" response field added prints all assigned variables sets the variable name to value multiple SSI statements stop processing the virtual document VARIABLES --------- There are server and user variables. User variables may be assigned using the "#set" statement. The values of both may be substituted into any tag value associated with any statement. User variable names may comprise alphanumeric and underscore characters, and are not case sensitive. Variable substitution is indicated by delimitting the variable name with '{' and '}'. The variable name may be followed by one or two comma separated numbers which serve as an extraction start index and count. A single number results in the variable text from zero for the specified. Two numbers specify a start index and count. The output from the "#echo" would be

This is another example ...
This is another example! All variables available, server, CGI and user, may be displayed using the #printenv directive. All variables are global in scope. This means the same set of variables are visible to all #included SSI documents, and user variables set by #included documents are seen by their parents, etc. Server variables containing document related information generally refer to the top-level, or parent document, with these local exceptions. COMPLIANCE ......... the level of compliance that should be applied DOCUMENT_DEPTH ..... level of "#include"d documents (root file is 1) PARENT_FILE_NAME ... name of file "#include"ing this current file THIS_FILE_NAME ..... name of the current file GENERAL GUIDELINES: To use the extended functionality of WASD variables leave the quotes off of 'var=' tags wherever possible. When they are present the SSI engine attempts to retain compatibility with other, general SSI implmentations, and the OSU cersion, and this may cause unexpected interactions or results. Another option is to use the 'value=' tag. EVALUATIONS and FLOW CONTROL ---------------------------- Flow control statements work in the same fashion to other implementations. The possible WASD idiosyncrasy is "#orif" which, in the absence of a true expression parser, allows multiple conditions to execute a single block of statements. The evalution of a flow control decision in an "#if", "#orif" or "#elif" is based on one or more tests against one or more variables. if string not empty, or not numeric 0 string equal to string search (* and % wildcards) numeric less than numeric equal to numeric greater than If a single string/variable is supplied in the test then if it is numeric and non-zero then the test is true, if a string and non-empty it is true. Other states are false. The "eqs=" test does a case-insensitive string compare of the supplied string and the "value=" string. The "srch=" test searches "value=" with the supplied string, which can contain the wildcards "*" matching multiple characters and "%" matching any one character. The "lt=", "eq=" and "gt=" convert the parameters to integers and do an arithetic compare. Multiple comparisons, even with multiple variables, may occur in the one decision statement, these will act as a logical AND. Logical ORs may be created using "#orif" statements. Any evaluation tag preceded by an exclamation point will have it's result logically negated. For example, returns a BOOL value of false. Note that the OSU-compliant #begin and #end directives are implemented as flow control statements. They are only permitted in SSI files identified as OSU-compliant (i.e. .HTMLX) and cannot be used in standard WASD SSI. SIMPLE EXAMPLE -------------- This example demonstrates some of the salient features of WASD's XSSI. <!--#echo value={GREETING} --> <!--#echo value="{REMOTE_HOST}!" -->

Simple XSSI Demonstration

, the time here is . FILE EXIST/NOT-EXIST -------------------- Generally, errors encountered halt document processing and report the error. For some common circumstances, in particular the existance or not of a particular file, may require an alternative action. For file activities (e.g. #include, #flastmod, #created, #fsize) the optional 'fmt=""' tag provides some measure of control on error behaviour. If the format string begins with a '?' files not found are not reported as errors and processing continues. Other file systems errors, such as directory not found, syntax errors, etc., are always reported. Every time a file is accessed (e.g. #include, #flastmod) the server variable THE_FILE_NAME gets set to that name if successful, or reset to empty if unsuccessful. This variable can be checked to determine success or otherwise. For #included files, the 'fmt="?"' just suppresses an error report, if the file exists then it is included. For #modified file specifications use 'fmt="?"' to suppress error reporting on evaluation of files that may exist but are not mandatory. For file statistic directives (e.g. #flastmod, #fcreated, #fsize) the 'fmt="?"' tag completely suppresses all output as well as error reporting. This can be used to check for the existance of a file. For example if the file TEST.TXT exists in the following example the variable THE_FILE_NAME would contain the full file name, if it does not exist it would be empty, and the code example would behave accordingly. File does not exist! File exists! "IF-MODIFIED-SINCE:" -------------------- SSI documents generally contain dynamic elements, that is those that may change with each access to the document (e.g. current date/time). This makes evaluation of any document modification date difficult (i.e. too much for the author to bother coding ;^) and so by default no "Last-Modified:" information is supplied against an SSI document. The potential efficiencies of having document last-modified timestamps so that if-modified-since requests can generate not-modified responses are significant in a range of cases (basically where any dynamic document elements relate only to the file(s) of which the document is composed). With the significant CPU overheads of processing SSI documents this approach has the potential to return substantial benefits for suitable documents and sites. WASD allows the document author to determine whether or not a last-modified header field should be generated for a particular document and which contributing document(s) should be used to determine it. This is done using the #modified directive. Where multiple source documents (files) are employed each can be checked using the virtual= or file= tags of the #modified directive, the most recently modified will be used to determine if it's been modified and also to generate the last-modified timestamp. By default the #modified directive gets the last-modified date of the current document (i.e. when no tags follow the directive, as in the first line of the example below). The "if-modified-since" tag compares the (most recent) revision date/time of the file(s) checked using the virtual= or file= tags, with any "If-Modified-Since:" date/time supplied with the request. If the file(s) revision date/time is the same or older than the request's then a not-modified (304 status) header is generated and sent to the client and document processing stops. If more recent an appropriate "Last-Modified:" header field is added to the document and it continues to be processed. If a request has a "Pragma: no-cache" field (as with Navigator's "reload" function) the document is always generated (this is consistent with general WASD behaviour). The following (rather exagerated) example illustrates the essential features. This construct should be placed at the very beginning of the SSI document, and certainly before there is any chance of output being sent to the browser. Once output to the client has occured there can be no change to the response header information (not unreasonably). The "last-modified" tag generates a "Last-Modified:" field using the (most recent) revision date/time of the file(s) and adds it to the response header. It is not necessary to do this if the "if-not-modified" tag has been used as this implicitly generates one, but there may be circumstances where a last-modified field might be desired even though the document is always generated. The 'expires=""' tag (which is bundled in with the #modified directive) takes a string literal and generates then adds an "Expires:" response header field. The string literal should be a legitimate RFC-1123 date string. This can be used for pre-expiring documents (so they are always reloaded), set it to a date in the not-too-distant past (e.g. expires="Fri, 13 Jan 1978 14:00:00 GMT"). To try really hard to pre-expire a document (some browsers seem reluctant to do this) set the expires to 0 or a negative number (e.g. expires="0" or expires="-1") and SSI will add cache-control headers in an attempt to invalidate any caching. Of course it could also be used for setting the legitimate future expiry of documents. VERSION HISTORY --------------- 30-JUN-2016 MGD SsiGetServerVar() add ERROR_URI 26-JAN-2016 MGD user variables use Dict..() functionality 12-FEB-2014 MGD bugfix; module accounting 05-NOV-2013 MGD SsiEnd() detect and report non-SSI problem encountered 17-JUN-2010 MGD SsiDoSet() and SsiGetTagValue() allow '$' in variable names 23-MAR-2010 MGD bugfix; SsiDoDcl() report cgi=/script= query string as error 03-NOV-2006 MGD bugfix; SsiEnd() propagate included document user variables back into parent document to ensure they remain *global* 31-OCT-2006 MGD appropriate buffer spaces to SSI_STRING_SIZE 11-JUN-2005 MGD to response header, to pre-expire 10-JAN-2004 MGD , SSI #exec (#dcl) directives can be allowed on per-path basis using SET ssi=exec= (e.g. 'ssi=exec=say,show'), SSI can now be enabled on a per-path basis using 'ssi=exec=#' 07-JUL-2003 MGD cache loading from SSI using network output 05-OCT-2002 MGD refine VMS security profile usage 10-JUL-2002 MGD provide real path to directory listing 27-APR-2002 MGD make SYSPRV enabled ASTs asynchronous, bugfix; SsiAccessesClose() now synchronous using SYSPRV (J.Begg demonstrated it was required for alarm-free closure) 04-AUG-2001 MGD support module WATCHing, bugfix; ++AccountingPtr->DoSsiCount; 24-JUN-2001 MGD add setlocale() as suggested by jfp@altavista.com 30-DEC-2000 MGD rework for FILE.C getting file contents in-memory (allows CACHE.C to cache the file, the test-bench showing a 100% improvement in throughput for simple SSIs!) 01-SEP-2000 MGD add optional, local path authorization (for calls from the likes of SSI.C) 18-MAY-2000 MGD bugfix; prevent '#exec blah="@file.com"' 09-APR-2000 MGD modify ERROR_... special variables in line with ERROR.C 04-MAR-2000 MGD use FaolToNet(), et.al., add the [NO]PRIVSSI setting 31-DEC-1999 MGD support ODS-2 and ODS-5 using ODS module, allow for carriage-control in FIX/UDF format files 12-NOV-1999 MGD bugfix; brain-dead code in SsiOsuFileName() 10-OCT-1999 MGD support "scrunched" :^) SSI files, support OSU-compatible directives, bugfix; sys$parse() NAM$M_NOCONCEAL for search lists, bugfix; no 'nxt' after 'FileXabPro'! 05-AUG-1999 MGD add #include content="text/..." whatever 11-APR-1999 MGD variables to ease the creation of virtual documents 08-APR-1999 MGD last-modified/not-modified functionality, 'fmt="?"' tag on #include and other file related directives 28-MAR-1999 MGD 'FileRab.rab$w_usz' now dynamic ('FileXabFhc.xab$w_lrl'), 'StatementBuffer' dynamic () 11-MAR-1999 MGD user variables have global scope (nested HTML documents), bugfix; GetFileSpec() wasn't using generic GetTagValue() 07-NOV-1998 MGD WATCH facility 18-OCT-1998 MGD error report support 19-SEP-1998 MGD improve granularity of file open, connect, close, ACP 19-JUL-1998 MGD bugfix; GetTagValue() must return number of characters! bugfix; MapUrl_Map() pass 'rqptr' for conditionals to work 13-APR-1998 MGD add block statement, variable assignment and flow-control eXtensions 23-JAN-1998 MGD and ordinal accesses 25-OCT-1997 MGD 17-AUG-1997 MGD message database, SYSUAF-authenticated users security-profile, additional (just for the hang of it :^) 27-FEB-1997 MGD delete on close for "temporary" files 01-FEB-1997 MGD HTTPd version 4 (also changed module name to SSI.c); extensive rework of some functions; statements now allowed to span multiple lines 04-JUN-1996 MGD bugfix; SsiFileDetails() error reporting 01-DEC-1995 MGD new for HTTPd version 3 */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #undef __VMS_VER #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include /* application-related header files */ #include "wasd.h" #define WASD_MODULE "SSI" #define SSI_DEFAULT_TIME_FORMAT "%Od-%b-%Y %T" /* a little VMSish! */ #define SSI_DEFAULT_SIZE_FORMAT "abbrev" #define SSI_DEFAULT_COMPLIANCE_LEVEL 603 /* current SSI implemented v6.0.3 */ #define SSI_OSU_COMPLIANCE_LEVEL 603 /* OSU HTMLX supported at v6.0.3 */ #define SSI_VAR_FMT_COMPLIANCE_LEVEL 603 /* #echo var="name=fmt" at v6.0.3 */ #define FILE_LAST_MODIFIED 1 #define FILE_FCREATED 2 #define FILE_FLASTMOD 3 #define FILE_FSIZE 4 #define SSI_STATE_DEFAULT 1 #define SSI_STATE_IF 2 #define SSI_STATE_ELIF 3 #define SSI_STATE_ELSE 4 /* for OSU #include part compliance */ #define SSI_STATE_BEGIN 5 #define SSI_MAX_DEPTH 5 /******************/ /* global storage */ /******************/ int SsiComplianceLevel, SsiSizeMax; /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern int DclCgiVariablePrefixLength; extern int ToLowerCase[], ToUpperCase[]; extern unsigned long SysPrvMask[]; extern char ConfigContentTypeSsi[], ErrorSanityCheck[], HttpProtocol[], SoftwareID[], TimeGmtString[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern MSG_STRUCT Msgs; extern SYS_INFO SysInfo; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Open the specified file. If there is a problem opening it then just return the status, do not report an error or anything. This is used to determine whether the file exists, as when attempting to open one of multiple, possible home pages. As pre-processed HTML files are by definition dynamic, no check of any "If-Modified-Since:" request field date/time is made, and no "Last-Modified:" field is included in any response header. When successfully opened and connected generate an HTTP header, if required. Once open and connected the pre-processing becomes AST-driven. */ SsiBegin (REQUEST_STRUCT *rqptr) { int status; char *cptr, *sptr, *zptr; FILE_CONTENT *fcptr; SSI_TASK *tkptr, *ParentDocumentTaskPtr, *RootDocumentTaskPtr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiBegin() !&F !&X !UL !UL !UL", &SsiBegin, rqptr->FileContentPtr->ContentPtr, rqptr->FileContentPtr->ContentSize, rqptr->FileContentPtr->ContentLength, rqptr->FileContentPtr->ContentRemaining); if (!rqptr->SsiTaskPtr) rqptr->SsiTaskPtr = NULL; /* take control of the file contents structure */ fcptr = rqptr->FileContentPtr; rqptr->FileContentPtr = NULL; if (ERROR_REPORTED (rqptr)) { /* previous error, cause threaded processing to unravel */ SysDclAst (fcptr->NextTaskFunction, rqptr); return; } if (!(Config.cfSsi.Enabled || (rqptr->rqPathSet.SsiExecPtr && rqptr->rqPathSet.SsiExecPtr[0] == '#'))) { rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_DISABLED), FI_LI); SysDclAst (fcptr->NextTaskFunction, rqptr); return; } /* SSI documents can call other SSI documents ... infinite recursion! */ if (LIST_GET_COUNT (&rqptr->SsiTaskList) > SSI_MAX_DEPTH) { rqptr->rqResponse.HttpStatus = 403; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_SSI_RECURSION), FI_LI); SysDclAst (fcptr->NextTaskFunction, rqptr); return; } if (WATCHING (rqptr, WATCH_RESPONSE)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "SSI !AZ !UL bytes", fcptr->FileName, fcptr->ContentLength); if (rqptr->SsiTaskPtr) { /* this is an included SSI document */ ParentDocumentTaskPtr = rqptr->SsiTaskPtr; RootDocumentTaskPtr = rqptr->SsiTaskPtr->RootDocumentTaskPtr; } else { /* no parent SSI document, will generate new CGI/user variables */ ParentDocumentTaskPtr = RootDocumentTaskPtr = NULL; } /* set up the task structure (possibly multiple concurrent) */ if (rqptr->SsiTaskPtr && LIST_HAS_NEXT (rqptr->SsiTaskPtr)) { rqptr->SsiTaskPtr = tkptr = LIST_GET_NEXT (rqptr->SsiTaskPtr); memset (LIST_GET_DATA(tkptr), 0, sizeof(SSI_TASK)-sizeof(LIST_ENTRY)); } else { rqptr->SsiTaskPtr = tkptr = VmGetHeap (rqptr, sizeof(SSI_TASK)); ListAddTail (&rqptr->SsiTaskList, tkptr, LIST_ENTRY_TYPE_SSI); } OdsStructInit (&tkptr->DetailsOds, true); /* set up read to SSI parse the file's body */ tkptr->FileContentPtr = fcptr; tkptr->ParsePtr = fcptr->ContentPtr; /* buffer the request's escape-HTML flag */ tkptr->OutputBufferEscapeHtml = rqptr->NetWriteEscapeHtml; tkptr->SizeFmtPtr = SSI_DEFAULT_SIZE_FORMAT; tkptr->TimeFmtPtr = SSI_DEFAULT_TIME_FORMAT; if (RootDocumentTaskPtr) tkptr->RootDocumentTaskPtr = RootDocumentTaskPtr; else tkptr->RootDocumentTaskPtr = tkptr; tkptr->ParentDocumentTaskPtr = ParentDocumentTaskPtr; if (tkptr->ParentDocumentTaskPtr) { /* inherit these from the parent document */ tkptr->ComplianceLevel = ParentDocumentTaskPtr->ComplianceLevel; tkptr->OsuCompliant = ParentDocumentTaskPtr->OsuCompliant; tkptr->TagVerify = ParentDocumentTaskPtr->TagVerify; tkptr->TraceState = ParentDocumentTaskPtr->TraceState; tkptr->CgiBufferPtr = tkptr->ParentDocumentTaskPtr->CgiBufferPtr; tkptr->UserDictPtr = DictDuplicate (rqptr, tkptr->ParentDocumentTaskPtr->UserDictPtr); } else { /* top-level document */ rqptr->AccountingDone++; InstanceGblSecIncrLong (&AccountingPtr->DoSsiCount); /* set the default compliance level */ if (!SsiComplianceLevel) { /* first time though check for this kludge */ if (!(cptr = getenv ("WASD_SSI_COMPLIANCE")) && !(cptr = getenv ("HTTPD$SSI_COMPLIANCE"))) SsiComplianceLevel = SSI_DEFAULT_COMPLIANCE_LEVEL; else SsiComplianceLevel = atoi(cptr); } tkptr->ComplianceLevel = SsiComplianceLevel; if (tkptr->ComplianceLevel >= SSI_OSU_COMPLIANCE_LEVEL) { /* OSU SSI? (it is if the content description contains "OSU"!) */ if (rqptr->rqContentInfo.DescriptionPtr && strstr (rqptr->rqContentInfo.DescriptionPtr, "OSU")) tkptr->OsuCompliant = true; } /* generate top-level document CGI variables to be used thoughout */ if (VMSnok (CgiGenerateVariables (rqptr, CGI_VARIABLE_STREAM))) { SsiEnd (rqptr); return; } tkptr->CgiBufferPtr = rqptr->rqCgi.BufferPtr; rqptr->rqCgi.BufferPtr = NULL; tkptr->UserDictPtr = DictCreate (rqptr, -1); } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z !UL !&B", fcptr->FileName, tkptr->ComplianceLevel, tkptr->OsuCompliant); if (tkptr->OsuCompliant && !tkptr->ParentDocumentTaskPtr && SsiGetVar (rqptr, "FORM___PART", NULL, true)) { /*****************/ /* OSU path part */ /*****************/ /* part was supplied with an OSU "/path/file.part.type" */ cptr = SsiGetVar (rqptr, "FORM___PART", NULL, false); zptr = (sptr = tkptr->CurrentPart) + sizeof(tkptr->CurrentPart); while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); SsiEnd (rqptr); return; } *sptr = '\0'; tkptr->FlowControlState[0] = SSI_STATE_DEFAULT; tkptr->FlowControlHasExecuted[0] = tkptr->FlowControlIsExecuting[0] = false; } else if (tkptr->OsuCompliant && tkptr->ParentDocumentTaskPtr && tkptr->ParentDocumentTaskPtr->IncludePart[0]) { /********************/ /* OSU include part */ /********************/ /* if the parent had an include part then can't begin straight off */ strcpy (tkptr->CurrentPart, tkptr->ParentDocumentTaskPtr->IncludePart); tkptr->FlowControlState[0] = SSI_STATE_DEFAULT; tkptr->FlowControlHasExecuted[0] = tkptr->FlowControlIsExecuting[0] = false; } else { /************/ /* WASD SSI */ /************/ /* no flow control structure always outputs */ tkptr->FlowControlState[0] = SSI_STATE_DEFAULT; tkptr->FlowControlHasExecuted[0] = false; tkptr->FlowControlIsExecuting[0] = true; } tkptr->FlowControlIndex = 0; if (!rqptr->rqResponse.HeaderGenerated) { rqptr->rqResponse.PreExpired = PRE_EXPIRE_SSI; ResponseHeader200 (rqptr, "text/html", NULL); if (rqptr->rqHeader.Method == HTTP_METHOD_HEAD) { SsiEnd (rqptr); return; } } /* network writes are checked for success, fudge the first one! */ rqptr->NetIoPtr->WriteStatus = SS$_NORMAL; if (*tkptr->ParsePtr) { tkptr->LineNumber++; if (tkptr->TraceState) SsiTraceLine (rqptr, tkptr->ParsePtr); } if (rqptr->rqPathSet.CacheSSI && !rqptr->rqCache.LoadCheck) { /* SSI output to be cached (using network output) */ rqptr->rqCache.LoadFromNet = CacheLoadBegin (rqptr, (int)rqptr->rqResponse.ContentLength64, rqptr->rqResponse.ContentTypePtr); } SsiParse (rqptr); } /*****************************************************************************/ /* End of SSI interpretation, successful or otherwise. */ SsiEnd (REQUEST_STRUCT *rqptr) { int HttpStatus; char *cptr, *sptr, *linptr, *modptr; DICT_STRUCT *dicptr; SSI_TASK *tkptr; REQUEST_AST NextTaskFunction; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiEnd() !&F !&A", &SsiEnd, rqptr->SsiTaskPtr->FileContentPtr->NextTaskFunction); tkptr = rqptr->SsiTaskPtr; if (ERROR_REPORTED (rqptr)) { if (rqptr->rqResponse.HeaderSent) { if (cptr = rqptr->rqResponse.ErrorReportPtr) { HttpStatus = rqptr->rqResponse.HttpStatus; /* oh boy, what a kludge :-{ */ if (modptr = strstr (cptr, "name=\"module\" content=\"")) modptr += 23; else modptr = "?"; if (linptr = strstr (cptr, "name=\"line\" content=\"")) linptr += 21; else linptr = "?"; for (sptr = modptr; *sptr && *sptr != '\"'; sptr++); if (*sptr) *sptr = '\0'; for (sptr = linptr; *sptr && *sptr != '\"'; sptr++); if (*sptr) *sptr = '\0'; SsiProblem (rqptr, "!AZ ", HttpStatusCodeText(HttpStatus), HttpStatus, modptr, linptr, FI_LI); } else SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_ERROR), FI_LI); } } OdsParseRelease (&tkptr->DetailsOds); NextTaskFunction = tkptr->FileContentPtr->NextTaskFunction; /* free the memory allocated to contain the file contents */ if (tkptr->FileContentPtr) { VmFreeFromHeap (rqptr, tkptr->FileContentPtr, FI_LI); tkptr->FileContentPtr = NULL; } /* restore previous SSI task (if any) */ rqptr->SsiTaskPtr = LIST_GET_PREV (tkptr); if (rqptr->SsiTaskPtr) { /* ensure global user variables are kept that way (thanks JPP) */ dicptr = rqptr->SsiTaskPtr->UserDictPtr; rqptr->SsiTaskPtr->UserDictPtr = tkptr->UserDictPtr; DictDestroy (dicptr); /* propagate any requirement to stop processing */ rqptr->SsiTaskPtr->StopProcessing = tkptr->StopProcessing; } /* restore the escape-HTML flag */ rqptr->NetWriteEscapeHtml = tkptr->OutputBufferEscapeHtml; /* pass control to the next task function */ SysDclAst (NextTaskFunction, rqptr); } /*****************************************************************************/ /* 'tkptr->ParsePtr' points to the currently parsed-up-to position in the buffer. Continue parsing the buffer looking for preprocessor statements. */ SsiParse (REQUEST_STRUCT *rqptr) { BOOL BeginSsi; int status; char ch; char *cptr; SSI_TASK *tkptr; REQUEST_AST AstFunction; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiParse() !&F", &SsiParse); tkptr = rqptr->SsiTaskPtr; if (rqptr->RequestState >= REQUEST_STATE_ABORT) { tkptr->StopProcessing = true; SsiEnd (rqptr); return; } if (ERROR_REPORTED (rqptr) || tkptr->StopProcessing) { tkptr->StopProcessing = true; SsiEnd (rqptr); return; } /* ensure the escape-HTML flag is "off" from any previous processing */ rqptr->NetWriteEscapeHtml = tkptr->TraceState; if (tkptr->TerminatedPtr) { /* For convenient (read lazy) and possibly more efficient programming each SSI directive has it's first character pointed to and it's next-after-last character overwritten with a null. This allows it to be parsed as a null-terminated string without needing to be copied to some other buffer, or the like. If this has happened then 'TerminatedPtr' points to the in-memory location and 'TerminatedChar' contains the character that was overwritten. Just restore the in-memory file to it's original condition. Must be restored *after* any asynchronous processing (e.g. #including a file) hence it being done here before we begin any next directive. */ *tkptr->TerminatedPtr = tkptr->TerminatedChar; tkptr->TerminatedPtr = NULL; } if (tkptr->StatementEndPtr) { /* adjust parse pointer for any statement previously processed */ cptr = tkptr->ParsePtr = tkptr->StatementEndPtr; if (cptr[0] == '-' && cptr[1] == '-' && cptr[2] == '>') { if (tkptr->InsideSsiStatement) tkptr->InsideSsiStatement = false; tkptr->ParsePtr += 3; } } BeginSsi = false; tkptr->StatementBeginPtr = tkptr->StatementEndPtr = NULL; /** if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchDataDump (tkptr->ParsePtr, strlen(tkptr->ParsePtr)); **/ if (tkptr->InsideSsiStatement) { /**********************************/ /* inside an "" */ /*****************************/ cptr = tkptr->ParsePtr; /* look for the start of a statement */ while (*cptr) { while (*cptr && *cptr != '<') { if (*cptr++ != '\n') continue; tkptr->LineNumber++; if (tkptr->TraceState && *cptr) SsiTraceLine (rqptr, cptr); if (tkptr->SuppressLine) { tkptr->ParsePtr = cptr; tkptr->SuppressLine = false; } } if (!*cptr) break; if (cptr[0] == '<' && cptr[1] == '!' && cptr[2] == '-' && cptr[3] == '-' && cptr[4] == '#') { tkptr->StatementBeginPtr = cptr; break; } cptr++; } if (tkptr->StatementBeginPtr && !strsame (tkptr->StatementBeginPtr, "", tkptr->StatementLength, tkptr->StatementBeginPtr); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); tkptr->TraceOutput = true; } cptr = tkptr->StatementBeginPtr; ch = TOLO(cptr[1]); if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex]) { /*************************************************/ /* flow-control is currently allowing processing */ /*************************************************/ if (SAME2(cptr,'##')) SsiDoComment (rqptr); else if (SAME2(cptr,'#"')) SsiDoEcho (rqptr); else if (ch == 'b' && strsame (cptr, "#begin", 6)) SsiDoBegin (rqptr); else if (ch == 'a' && strsame (cptr, "#accesses", 9)) SsiDoAccesses (rqptr); else if (ch == 'c' && strsame (cptr, "#config", 7)) SsiDoConfig (rqptr); else if (ch == 'd' && strsame (cptr, "#dcl", 4)) SsiDoDcl (rqptr); else if (ch == 'd' && strsame (cptr, "#dir", 4)) SsiDoDir (rqptr); else if (ch == 'e' && strsame (cptr, "#echo", 5)) SsiDoEcho (rqptr); else if (ch == 'e' && strsame (cptr, "#elif", 5)) SsiDoElif (rqptr); else if (ch == 'e' && strsame (cptr, "#else", 5)) SsiDoElse (rqptr); else /* MUST precede #end for the obvious reason */ if (ch == 'e' && strsame (cptr, "#endif", 6)) SsiDoEndif (rqptr); else if (ch == 'e' && strsame (cptr, "#end", 4)) SsiDoEnd (rqptr); else if (ch == 'e' && strsame (cptr, "#exec", 5)) SsiDoDcl (rqptr); else if (ch == 'e' && strsame (cptr, "#exit", 5)) SsiDoExit (rqptr); else if (ch == 'f' && strsame (cptr, "#fcreated", 9)) SsiDoFCreated (rqptr); else if (ch == 'f' && strsame (cptr, "#flastmod", 9)) SsiDoFLastMod (rqptr); else if (ch == 'f' && strsame (cptr, "#fsize", 6)) SsiDoFSize (rqptr); else if (ch == 'i' && strsame (cptr, "#if", 3)) SsiDoIf (rqptr); else if (ch == 'i' && strsame (cptr, "#include", 8)) SsiDoInclude (rqptr); else if (ch == 'i' && strsame (cptr, "#index", 6)) SsiDoDir (rqptr); else if (ch == 'm' && strsame (cptr, "#modified", 9)) SsiDoModified (rqptr); else if (ch == 'o' && strsame (cptr, "#orif", 5)) SsiDoOrif (rqptr); else if (ch == 'p' && strsame (cptr, "#printenv", 9)) SsiDoPrintEnv (rqptr); else /* not needed with WASD v7.2ff, backward compatibility, do nothing! */ if (ch == 's' && strsame (cptr, "#scrunch", 8)) status = 0; else if (ch == 's' && strsame (cptr, "#set", 4)) SsiDoSet (rqptr); else if (ch == 's' && strsame (cptr, "#stop", 5)) SsiDoStop (rqptr); else /* #trace is kept for backward compatibility */ if (ch == 't' && strsame (cptr, "#trace", 6)) SsiDoTrace (rqptr); else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_STATEMENT_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } else { /********************************************/ /* flow control is currently not processing */ /********************************************/ /* looking only for the following */ if (ch == 'b' && strsame (cptr, "#begin", 6)) SsiDoBegin (rqptr); else if (ch == 'i' && strsame (cptr, "#if", 3)) SsiDoIf (rqptr); else if (ch == 'o' && strsame (cptr, "#orif", 5)) SsiDoOrif (rqptr); else if (ch == 'e' && strsame (cptr, "#elif", 5)) SsiDoElif (rqptr); else if (ch == 'e' && strsame (cptr, "#else", 5)) SsiDoElse (rqptr); else if (ch == 'e' && strsame (cptr, "#endif", 6)) SsiDoEndif (rqptr); else SysDclAst (&SsiParse, rqptr); } } /*****************************************************************************/ /* Number of times this document has been accessed. Working with AST-driven code is sometimes like participating in a lonnngg, sloowww root-canal. */ SsiDoAccesses (REQUEST_STRUCT *rqptr) { int status; char *dptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoAccesses()"); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); if (!Config.cfSsi.AccessesEnabled) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_ACCESS_DISABLED), FI_LI); SsiEnd (rqptr); return; } tkptr->AccessSinceText[0] = tkptr->FormatString[0] = '\0'; tkptr->AccessOrdinal = false; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (rqptr->SsiTaskPtr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "ORDINAL", 7)) { dptr += 7; tkptr->AccessOrdinal = true; } else if (strsame (dptr, "SINCE=", 6)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->AccessSinceText, sizeof(tkptr->AccessSinceText)); else if (strsame (dptr, "TIMEFMT=", 8)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->FormatString, sizeof(tkptr->FormatString)); else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } /* if the file's access count has been previously determined */ if (tkptr->AccessCount) SsiAccessesOutput (rqptr); else SsiAccessesOpen (rqptr); } /*****************************************************************************/ /* AST-driven file create/open. These functions do not need to be ODS-5 aware as they do not use a NAM block. */ SsiAccessesOpen (REQUEST_STRUCT *rqptr) { int status; char *dptr, *cptr, *sptr, *zptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ tkptr = rqptr->SsiTaskPtr; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesOpen() !&F", &SsiAccessesOpen); zptr = (sptr = tkptr->ScratchFileName) + sizeof(tkptr->ScratchFileName); for (cptr = tkptr->FileContentPtr->FileName; *cptr && *cptr != ';' && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = '$'; if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); SsiEnd (rqptr); return; } *sptr = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", tkptr->ScratchFileName); tkptr->AccessFab = cc$rms_fab; tkptr->AccessFab.fab$l_ctx = rqptr; tkptr->AccessFab.fab$l_fna = tkptr->ScratchFileName; tkptr->AccessFab.fab$b_fns = sptr-tkptr->ScratchFileName; tkptr->AccessFab.fab$l_fop = FAB$M_CIF | FAB$M_SQO; tkptr->AccessFab.fab$b_fac = FAB$M_GET | FAB$M_PUT | FAB$M_UPD; tkptr->AccessFab.fab$w_mrs = sizeof(unsigned long); tkptr->AccessFab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD; tkptr->AccessFab.fab$b_org = FAB$C_SEQ; tkptr->AccessFab.fab$b_rfm = FAB$C_FIX; /* initialize the date extended attribute block */ tkptr->AccessFab.fab$l_xab = &tkptr->AccessXabDat; tkptr->AccessXabDat = cc$rms_xabdat; tkptr->AccessXabDat.xab$l_nxt = &tkptr->AccessXabPro; /* initialize the protection extended attribute block */ tkptr->AccessXabPro = cc$rms_xabpro; tkptr->AccessXabPro.xab$w_pro = 0xaa00; /* W:RE,G:RE,O:RWED,S:RWED */ /* "temporary" file, automatic delete on closing it */ if (rqptr->DeleteOnClose) tkptr->AccessFab.fab$l_fop |= FAB$M_DLT; /* turn on SYSPRV to allow access to the counter file */ sys$setprv (1, &SysPrvMask, 0, 0); tkptr->AccessFab.fab$l_fop |= FAB$M_ASY; status = sys$create (&tkptr->AccessFab, &SsiAccessesOpenAst, &SsiAccessesOpenAst); sys$setprv (0, &SysPrvMask, 0, 0); } /*****************************************************************************/ /* AST called from SsiDoAccesses() when asynchronous RAB connect completes. */ SsiAccessesOpenAst (struct FAB *FabPtr) { int status; REQUEST_STRUCT *rqptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = FabPtr->fab$l_ctx; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesOpenAst() !&F sts:!&S stv:!&S", &SsiAccessesOpenAst, FabPtr->fab$l_sts, FabPtr->fab$l_stv); #if WATCH_MOD HttpdCheckPriv (FI_LI); #endif /* WATCH_MOD */ tkptr = rqptr->SsiTaskPtr; if (VMSnok (FabPtr->fab$l_sts) && FabPtr->fab$l_stv) status = FabPtr->fab$l_stv; else status = FabPtr->fab$l_sts; if (VMSnok (status)) { SsiProblem (rqptr, "!&m", status, FI_LI); tkptr->AccessAstFunction = &SsiEnd; SsiAccessesClose (&tkptr->AccessFab); return; } tkptr->AccessRab = cc$rms_rab; tkptr->AccessRab.rab$l_ctx = rqptr; tkptr->AccessRab.rab$l_fab = &tkptr->AccessFab; tkptr->AccessRab.rab$l_rbf = tkptr->AccessCount; tkptr->AccessRab.rab$w_rsz = sizeof(tkptr->AccessCount); status = sys$connect (&tkptr->AccessRab, &SsiAccessesConnectAst, &SsiAccessesConnectAst); } /*****************************************************************************/ /* AST called from SsiDoAccesses() when asynchronous RAB connect completes. */ SsiAccessesConnectAst (struct RAB *RabPtr) { int status; REQUEST_STRUCT *rqptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = RabPtr->rab$l_ctx; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesConnectAst() !&F sts:!&S stv:!&S", &SsiAccessesConnectAst, RabPtr->rab$l_sts, RabPtr->rab$l_stv); tkptr = rqptr->SsiTaskPtr; if (VMSnok (RabPtr->rab$l_sts) && RabPtr->rab$l_stv) status = RabPtr->rab$l_stv; else status = RabPtr->rab$l_sts; if (VMSnok (status)) { SsiProblem (rqptr, "!&m", status, FI_LI); tkptr->AccessAstFunction = &SsiEnd; SsiAccessesClose (&tkptr->AccessFab); return; } if (RabPtr->rab$l_sts == RMS$_CREATED) { /***************************************/ /* count file did not previously exist */ /***************************************/ tkptr->AccessCount = 1; tkptr->AccessRab.rab$l_rbf = &tkptr->AccessCount; tkptr->AccessRab.rab$w_rsz = sizeof(tkptr->AccessCount); tkptr->AccessRab.rab$l_rop |= RAB$M_ASY; sys$put (&tkptr->AccessRab, &SsiAccessesUpdateAst, &SsiAccessesUpdateAst); return; } else { /**********************/ /* count file existed */ /**********************/ tkptr->AccessRab.rab$l_ubf = &tkptr->AccessCount; tkptr->AccessRab.rab$w_usz = sizeof(tkptr->AccessCount); tkptr->AccessRab.rab$l_rop |= RAB$M_ASY; status = sys$get (&tkptr->AccessRab, &SsiAccessesGetAst, &SsiAccessesGetAst); return; } } /*****************************************************************************/ /* Count file existed and the sys$get() of the previous count from SsiAccessesConnectAst() has generated an AST to this function. */ SsiAccessesGetAst (struct RAB *RabPtr) { int status; REQUEST_STRUCT *rqptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = RabPtr->rab$l_ctx; tkptr = rqptr->SsiTaskPtr; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesGetAst() !&F, sts:!&S stv:!&S !UL", &SsiAccessesGetAst, RabPtr->rab$l_sts, RabPtr->rab$l_stv, tkptr->AccessCount); if (VMSnok (RabPtr->rab$l_sts) && RabPtr->rab$l_stv) status = RabPtr->rab$l_stv; else status = RabPtr->rab$l_sts; if (VMSnok (status)) { if (status != RMS$_EOF) { SsiProblem (rqptr, "!&m", status, FI_LI); tkptr->AccessAstFunction = &SsiEnd; SsiAccessesClose (&tkptr->AccessFab); return; } /* EOF indicates the initial write failed (e.g. disk quota) */ tkptr->AccessCount = 1; tkptr->AccessRab.rab$l_rbf = &tkptr->AccessCount; tkptr->AccessRab.rab$w_rsz = sizeof(tkptr->AccessCount); tkptr->AccessRab.rab$l_rop |= RAB$M_ASY; sys$put (&tkptr->AccessRab, &SsiAccessesUpdateAst, &SsiAccessesUpdateAst); return; } tkptr->AccessCount++; tkptr->AccessRab.rab$l_rbf = &tkptr->AccessCount; tkptr->AccessRab.rab$w_rsz = sizeof(tkptr->AccessCount); tkptr->AccessRab.rab$l_rop |= RAB$M_ASY; status = sys$update (&tkptr->AccessRab, &SsiAccessesUpdateAst, &SsiAccessesUpdateAst); } /*****************************************************************************/ /* Called as an AST from either the sys$put() in SsiAccessesConnectAst() or the sys$update() in SsiAccessesgetAst(). Check the status of the update to the access count in the file and if OK generated the required SSI page output. */ SsiAccessesUpdateAst (struct RAB *RabPtr) { int status; REQUEST_STRUCT *rqptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = RabPtr->rab$l_ctx; tkptr = rqptr->SsiTaskPtr; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesUpdateAst() !&F sts:!&S stv:!&S !UL", &SsiAccessesUpdateAst, RabPtr->rab$l_sts, RabPtr->rab$l_stv, tkptr->AccessCount); if (VMSnok (RabPtr->rab$l_sts) && RabPtr->rab$l_stv) status = RabPtr->rab$l_stv; else status = RabPtr->rab$l_sts; if (VMSnok (status)) { SsiProblem (rqptr, "!&m", status, FI_LI); tkptr->AccessAstFunction = &SsiEnd; SsiAccessesClose (&tkptr->AccessFab); return; } SsiAccessesOutput (rqptr); } /*****************************************************************************/ /* This function can be called from a number of places to close an open access count file. Prior to calling this function 'tkptr->AccessAstFunction' should be set to the function required to be executed after the close is complete. */ SsiAccessesClose (struct FAB *FabPtr) { int status; REQUEST_STRUCT *rqptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = FabPtr->fab$l_ctx; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesClose() !&F", &SsiAccessesClose); tkptr = rqptr->SsiTaskPtr; if (tkptr->AccessFab.fab$w_ifi) { if (rqptr->DeleteOnClose) tkptr->AccessFab.fab$l_fop |= FAB$M_DLT; /* use SYSPRV to ensure alarm-free close of file */ sys$setprv (1, &SysPrvMask, 0, 0); tkptr->AccessFab.fab$l_fop |= FAB$M_ASY; status = sys$close (&tkptr->AccessFab, &SsiAccessesFileClosed, &SsiAccessesFileClosed); sys$setprv (0, &SysPrvMask, 0, 0); return; } SysDclAst (tkptr->AccessAstFunction, rqptr); } /*****************************************************************************/ /* May be called directly or as an AST from sys$close() above. */ SsiAccessesFileClosed (struct FAB *FabPtr) { REQUEST_STRUCT *rqptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = FabPtr->fab$l_ctx; tkptr = rqptr->SsiTaskPtr; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesFileClosed() !&F sts:!&S stv:!&S", &SsiAccessesFileClosed, FabPtr->fab$l_sts, FabPtr->fab$l_stv); #if WATCH_MOD HttpdCheckPriv (FI_LI); #endif /* WATCH_MOD */ SysDclAst (tkptr->AccessAstFunction, rqptr); } /*****************************************************************************/ /* Generate and output the formatted access count. */ SsiAccessesOutput (REQUEST_STRUCT *rqptr) { static char *Ordination [] = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; int status, TensUnits; unsigned short Length; char *OrdinalPtr; char TimeString [256]; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiAccessesOutput() !8XL", &SsiAccessesOutput); tkptr = rqptr->SsiTaskPtr; if (tkptr->AccessOrdinal) { TensUnits = tkptr->AccessCount % 100; if (TensUnits >= 11 && TensUnits <= 19) OrdinalPtr = "th"; else OrdinalPtr = Ordination[TensUnits%10]; } else OrdinalPtr = ""; if (tkptr->AccessSinceText[0]) { if (!SsiTimeString (rqptr, &tkptr->AccessXabDat.xab$q_cdt, tkptr->FormatString, TimeString, sizeof(TimeString))) { SsiEnd (rqptr); return; } } else TimeString[0] = '\0'; status = FaoToNet (rqptr, "!&L!AZ!AZ!AZ", tkptr->AccessCount, OrdinalPtr, tkptr->AccessSinceText, TimeString); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); tkptr->TraceOutput = true; if (tkptr->AccessFab.fab$w_ifi) { /* we've been access the on-disk file */ tkptr->AccessAstFunction = &SsiParse; SsiAccessesClose (&tkptr->AccessFab); } else { /* already had the access count, just continue */ SysDclAst (&SsiParse, rqptr); } } /*****************************************************************************/ /* OSU-compliant, marking the beginning of an #includable "part". */ SsiDoBegin (REQUEST_STRUCT *rqptr) { char String [SSI_INCLUDE_PART_MAX+1]; char *cptr, *dptr, *sptr, *zptr; SSI_TASK *tkptr, *ParentDocumentTaskPtr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoBegin()"); tkptr = rqptr->SsiTaskPtr; if (!tkptr->OsuCompliant || !tkptr->CurrentPart[0]) { /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); return; } if (tkptr->TraceState) SsiTraceStatement (rqptr); dptr = tkptr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr && isspace(*dptr)) dptr++; while (*dptr) { while (*dptr && (*dptr == '\"' || isspace(*dptr))) dptr++; if (!*dptr) break; zptr = (sptr = String) + sizeof(String); while (*dptr && *dptr != '\"' && !isspace(*dptr) && sptr < zptr) *sptr++ = *dptr++; if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); SsiEnd (rqptr); return; } *sptr = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z !&Z", String, tkptr->CurrentPart); if (strcmp (String, tkptr->CurrentPart)) continue; tkptr->SuppressLine = true; tkptr->FlowControlIndex++; if (tkptr->FlowControlIndex > SSI_MAX_FLOW_CONTROL) { tkptr->FlowControlIndex = 0; SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); return; } /* beginning of the #included part */ tkptr->FlowControlState[tkptr->FlowControlIndex] = SSI_STATE_BEGIN; tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex] = tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = true; break; } /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Comment hidden from the final document because it's inside an SSI statement! */ SsiDoComment (REQUEST_STRUCT *rqptr) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoComment()"); if (rqptr->SsiTaskPtr->TraceState) SsiTraceStatement (rqptr); rqptr->SsiTaskPtr->SuppressLine = true; SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Config sets the default behaviour for a specific action for the rest of the document. */ SsiDoConfig (REQUEST_STRUCT *rqptr) { int status, BufferSize; char *cptr, *dptr; char TagValue [256]; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoConfig() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); dptr = tkptr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr && isspace(*dptr)) dptr++; if (strsame (dptr, "BUFFERSIZE", 6)) { /* not needed with WASD v7.2ff, backward compatibility, do nothing! */ dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); } else if (strsame (dptr, "COMPLIANCE", 10)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); tkptr->ComplianceLevel = atoi(TagValue); } else if (strsame (dptr, "ERRMSG", 6)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); cptr = VmGetHeap (rqptr, strlen(TagValue)+1); strcpy (tkptr->ErrMsgPtr = cptr, TagValue); } else if (strsame (dptr, "SIZEFMT", 7)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); if (!(strsame (TagValue, "abbrev", -1) || strsame (TagValue, "bytes", -1) || strsame (TagValue, "blocks", -1))) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_VALUE), FI_LI); SsiEnd (rqptr); return; } cptr = VmGetHeap (rqptr, strlen(TagValue)+1); strcpy (tkptr->SizeFmtPtr = cptr, TagValue); } else if (strsame (dptr, "OSU", 3)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); if (TOUP(TagValue[0]) == 'Y' || TagValue[0] == '1') tkptr->OsuCompliant = true; else if (TOUP(TagValue[0]) == 'N' || TagValue[0] == '0') tkptr->OsuCompliant = false; else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_VALUE), FI_LI); SsiEnd (rqptr); return; } } else if (strsame (dptr, "TIMEFMT", 7)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); cptr = VmGetHeap (rqptr, strlen(TagValue)+1); strcpy (tkptr->TimeFmtPtr = cptr, TagValue); } else if (strsame (dptr, "TRACE", 5)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); if ((TOUP(TagValue[0]) == 'O' && TOUP(TagValue[1]) == 'N') || TOUP(TagValue[0]) == 'Y' || TagValue[0] == '1') { if (!rqptr->SsiTaskPtr->TraceState) { NetWriteBuffered (rqptr, NULL, "
", 5);
            rqptr->NetWriteEscapeHtml = true;
            tkptr->TraceState = true;
         }
         SsiTraceStatement (rqptr);
      }
      else
      if ((TOUP(TagValue[0]) == 'O' && TOUP(TagValue[1]) == 'F') ||
          TOUP(TagValue[0]) == 'N' || 
          TagValue[0] == '0')
      {
         if (rqptr->SsiTaskPtr->TraceState)
         {
            SsiTraceStatement (rqptr);
            rqptr->NetWriteEscapeHtml = false;
            NetWriteBuffered (rqptr, NULL, "
", 6); tkptr->TraceState = false; } } else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_VALUE), FI_LI); SsiEnd (rqptr); return; } } else if (strsame (dptr, "VERIFY", 6)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); if (TOUP(TagValue[0]) == 'Y' || TagValue[0] == '1') tkptr->TagVerify = true; else if (TOUP(TagValue[0]) == 'N' || TagValue[0] == '0') tkptr->TagVerify = false; else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_VALUE), FI_LI); SsiEnd (rqptr); return; } } else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } /* declare an AST to execute the required function */ SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Process a DCL/EXEC statement. The array at the beginning of this function provides the allowed DCL statements and their DCL command equivalents. Only innocuous DCL commands are allowed (hopefully! e.g. SHOW, WRITE SYS$OUTPUT) for the average document. For documents owned by SYSTEM and NOT WORLD WRITEABLE (for obvious reasons) the execution of any DCL command or command procedure is allowed allowing maximum flexibility for the privileged document author. */ SsiDoDcl (REQUEST_STRUCT *rqptr) { /* First element is allowed DCL command. Second is actual DCL verb/command/syntax. Third, if non-empty, idicates a file specification is required. Fourth, if 'P', indicates it is for privileged documents only! if '@', that parameter should be checked for "@file.com" hack! */ static char *SupportedDcl [] = { "cgi", "", "", "@", /* any document */ "cmd", "", "", "P", /* privileged only! */ "dir", "directory ", "", "@", /* any document */ "exec", "", "", "P", /* privileged only! */ "file", "@", "Y", "P", /* privileged only! */ "run", "run ", "Y", "P", /* privileged only! */ "say", "write sys$output", "", "@", /* any document */ "script", "", "", "@", /* any document */ "show", "show", "", "@", /* any document */ "vdir", "directory ", "", "@", /* any document */ "virtual", "@", "Y", "P", /* privileged only! */ "vrun", "run ", "Y", "P", /* privileged only! */ "", "", "", "P" /* must be terminated by empty/privileged command! */ }; int idx, status; char *cptr, *dptr, *sptr, *zptr; char DclCommand [1024], MappedFile [ODS_MAX_FILE_NAME_LENGTH+1], ScriptName [ODS_MAX_FILE_NAME_LENGTH+1], MappedScript [ODS_MAX_FILE_NAME_LENGTH+1], MappedRunTime [ODS_MAX_FILE_NAME_LENGTH+1], TagValue [SSI_STRING_SIZE]; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoDcl() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr && isspace(*dptr)) dptr++; for (idx = 0; *SupportedDcl[idx]; idx += 4) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z !&Z !&Z !&Z", SupportedDcl[idx], SupportedDcl[idx+1], SupportedDcl[idx+2], SupportedDcl[idx+3]); /* compare the user-supplied DCL command to the list of allowed */ cptr = SupportedDcl[idx]; sptr = dptr; while (*cptr && *sptr != '=' && TOUP(*cptr) == TOUP(*sptr)) { cptr++; sptr++; } if (!*cptr && *sptr == '=') break; } if (!SupportedDcl[idx][0] && !strsame (dptr = rqptr->SsiTaskPtr->StatementBeginPtr, "#exec ", 6)) { /************************/ /* unsupported command! */ /************************/ SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_DCL_UNSUPPORTED), FI_LI); SsiEnd (rqptr); return; } /*************************/ /* supported DCL command */ /*************************/ if (rqptr->rqPathSet.SsiExecPtr) { /**************************/ /* path SETing controlled */ /**************************/ cptr = rqptr->rqPathSet.SsiExecPtr; while (*cptr == '#') cptr++; while (*cptr && *cptr != '*') { sptr = SupportedDcl[idx]; while (*cptr && *cptr != ',' && *sptr && TOLO(*cptr) == TOLO(*sptr)) { cptr++; sptr++; } if (!*sptr && (!*cptr || *cptr == ',')) { cptr = "*"; break; } while (*cptr && *cptr != ',') cptr++; while (*cptr == ',') cptr++; } if (!*cptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_DCL_DISABLED), FI_LI); SsiEnd (rqptr); return; } } else if (SupportedDcl[idx+3][0] == 'P') { /******************************/ /* "privileged" DCL requested */ /******************************/ if (!Config.cfSsi.ExecEnabled) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_DCL_DISABLED), FI_LI); SsiEnd (rqptr); return; } if (!rqptr->rqPathSet.PrivSsi) { /* SYSTEM is UIC [1,4] */ if (tkptr->FileContentPtr->UicGroup != 1 || tkptr->FileContentPtr->UicMember != 4) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_DCL_NOT_SYSTEM), FI_LI); SsiEnd (rqptr); return; } /* protect word: wwggooss, protect bits: dewr, 1 denies access */ if (!(tkptr->FileContentPtr->Protection & 0x2000)) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_DCL_NOT_WORLD), FI_LI); SsiEnd (rqptr); return; } } } /****************************************************/ /* create the DCL command from array and parameters */ /****************************************************/ cptr = SupportedDcl[idx+1]; sptr = DclCommand; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; if (SupportedDcl[idx+2][0]) { /* file specification involved */ dptr += SsiGetFileSpec (rqptr, dptr, TagValue, sizeof(TagValue)); } else { /* some DCL verb/parameters or another */ dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); if (TagValue[0] && sptr > DclCommand) *sptr++ = ' '; } for (cptr = TagValue; *cptr; *sptr++ = *cptr++); *sptr = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", DclCommand); /************************/ /* check for this issue */ /************************/ if (SupportedDcl[idx+3][0] == '@') { for (cptr = TagValue; *cptr && *cptr != '@' && *cptr != '\''; cptr++); if (*cptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_DCL_UNSUPPORTED), FI_LI); SsiEnd (rqptr); return; } } /************/ /* continue */ /************/ /* by default HTML-forbidden characters in DCL output are escaped */ rqptr->NetWriteEscapeHtml = true; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (rqptr->SsiTaskPtr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "PAR=", 4)) { /* parameters to the command procedure, directory, etc. */ dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); if (TagValue[0]) *sptr++ = ' '; for (cptr = TagValue; *cptr; *sptr++ = *cptr++); *sptr = '\0'; } else if (strsame (dptr, "type=", 5)) { dptr += SsiGetTagValue (rqptr, dptr, TagValue, sizeof(TagValue)); if (TagValue[0] && strsame (TagValue, "text/html", -1)) rqptr->NetWriteEscapeHtml = tkptr->TraceState; else rqptr->NetWriteEscapeHtml = true; } else if (*dptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } if (strsame (SupportedDcl[idx], "cgi", -1) || strsame (SupportedDcl[idx], "script", -1)) { for (sptr = DclCommand; *sptr && *sptr != '?'; sptr++); if (*sptr) { SsiProblem (rqptr, "!AZ", sptr, FI_LI); SsiEnd (rqptr); return; } /* initialize with some nulls */ *(ULONGPTR)MappedFile = *(ULONGPTR)ScriptName = *(ULONGPTR)MappedScript = *(ULONGPTR)MappedRunTime = 0; cptr = MapUrl_Map (DclCommand, sizeof(DclCommand), MappedFile, sizeof(MappedFile), ScriptName, sizeof(ScriptName), MappedScript, sizeof(MappedScript), MappedRunTime, sizeof(MappedRunTime), NULL, rqptr, NULL); if (!cptr[0] && cptr[1]) { SsiProblem (rqptr, "!AZ", cptr+1, FI_LI); SsiEnd (rqptr); return; } if (!ScriptName[0]) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SCRIPT_NOT_FOUND), FI_LI); SsiEnd (rqptr); return; } rqptr->rqCgi.AbsorbHeader = rqptr->rqCgi.BufferRecords = true; for (cptr = MappedScript; *cptr && *cptr != ':'; cptr++); if (SAME2(cptr,'::')) { /* hmmm, 'ScriptName' could present a bit of a problem here */ DECnetBegin (rqptr, &SsiParse, MappedScript, MappedRunTime); } else if (ScriptName[0] == '+') { ScriptName[0] = '/'; DclBegin (rqptr, &SsiParse, NULL, ScriptName, NULL, MappedScript, MappedRunTime, NULL); } else DclBegin (rqptr, &SsiParse, NULL, ScriptName, MappedScript, NULL, MappedRunTime, NULL); } else DclBegin (rqptr, &SsiParse, DclCommand, NULL, NULL, NULL, NULL, NULL); } /*****************************************************************************/ /* Generate an "Index of" directory listing by calling DirBegin() task. */ SsiDoDir (REQUEST_STRUCT *rqptr) { int status; char *cptr, *dptr; char DirSpec [256], RealPath [256], TagValue [256]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoDir() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); if (rqptr->SsiTaskPtr->TraceState) SsiTraceStatement (rqptr); RealPath[0] = TagValue[0] = '\0'; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (rqptr->SsiTaskPtr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "FILE=", 5) || strsame (dptr, "VIRTUAL=", 8)) dptr += SsiGetFileSpec (rqptr, dptr, DirSpec, sizeof(DirSpec)); else if (strsame (dptr, "PAR=", 4)) { strcpy (TagValue, "httpd=index&"); dptr += SsiGetTagValue (rqptr, dptr, TagValue+12, sizeof(TagValue)-12); } else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } MapUrl_Map (RealPath, sizeof(RealPath), DirSpec, 0, NULL, 0, NULL, 0, NULL, 0, NULL, rqptr, NULL); DirBegin (rqptr, &SsiParse, DirSpec, TagValue, RealPath, true); } /*****************************************************************************/ /* Output a server or user-assigned variable. */ SsiDoEcho (REQUEST_STRUCT *rqptr) { int status; char String [SSI_STRING_SIZE], FormatString [SSI_STRING_SIZE]; char *cptr, *dptr, *sptr, *zptr, *VarPtr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoEcho() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); FormatString[0] = '\0'; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; /* two formats: ' */ dptr += SsiGetTagValue (rqptr, dptr, cptr = String, sizeof(String)); } else if (strsame (dptr, "value=", 6) || strsame (dptr, "VAR=", 4)) { /* format is */ dptr += SsiGetTagValue (rqptr, dptr, cptr = String, sizeof(String)); } else if (strsame (dptr, "HEADER=", 7)) { /* format is append to response header */ dptr += SsiGetTagValue (rqptr, dptr, sptr = String, sizeof(String)-2); while (*sptr) sptr++; *sptr = '\0'; ResponseDictFromString (rqptr, String, sptr - String); /* this is a stand-alone function */ continue; } else { /* format is */ zptr = (sptr = String) + sizeof(String); while (*dptr && !isspace(*dptr) && *dptr != '=' && sptr < zptr) *sptr++ = TOUP(*dptr++); if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); SsiEnd (rqptr); return; } *sptr = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", String); if (*dptr == '=') dptr += SsiGetTagValue (rqptr, dptr, FormatString, sizeof(FormatString)); else FormatString[0] = '\0'; cptr = SsiGetVar (rqptr, String, FormatString, false); } if (rqptr->SsiTaskPtr->StopProcessing) break; if (TOUP(VarPtr[5]) == 'A') { /* OSU-compliant echoes */ if (strsame (VarPtr, "VAR=\"ACCESSES_ORDINAL", 21) && !isalpha(VarPtr[21])) { tkptr->AccessOrdinal = true; tkptr->AccessSinceText[0] = tkptr->FormatString[0] = '\0'; /* if the file's access count has been previously determined */ if (tkptr->AccessCount) SsiAccessesOutput (rqptr); else SsiAccessesOpen (rqptr); /* generated asynchronously */ return; } if (strsame (VarPtr, "VAR=\"ACCESSES", 13) && !isalpha(VarPtr[13])) { tkptr->AccessOrdinal = false; tkptr->AccessSinceText[0] = tkptr->FormatString[0] = '\0'; /* if the file's access count has been previously determined */ if (tkptr->AccessCount) SsiAccessesOutput (rqptr); else SsiAccessesOpen (rqptr); /* generated asynchronously */ return; } } if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex]) { NetWriteBuffered (rqptr, NULL, cptr, strlen(cptr)); tkptr->TraceOutput = true; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Flow control statement. If the current flow control is executing then it now disallowed. If the current flow control structure (including "#if"s, "#orif"s and other "#elif"s) have not allowed execution then evaluate the conditional and allow execution if true. */ SsiDoElif (REQUEST_STRUCT *rqptr) { SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoElif() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; tkptr->SuppressLine = true; /* if previous flow control was not an "#if" or "#elif" */ if (tkptr->FlowControlState[tkptr->FlowControlIndex] != SSI_STATE_IF && tkptr->FlowControlState[tkptr->FlowControlIndex] != SSI_STATE_ELIF) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); return; } tkptr->FlowControlState[tkptr->FlowControlIndex] = SSI_STATE_ELIF; if (tkptr->FlowControlHasExecuted[rqptr->SsiTaskPtr->FlowControlIndex]) { /* if flow control has already output just continue parsing */ tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = false; SysDclAst (&SsiParse, rqptr); return; } /* if the parent level is executing and it evaluates true then execute */ if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex-1]) { if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex] = tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = SsiEvaluate (rqptr); } /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Flow control statement. If the current flow control structure (including "#if"s, "#orif"s and/or "#elif"s) have not allowed execution then this will. */ SsiDoElse (REQUEST_STRUCT *rqptr) { SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoElse() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; tkptr->SuppressLine = true; /* if previous flow control was not an "#if" or "#elif" */ if (tkptr->FlowControlState[tkptr->FlowControlIndex] != SSI_STATE_IF && tkptr->FlowControlState[tkptr->FlowControlIndex] != SSI_STATE_ELIF) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); return; } tkptr->FlowControlState[tkptr->FlowControlIndex] = SSI_STATE_ELSE; /* if there has been no output so far */ if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex-1] && !tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex]) { if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex] = tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = true; } else tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = false; /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* OSU-compliant, marking the end of an #includable "part". */ SsiDoEnd (REQUEST_STRUCT *rqptr) { char String [SSI_INCLUDE_PART_MAX+1]; char *cptr, *dptr, *sptr, *zptr; SSI_TASK *tkptr, *ParentDocumentTaskPtr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoEnd() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (!tkptr->OsuCompliant || !tkptr->CurrentPart[0]) { /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); return; } tkptr->SuppressLine = true; if (tkptr->TraceState) SsiTraceStatement (rqptr); dptr = tkptr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr && isspace(*dptr)) dptr++; while (*dptr) { while (*dptr && (*dptr == '\"' || isspace(*dptr))) dptr++; if (!*dptr) break; zptr = (sptr = String) + sizeof(String); while (*dptr && *dptr != '\"' && !isspace(*dptr) && sptr < zptr) *sptr++ = *dptr++; if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); SsiEnd (rqptr); return; } *sptr = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z !&Z", String, tkptr->CurrentPart); if (strcmp (String, tkptr->CurrentPart)) continue; /* end of the #included part */ tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex] = tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = false; break; } tkptr->SuppressLine = true; if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex]) { /* not yet found, declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); return; } /* if previous flow control was not a #begin */ if (tkptr->FlowControlState[tkptr->FlowControlIndex] != SSI_STATE_BEGIN) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); return; } if (tkptr->FlowControlIndex) { tkptr->FlowControlIndex--; /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); return; } SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); } /*****************************************************************************/ /* Flow control statement. */ SsiDoEndif (REQUEST_STRUCT *rqptr) { SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoEndIf()"); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->SuppressLine = true; if (tkptr->FlowControlIndex) { tkptr->FlowControlIndex--; /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); return; } SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); } /*****************************************************************************/ /* Exit from current document processing here! */ SsiDoExit (REQUEST_STRUCT *rqptr) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoExit()"); if (rqptr->SsiTaskPtr->TraceState) SsiTraceStatement (rqptr); SsiEnd (rqptr); } /*****************************************************************************/ /* Output the specified file's creation date and time. */ SsiDoFCreated (REQUEST_STRUCT *rqptr) { char *dptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoFCreated() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->ScratchFileName[0] = tkptr->FormatString[0] = '\0'; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (rqptr->SsiTaskPtr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "FILE=", 5) || strsame (dptr, "VIRTUAL=", 8)) dptr += SsiGetFileSpec (rqptr, dptr, tkptr->ScratchFileName, sizeof(tkptr->ScratchFileName)); else if (strsame (dptr, "FMT=", 4)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->FormatString, sizeof(tkptr->FormatString)); else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } SsiFileDetails (rqptr, FILE_FCREATED); } /*****************************************************************************/ /* Output the specified file's last modification date and time. */ SsiDoFLastMod (REQUEST_STRUCT *rqptr) { char *dptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoFLastMod() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->ScratchFileName[0] = tkptr->FormatString[0] = '\0'; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (rqptr->SsiTaskPtr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "FILE=", 5) || strsame (dptr, "VIRTUAL=", 8)) dptr += SsiGetFileSpec (rqptr, dptr, tkptr->ScratchFileName, sizeof(tkptr->ScratchFileName)); else if (strsame (dptr, "FMT=", 4)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->FormatString, sizeof(tkptr->FormatString)); else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } SsiFileDetails (rqptr, FILE_FLASTMOD); } /*****************************************************************************/ /* Output the specified file's size. */ SsiDoFSize (REQUEST_STRUCT *rqptr) { char *dptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoFSize() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->ScratchFileName[0] = tkptr->FormatString[0] = '\0'; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (rqptr->SsiTaskPtr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "FILE=", 5) || strsame (dptr, "VIRTUAL=", 8)) dptr += SsiGetFileSpec (rqptr, dptr, tkptr->ScratchFileName, sizeof(tkptr->ScratchFileName)); else if (strsame (dptr, "FMT=", 4)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->FormatString, sizeof(tkptr->FormatString)); else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } SsiFileDetails (rqptr, FILE_FSIZE); } /*****************************************************************************/ /* Directive for changing/generating HTTP response headers and/or fields. */ SsiDoModified (REQUEST_STRUCT *rqptr) { static char Expires [64] = "Expires: ", LastModified [64] = "Last-Modified: "; /* point immediately after the hard-wired strings */ static char *ExpiresPtr = Expires + 9, *LastModifiedPtr = LastModified + 15; /* available == size - start - '\r' - '\n' - '\0' */ static int ExpiresAvailable = sizeof(Expires) - 12; BOOL DoIfModifiedSince, DoLastModified; int status; char *dptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoModified() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); DoLastModified = DoIfModifiedSince = false; *ExpiresPtr = '\0'; tkptr->ScratchFileName[0] = '\0'; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (tkptr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "EXPIRES=", 8)) dptr += SsiGetTagValue (rqptr, dptr, ExpiresPtr, ExpiresAvailable); else if (strsame (dptr, "FILE=", 5) || strsame (dptr, "VIRTUAL=", 8)) dptr += SsiGetFileSpec (rqptr, dptr, tkptr->ScratchFileName, sizeof(tkptr->ScratchFileName)); else if (strsame (dptr, "FMT=", 4)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->FormatString, sizeof(tkptr->FormatString)); else if (strsame (dptr, "IF-MODIFIED-SINCE", 17)) { dptr += 17; DoLastModified = false; DoIfModifiedSince = true; } else if (strsame (dptr, "LAST-MODIFIED", 13)) { dptr += 13; DoLastModified = true; DoIfModifiedSince = false; } else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } if (DoIfModifiedSince) { /******************************/ /* check "If-Modified-Since:" */ /******************************/ if (!tkptr->LastModifiedTime64) { /* no files' RDTs have been checked */ SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_VALUE), FI_LI); SsiEnd (rqptr); return; } if (!rqptr->NotFromCache) { if (!HttpIfModifiedSince (rqptr, &tkptr->LastModifiedTime64, -1)) { /****************/ /* not modified */ /****************/ SsiEnd (rqptr); return; } } /******************/ /* modified since */ /******************/ /* supply a "Last-Modified:" response header */ if (VMSnok (status = HttpGmTimeString (LastModifiedPtr, &tkptr->LastModifiedTime64))) { SsiProblem (rqptr, "!&m", status, FI_LI); SsiEnd (rqptr); return; } ResponseDictFromString (rqptr, LastModified, -1); /* now just continue on */ SysDclAst (&SsiParse, rqptr); return; } if (DoLastModified) { /*****************************/ /* explicit "Last-Modified:" */ /*****************************/ if (!tkptr->LastModifiedTime64) { /* no files' RDTs have been checked */ SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_VALUE), FI_LI); SsiEnd (rqptr); return; } if (VMSnok (status = HttpGmTimeString (LastModifiedPtr, &tkptr->LastModifiedTime64))) { SsiProblem (rqptr, "!&m", status, FI_LI); SsiEnd (rqptr); return; } ResponseDictFromString (rqptr, LastModified, -1); /* just continue on */ SysDclAst (&SsiParse, rqptr); return; } if (*ExpiresPtr) { /***********************/ /* explicit "Expires:" */ /***********************/ if (*ExpiresPtr == '0') { /* pre-expire, so let's try really hard! */ ResponseDictFromString (rqptr, "Expires: Fri, 13 Jan 1978 14:00:00 GMT\r\n\ Cache-Control: no-cache, no-store\r\n\ Pragma: no-cache\r\n", -1); } else ResponseDictFromString (rqptr, Expires, -1); /* just continue on */ SysDclAst (&SsiParse, rqptr); return; } if (tkptr->ScratchFileName[0]) { /******************/ /* get file's RDT */ /******************/ /* get the revision timestamp of the specified file */ SsiFileDetails (rqptr, FILE_LAST_MODIFIED); return; } /* hmmm, nothing specified, check the modification date of this file */ strcpy (tkptr->ScratchFileName, tkptr->FileContentPtr->FileName); SsiFileDetails (rqptr, FILE_LAST_MODIFIED); return; } /*****************************************************************************/ /* Flow control statement. If the parent level is executing evalutate the conditional and allow execution if true. */ SsiDoIf (REQUEST_STRUCT *rqptr) { SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoIf() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; tkptr->SuppressLine = true; tkptr->FlowControlIndex++; if (tkptr->FlowControlIndex > SSI_MAX_FLOW_CONTROL) { tkptr->FlowControlIndex = 0; SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); return; } tkptr->FlowControlState[tkptr->FlowControlIndex] = SSI_STATE_IF; /* if the parent level is executing and it evaluates true then execute */ if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex-1]) { if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex] = tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = SsiEvaluate (rqptr); } else tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex] = tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = false; /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Include the contents of the specified file by calling FileBegin() task. The tag content="text/..." makes any file extension acceptable as the text type an presents it as that ("text/html" is included unescaped, "text/plain" escaped). This makes a file with any extension accepted as text! The tag type="text/plain" (older variant) forces a "text/html" file to be escaped and presented as a plain text file. */ SsiDoInclude (REQUEST_STRUCT *rqptr) { BOOL ok; int status; char *cptr, *dptr, *sptr, *zptr; char FileContent [256], FileFormat [256], FileName [256]; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoInclude() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->IncludePart[0] = tkptr->TheFileNameVar[0] = '\0'; FileContent[0] = FileName[0] = FileFormat[0] = '\0'; dptr = tkptr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (tkptr->StopProcessing) break; while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "FILE=", 5) || strsame (dptr, "VIRTUAL=", 8)) dptr += SsiGetFileSpec (rqptr, dptr, FileName, sizeof(FileName)); else if (strsame (dptr, "type=", 5)) dptr += SsiGetTagValue (rqptr, dptr, FileFormat, sizeof(FileFormat)); else if (strsame (dptr, "content=", 8)) dptr += SsiGetTagValue (rqptr, dptr, FileContent, sizeof(FileContent)); else if (strsame (dptr, "FMT=", 4)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->FormatString, sizeof(tkptr->FormatString)); else if (tkptr->OsuCompliant && strsame (dptr, "PART=", 5)) dptr += SsiGetTagValue (rqptr, dptr, tkptr->IncludePart, sizeof(tkptr->IncludePart)); else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } } if (tkptr->StopProcessing) { SsiEnd (rqptr); return; } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z !&Z !&Z", FileName, FileContent, FileFormat); if (tkptr->OsuCompliant) { /* OSU only includes other SSI files */ cptr = ConfigContentTypeSsi; } else if (!(cptr = FileContent)[0]) { /* find the file extension and set the content type */ for (cptr = FileName; *cptr && *cptr != ']'; cptr++); while (*cptr && *cptr != '.') cptr++; cptr = ConfigContentType (NULL, cptr); } if (!strsame (cptr, "text/", 5)) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_INCLUDE_NOT_TEXT), FI_LI); SsiEnd (rqptr); return; } /* set the variable file name */ strcpy (tkptr->TheFileNameVar, FileName); if (FileFormat[0]) cptr = FileFormat; if (ConfigSameContentType (cptr, "text/plain", -1)) { /* it's plain text, so make it look like it, with escaped HTML */ FileSetPreTag (rqptr, true); FileSetEscapeHtml (rqptr, true); } if (ConfigSameContentType (cptr, ConfigContentTypeSsi, -1)) { /* buffer the file contents and pass them to the SSI engine */ FileSetContentHandler (rqptr, &SsiBegin, SsiSizeMax); } FileSetCacheAllowed (rqptr, true); FileSetAuthorizePath (rqptr, true); /* ultimately return to the SSI engine parser */ FileBegin (rqptr, &SsiParse, &SsiIncludeError, NULL, FileName, cptr); } /*****************************************************************************/ /* */ SsiIncludeError (REQUEST_STRUCT *rqptr) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiIncludeError() !&F !&Z", &SsiIncludeError, rqptr->SsiTaskPtr->FormatString); if (rqptr->SsiTaskPtr->FormatString[0] == '?') { /* reset the file variable data to indicate it was not found */ rqptr->SsiTaskPtr->TheFileNameVar[0] = '\0'; /* declare an AST to continue processing */ SysDclAst (&SsiParse, rqptr); return; } else { /* report the error and stop processing */ SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_INCLUDE_ACCESS), FI_LI); SsiEnd (rqptr); return; } } /*****************************************************************************/ /* Flow control statement. If the preceding control statement was an "#if" or "#elif" and it did not evaluate true then this allows another chance to execute this segment. It is essentially an OR on the last evalution. The evaluation here is not performed if the previous allowed execution. */ SsiDoOrif (REQUEST_STRUCT *rqptr) { SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoOrIf() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; tkptr->SuppressLine = true; /* if previous flow control was not an "#if" or "#elif" */ if (tkptr->FlowControlState[tkptr->FlowControlIndex] != SSI_STATE_IF && tkptr->FlowControlState[tkptr->FlowControlIndex] != SSI_STATE_ELIF) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_FLOW_CONTROL), FI_LI); SsiEnd (rqptr); return; } /* if the parent level is executing and the current is not then evaluate */ if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex-1] && !tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex]) { if (tkptr->TraceState) SsiTraceStatement (rqptr); tkptr->FlowControlHasExecuted[tkptr->FlowControlIndex] = tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex] = SsiEvaluate (rqptr); } /* declare an AST to execute the next function */ SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Print all server and user-assigned variables. Usually used for no more than document debugging. Variable names and values have HTML- forbidden characters escaped. */ SsiDoPrintEnv (REQUEST_STRUCT *rqptr) { BOOL EscapeHtmlBuffer; int length; char *cptr, *sptr; DICT_ENTRY_STRUCT *denptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoPrintEnv()"); tkptr = rqptr->SsiTaskPtr; if (tkptr->TraceState) SsiTraceStatement (rqptr); if (tkptr->TraceState) NetWriteBuffered (rqptr, NULL, "\n\n", 2); else NetWriteBuffered (rqptr, NULL, "
\n\n", 6);
   EscapeHtmlBuffer = rqptr->NetWriteEscapeHtml;
   rqptr->NetWriteEscapeHtml = true;
   tkptr->TraceOutput = true;

   /* SSI variables */
   cptr = SsiGetServerVar (rqptr, sptr = "COMPLIANCE", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "CREATED", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "DATE_GMT", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "DATE_LOCAL", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "DOCUMENT_DEPTH", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "DOCUMENT_NAME", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "DOCUMENT_ROOT", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "DOCUMENT_URI", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);

   if (rqptr->RedirectErrorStatusCode)
   {
      /* SSI variables that are only present during an error report */
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_LINE", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_MODULE", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_REPORT", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_REPORT2", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_REPORT3", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_STATUS_CLASS", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_STATUS_CODE", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_STATUS_EXPLANATION", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_STATUS_TEXT", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
      cptr = SsiGetServerVar (rqptr, sptr = "ERROR_TYPE", NULL);
      SsiPrintEnvVar (rqptr, sptr, cptr);
   }

   cptr = SsiGetServerVar (rqptr, sptr = "FILE_NAME", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   /* OSU-compliant variable */
   cptr = SsiGetServerVar (rqptr, sptr = "HW_NAME", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "LAST_MODIFIED", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "QUERY_STRING_UNESCAPED", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "PARENT_FILE_NAME", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   /* OSU-compliant variable */
   cptr = SsiGetServerVar (rqptr, sptr = "SERVER_VERSION", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "THE_FILE_NAME", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   cptr = SsiGetServerVar (rqptr, sptr = "THIS_FILE_NAME", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);
   /* OSU-compliant variable */
   cptr = SsiGetServerVar (rqptr, sptr = "VMS_VERSION", NULL);
   SsiPrintEnvVar (rqptr, sptr, cptr);

   /* CGI variables */
   NetWriteBuffered (rqptr, NULL, "\n", 1);
   cptr = tkptr->CgiBufferPtr;
   for (;;)
   {
      if (!(length = *(USHORTPTR)cptr)) break;
      SsiPrintEnvVar (rqptr, cptr+sizeof(short)+DclCgiVariablePrefixLength,
                      NULL);
      cptr += length + sizeof(short);
   }

   /* user variables */
   DictIterate (tkptr->UserDictPtr, NULL);
   while (denptr = DictIterate (tkptr->UserDictPtr, "*"))
      SsiPrintEnvVar (rqptr, DICT_GET_KEY(denptr), DICT_GET_VALUE(denptr));

   rqptr->NetWriteEscapeHtml = EscapeHtmlBuffer;
   if (!tkptr->TraceState)
      NetWriteBuffered (rqptr, NULL, "
", 6); SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Simply do the "printenv" of a single variable for SsiDoPrintEnv(). */ SsiPrintEnvVar ( REQUEST_STRUCT *rqptr, char* VarName, char* VarValue ) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiPrintEnvVar() !AZ=!AZ", VarName, VarValue); NetWriteBuffered (rqptr, NULL, VarName, strlen(VarName)); if (VarValue) { NetWriteBuffered (rqptr, NULL, "=", 1); NetWriteBuffered (rqptr, NULL, VarValue, strlen(VarValue)); } NetWriteBuffered (rqptr, NULL, "\n", 1); } /*****************************************************************************/ /* Set a user variable. Server variables cannot be set. Existing variable names are of course set to the new value. New variables are created ex nihlo and added to a simple linked list. */ SsiDoSet (REQUEST_STRUCT *rqptr) { BOOL PreTagFileContents; char *cptr, *dptr; char VarName [256], VarValue [SSI_STRING_SIZE]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoSet() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); rqptr->SsiTaskPtr->SuppressLine = true; if (rqptr->SsiTaskPtr->TraceState) SsiTraceStatement (rqptr); dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; VarName[0] = VarValue[0]= '\0'; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (strsame (dptr, "VAR=", 4)) { dptr += SsiGetTagValue (rqptr, dptr, VarName, sizeof(VarName)); continue; } if (strsame (dptr, "value=", 6)) dptr += SsiGetTagValue (rqptr, dptr, VarValue, sizeof(VarValue)); else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); SsiEnd (rqptr); return; } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z !&Z", VarName, VarValue); if (rqptr->SsiTaskPtr->StopProcessing) { SsiEnd (rqptr); return; } if (!VarName[0]) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_VARIABLE), FI_LI); SsiEnd (rqptr); return; } for (cptr = VarName; *cptr; cptr++) if (!isalnum(*cptr) && *cptr != '_' && *cptr != '$') break; if (*cptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_VARIABLE), FI_LI); SsiEnd (rqptr); return; } if (SsiGetServerVar (rqptr, VarName, NULL)) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_VARIABLE), FI_LI); SsiEnd (rqptr); return; } DictInsert (rqptr->SsiTaskPtr->UserDictPtr, DICT_TYPE_SSI, VarName, -1, VarValue, -1); if (rqptr->SsiTaskPtr->TraceState) SsiTraceSetVar (rqptr, VarName, VarValue); /* ready for the next round (if any) */ VarValue[0]= '\0'; } SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Stop processing the document here! */ SsiDoStop (REQUEST_STRUCT *rqptr) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoStop()"); if (rqptr->SsiTaskPtr->TraceState) SsiTraceStatement (rqptr); rqptr->SsiTaskPtr->StopProcessing = true; SsiEnd (rqptr); } /*****************************************************************************/ /* Turn the SSI trace on or off ("#config trace=1" is prefered, this is kept for backward compatibility). */ SsiDoTrace (REQUEST_STRUCT *rqptr) { char *cptr, *dptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiDoTrace() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr && isspace(*dptr)) dptr++; for (cptr = dptr; *cptr && !isspace(*cptr); cptr++); *cptr = '\0'; if ((TOUP(dptr[0]) == 'O' && TOUP(dptr[1]) == 'N') || TOUP(dptr[0]) == 'Y' || dptr[0] == '1') { if (!rqptr->SsiTaskPtr->TraceState) { NetWriteBuffered (rqptr, NULL, "
", 5);
         rqptr->NetWriteEscapeHtml = true;
         rqptr->SsiTaskPtr->TraceState = true;
      }
      SsiTraceStatement (rqptr);
   }
   else
   if ((TOUP(dptr[0]) == 'O' && TOUP(dptr[1]) == 'F') ||
       TOUP(dptr[0]) == 'N' || 
       dptr[0] == '0')
   {
      if (rqptr->SsiTaskPtr->TraceState)
      {
         SsiTraceStatement (rqptr);
         rqptr->NetWriteEscapeHtml = false;
         NetWriteBuffered (rqptr, NULL, "
", 6); rqptr->SsiTaskPtr->TraceState = false; } } else if (rqptr->SsiTaskPtr->TraceState) SsiTraceStatement (rqptr); SysDclAst (&SsiParse, rqptr); } /*****************************************************************************/ /* Display the current SSI file record. */ SsiTraceLine ( REQUEST_STRUCT *rqptr, char *cptr ) { int status; char ch; char *sptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiTraceLine()"); for (sptr = cptr; *sptr && *sptr != '\n'; sptr++); ch = *sptr; *sptr = '\0'; status = FaoToNet (rqptr, "!&?\n\r\r[!4ZL:!UL]!&;AZ\n", rqptr->SsiTaskPtr->TraceOutput, rqptr->SsiTaskPtr->LineNumber, rqptr->SsiTaskPtr->FlowControlIndex, cptr); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); *sptr = ch; rqptr->SsiTaskPtr->TraceOutput = false; } /*****************************************************************************/ /* Display the current SSI statement. */ SsiTraceStatement (REQUEST_STRUCT *rqptr) { int status; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiTraceStatement()"); status = FaoToNet (rqptr, "[!&;AZ]", rqptr->SsiTaskPtr->StatementBeginPtr); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->SsiTaskPtr->TraceOutput = true; } /*****************************************************************************/ /* Display the name and value of a variable as it is retrieved. */ SsiTraceGetVar ( REQUEST_STRUCT *rqptr, char *VarName, char *VarValue ) { int status; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiTraceGetVar() !AZ=!AZ", VarName, VarValue); status = FaoToNet (rqptr, "[!&;AZ:!&;AZ]", VarName, VarValue); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->SsiTaskPtr->TraceOutput = true; } /*****************************************************************************/ /* Display the name and value of a variable as it is stored. */ SsiTraceSetVar ( REQUEST_STRUCT *rqptr, char *VarName, char *VarValue ) { int status; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiTraceSetVar()"); status = FaoToNet (rqptr, "[!&;AZ=!&;AZ]", VarName, VarValue); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->SsiTaskPtr->TraceOutput = true; } /*****************************************************************************/ /* First search the server-assigned variables, then the user-assigned variables. If found return a pointer to a symbol's value string. If no such symbol found return a NULL. */ char* SsiGetVar ( REQUEST_STRUCT *rqptr, char *VarName, char *Format, BOOL CheckOnly ) { char *ValuePtr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiGetVar() !AZ \'!AZ\' !UL", VarName, Format, CheckOnly); ValuePtr = SsiGetServerVar (rqptr, VarName, Format); if (rqptr->SsiTaskPtr->StopProcessing) return (ValuePtr); if (ValuePtr) goto SsiGetVarReturn; ValuePtr = SsiGetUserVar (rqptr, VarName); if (rqptr->SsiTaskPtr->StopProcessing) return (ValuePtr); if (ValuePtr) goto SsiGetVarReturn; if (CheckOnly) goto SsiGetVarReturn; /* variable not found */ ValuePtr = MsgFor(rqptr,MSG_SSI_VARIABLE_NOT_FOUND); SsiGetVarReturn: if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", ValuePtr); return (ValuePtr); } /*****************************************************************************/ /* Return a pointer to the value of a server-assigned variable, NULL if no such variable. The calling routine might want to check for an error message being generated by bad format or time from SsiTimeString(). */ char* SsiGetServerVar ( REQUEST_STRUCT *rqptr, char *VarName, char *VarParam ) { #define SIZEOF_TIME_STRING 64 static $DESCRIPTOR (NumberFaoDsc, "!UL\0"); static $DESCRIPTOR (StringFaoDsc, "!AZ\0"); static $DESCRIPTOR (ReportFaoDsc, "!AZ ... !AZ\0"); static $DESCRIPTOR (Report2FaoDsc, "\0"); static $DESCRIPTOR (StringDsc, ""); int status, DocumentDepth, StatusCode; int64 Time64; unsigned short Length; char *cptr, *sptr, *zptr; char String [SSI_STRING_SIZE], LoCaseName [256]; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiGetServerVar() !AZ \'!AZ\'", VarName, VarParam); tkptr = rqptr->SsiTaskPtr; zptr = (sptr = LoCaseName) + sizeof(LoCaseName)-1; for (cptr = VarName; *cptr && sptr < zptr; *sptr++ = tolower(*cptr++)); *sptr = '\0'; cptr = LoCaseName; switch (*cptr) { case 'c' : /* for all these also match the trailing null */ if (MATCH11 (cptr, "compliance")) { cptr = VmGetHeap (rqptr, 16); StringDsc.dsc$a_pointer = cptr; StringDsc.dsc$w_length = 15; sys$fao (&NumberFaoDsc, &Length, &StringDsc, tkptr->ComplianceLevel); return (cptr); } if (MATCH8 (cptr, "created")) { cptr = VmGetHeap (rqptr, SIZEOF_TIME_STRING); SsiTimeString (rqptr, &tkptr->RootDocumentTaskPtr->FileContentPtr->CdtTime64, VarParam, cptr, SIZEOF_TIME_STRING); return (cptr); } break; case 'd' : if (MATCH11 (cptr, "date_local")) { cptr = VmGetHeap (rqptr, SIZEOF_TIME_STRING); SsiTimeString (rqptr, NULL, VarParam, cptr, SIZEOF_TIME_STRING); return (cptr); } if (MATCH9 (cptr, "date_gmt")) { sys$gettim (&Time64); TimeAdjustToGMT (&Time64); cptr = VmGetHeap (rqptr, SIZEOF_TIME_STRING+5); SsiTimeString (rqptr, &Time64, VarParam, cptr, SIZEOF_TIME_STRING); strcat (cptr, " GMT"); return (cptr); } if (MATCH15 (cptr, "document_depth")) { if (tkptr->DocumentDepthPtr) return (tkptr->DocumentDepthPtr); StringDsc.dsc$a_pointer = String; StringDsc.dsc$w_length = sizeof(String)-1; DocumentDepth = LIST_GET_COUNT (&rqptr->SsiTaskList); sys$fao (&NumberFaoDsc, &Length, &StringDsc, DocumentDepth); tkptr->DocumentDepthPtr = cptr = VmGetHeap (rqptr, Length); strcpy (cptr, String); return (cptr); } if (MATCH14 (cptr, "document_name")) return (SsiGetCgiVar (rqptr, "PATH_TRANSLATED")); if (MATCH14 (cptr, "document_root")) { if (tkptr->DocumentRootPtr) return (tkptr->DocumentRootPtr); tkptr->DocumentRootPtr = sptr = VmGetHeap (rqptr, rqptr->rqHeader.PathInfoLength+1); zptr = NULL; cptr = rqptr->rqHeader.PathInfoPtr; while (*cptr) { if (*cptr == '/') zptr = sptr; *sptr++ = *cptr++; } if (zptr) *(zptr+1) = '\0'; else *tkptr->DocumentRootPtr = '\0'; return (tkptr->DocumentRootPtr); } if (MATCH13 (cptr, "document_uri")) return (rqptr->rqHeader.PathInfoPtr); break; case 'e' : if (!rqptr->RedirectErrorStatusCode) return (NULL); StringDsc.dsc$a_pointer = String; StringDsc.dsc$w_length = sizeof(String)-1; if (MATCH11 (cptr, "error_line")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_LINE")); if (MATCH13 (cptr, "error_module")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_MODULE")); if (MATCH13 (cptr, "error_report")) { if (!(cptr = SsiGetCgiVar (rqptr, "FORM_ERROR_TEXT"))) return (NULL); /* return if it's an ErrorGeneral() error */ if (!(sptr = SsiGetCgiVar (rqptr, "FORM_ERROR_ABOUT"))) return (cptr); if (!*sptr) return (cptr); sys$fao (&ReportFaoDsc, &Length, &StringDsc, cptr, sptr); cptr = VmGetHeap (rqptr, Length); strcpy (cptr, String); return (cptr); } if (MATCH14 (cptr, "error_report2")) { if (!(cptr = SsiGetCgiVar (rqptr, "FORM_ERROR_VMS"))) return (""); /* return if it's an ErrorGeneral() error */ if (!(sptr = SsiGetCgiVar (rqptr, "FORM_ERROR_ABOUT2"))) return (""); if (!*sptr) return (cptr); sys$fao (&Report2FaoDsc, &Length, &StringDsc, cptr, sptr); cptr = VmGetHeap (rqptr, Length); strcpy (cptr, String); return (cptr); } if (MATCH14 (cptr, "error_report3")) { if (!(cptr = SsiGetCgiVar (rqptr, "FORM_ERROR_TEXT2"))) return (""); return (cptr); } if (MATCH11 (cptr, "error_type")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_TYPE")); if (MATCH16 (cptr, "error_status_code")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_STATUS")); if (MATCH16 (cptr, "error_status_class")) { if (!(cptr = SsiGetCgiVar (rqptr, "FORM_ERROR_STATUS"))) return (NULL); StatusCode = atoi(cptr); sys$fao (&NumberFaoDsc, &Length, &StringDsc, StatusCode / 100); cptr = VmGetHeap (rqptr, Length); strcpy (cptr, String); return (cptr); } if (MATCH16 (cptr, "error_status_text")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_STATUS_TEXT")); if (MATCH16 (cptr, "error_status_explanation")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_STATUS_EXPLANATION")); if (MATCH11 (cptr, "error_type")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_TYPE")); if (MATCH10 (cptr, "error_uri")) return (SsiGetCgiVar (rqptr, "FORM_ERROR_URI")); break; case 'f' : if (MATCH10 (cptr, "file_name")) return (SsiGetCgiVar (rqptr, "PATH_TRANSLATED")); break; case 'h' : /* an OSU-compliant variable */ if (MATCH8 (cptr, "hw_name")) return (SysInfo.HwName); break; case 'l' : if (MATCH13 (cptr, "last_modified")) { cptr = VmGetHeap (rqptr, SIZEOF_TIME_STRING); SsiTimeString (rqptr, &tkptr->RootDocumentTaskPtr->FileContentPtr->RdtTime64, VarParam, cptr, SIZEOF_TIME_STRING); return (cptr); } break; case 'g' : /* an OSU-compliant variable */ if (MATCH7 (cptr, "getenv")) { if (!(sptr = getenv (VarParam))) return (NULL); cptr = VmGetHeap (rqptr, Length = strlen(sptr)+1); memcpy (cptr, sptr, Length); return (cptr); } break; case 'q' : if (MATCH16 (cptr, "query_string_unescaped")) return (SsiGetCgiVar (rqptr, "QUERY_STRING")); break; case 'p' : if (MATCH16 (cptr, "parent_file_name")) { if (tkptr->ParentDocumentTaskPtr) return (tkptr->ParentDocumentTaskPtr->FileContentPtr->FileName); else return (""); } break; case 's' : /* an OSU-compliant variable */ if (MATCH15 (cptr, "server_version")) return (SsiGetCgiVar (rqptr, "SERVER_SOFTWARE")); break; case 't' : if (MATCH14 (cptr, "the_file_name")) return (tkptr->TheFileNameVar); if (MATCH15 (cptr, "this_file_name")) return (tkptr->FileContentPtr->FileName); break; case 'v' : /* an OSU-compliant variable */ if (MATCH12 (cptr, "vms_version")) return (SysInfo.Version); break; } return (SsiGetCgiVar (rqptr, cptr)); } /*****************************************************************************/ /* Search the CGI-assigned variables. If found return a pointer to a symbol's value string. If no such symbol found return a NULL. */ char* SsiGetCgiVar ( REQUEST_STRUCT *rqptr, char *VarName ) { unsigned short Length; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiGetCgiVar() !&Z", VarName); cptr = rqptr->SsiTaskPtr->CgiBufferPtr; for (;;) { if (!(Length = *(USHORTPTR)cptr)) break; for (sptr = cptr+sizeof(short)+DclCgiVariablePrefixLength; *sptr && *sptr != '='; sptr++); *sptr = '\0'; if (strsame (cptr+sizeof(short)+DclCgiVariablePrefixLength, VarName, -1)) { *sptr = '='; break; } *sptr = '='; cptr += Length + sizeof(short); } if (Length) { /* found */ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", sptr+1); return (sptr+1); } /* not found */ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", NULL); return (NULL); } /*****************************************************************************/ /* Search the user-assigned variables. If found return a pointer to a symbol's value string. If no such symbol found return a NULL. */ char* SsiGetUserVar ( REQUEST_STRUCT *rqptr, char *VarName ) { DICT_STRUCT *dicptr; DICT_ENTRY_STRUCT *denptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiGetUserVar() !&Z", VarName); dicptr = rqptr->SsiTaskPtr->UserDictPtr; if (denptr = DictLookup (dicptr, DICT_TYPE_SSI, VarName, -1)) return (DICT_GET_VALUE(denptr)); return (NULL); } /*****************************************************************************/ /* Used by flow-control statements that do an evaluation to make a decision. */ BOOL SsiEvaluate (REQUEST_STRUCT *rqptr) { BOOL NegateResult, Result; int NumberOne, NumberTwo; char *dptr; char ValueOne [SSI_STRING_SIZE], ValueTwo [SSI_STRING_SIZE]; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiEvaluate() !&Z", rqptr->SsiTaskPtr->StatementBeginPtr); tkptr = rqptr->SsiTaskPtr; Result = false; dptr = rqptr->SsiTaskPtr->StatementBeginPtr; while (*dptr && !isspace(*dptr)) dptr++; while (*dptr) { if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", dptr); if (rqptr->SsiTaskPtr->StopProcessing) return (false); while (*dptr && isspace(*dptr)) dptr++; if (!*dptr) break; if (*dptr == '!') { dptr++; NegateResult = true; } else NegateResult = false; if (strsame (dptr, "value=", 6) || strsame (dptr, "VAR=", 4) || strsame (dptr, "PAR=", 4)) { dptr += SsiGetTagValue (rqptr, dptr, ValueOne, sizeof(ValueOne)); if (tkptr->StopProcessing) { Result = false; break; } if (isdigit(ValueOne[0])) Result = atoi(ValueOne); else Result = ValueOne[0]; if (NegateResult) Result = !Result; continue; } if (strsame (dptr, "SRCH=", 5)) { dptr += SsiGetTagValue (rqptr, dptr, ValueTwo, sizeof(ValueTwo)); if (tkptr->StopProcessing) { Result = false; break; } Result = StringMatchGreedy (rqptr, ValueOne, ValueTwo); } else if (strsame (dptr, "EQS=", 4)) { dptr += SsiGetTagValue (rqptr, dptr, ValueTwo, sizeof(ValueTwo)); if (tkptr->StopProcessing) { Result = false; break; } Result = strsame (ValueOne, ValueTwo, -1); } else if (strsame (dptr, "EQ=", 3)) { dptr += SsiGetTagValue (rqptr, dptr, ValueTwo, sizeof(ValueTwo)); if (tkptr->StopProcessing) { Result = false; break; } NumberOne = NumberTwo = 0; NumberOne = atoi(ValueOne); NumberTwo = atoi(ValueTwo); Result = (NumberOne == NumberTwo); } else if (strsame (dptr, "GT=", 3)) { dptr += SsiGetTagValue (rqptr, dptr, ValueTwo, sizeof(ValueTwo)); if (tkptr->StopProcessing) { Result = false; break; } NumberOne = NumberTwo = 0; NumberOne = atoi(ValueOne); NumberTwo = atoi(ValueTwo); Result = (NumberOne > NumberTwo); } else if (strsame (dptr, "LT=", 3)) { dptr += SsiGetTagValue (rqptr, dptr, ValueTwo, sizeof(ValueTwo)); if (tkptr->StopProcessing) { Result = false; break; } NumberOne = NumberTwo = 0; NumberOne = atoi(ValueOne); NumberTwo = atoi(ValueTwo); Result = (NumberOne < NumberTwo); } else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_UNKNOWN), FI_LI); Result = false; break; } if (NegateResult) Result = !Result; if (!Result) break; } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&?TRUE\rFALSE\r", Result); return (Result); } /*****************************************************************************/ /* Using the locale formatting capabilities of function strftime(), output the time represented by the specified VMS quadword, binary time. If 'Time64Ptr' is NULL then default to the current time. Returns number of characters placed into 'TimeString', or zero if an error. */ int SsiTimeString ( REQUEST_STRUCT *rqptr, ulong *Time64Ptr, char *TimeFmtPtr, char *TimeString, int SizeOfTimeString ) { static BOOL InitDone; int Length; int64 Time64; struct tm UnixTime; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiTimeString() !%D \'!AZ\'", Time64Ptr, TimeFmtPtr); if (!TimeFmtPtr) TimeFmtPtr = rqptr->SsiTaskPtr->TimeFmtPtr; else if (!TimeFmtPtr[0]) TimeFmtPtr = rqptr->SsiTaskPtr->TimeFmtPtr; if (!Time64Ptr) sys$gettim (Time64Ptr = &Time64); TimeVmsToUnix (Time64Ptr, &UnixTime); if (!InitDone) { setlocale (LC_TIME, ""); InitDone = true; } if (!(Length = strftime (TimeString, SizeOfTimeString, TimeFmtPtr, &UnixTime))) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_DATE_TIME), FI_LI); return (0); } return (Length); } /*****************************************************************************/ /* Get the value of a tag parameter (e.g. tag_name="value"). Allows variable substitution into tag values, a la Apache. Tag values can have variable values substituted into them using a leading "{" and trailing '}' character sequence with the variable name between. Otherwise reserved characters may be escaped using a leading backslash. If comma-separated numbers are appended to a substitution variable these become starting and ending indices, extracting that range from the variable (a single number sets the count from zero). Returns the number of characters scanned to get the value; note that this is not necessarily the same as the number of characters in the variable value! */ int SsiGetTagValue ( REQUEST_STRUCT *rqptr, char *String, char *Value, int SizeOfValue ) { BOOL IsVarEquals, Negate, VarDidSubstitution, VarHadSpace, VarQuoted; int ExtractCount, StartIndex; char *cptr, *sptr, *vptr, *vzptr, *zptr, *ValueEqualsPtr, *ValueSpacePtr, *VarEqualsPtr; char FormatString [SSI_STRING_SIZE], VarName [256]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiGetTagValue() !&Z", String); if (!*(cptr = String)) return (0); VarQuoted = VarDidSubstitution = IsVarEquals = VarHadSpace = false; ValueEqualsPtr = VarEqualsPtr = NULL; zptr = (sptr = Value) + SizeOfValue; if (String[0] == '=') { /* this is the second bite of a #echo var="name=fmt" */ VarQuoted = true; } else if (strsame (String, "VAR=", 4)) IsVarEquals = true; for (cptr = String; *cptr && *cptr != '=' && *cptr != '\"'; cptr++); if (*cptr == '=') cptr++; if (*cptr == '\"') { cptr++; VarQuoted = true; } while (((*cptr && VarQuoted && *cptr != '\"') || (*cptr && !VarQuoted && !isspace(*cptr))) && sptr < zptr) { if (*cptr != '{') { /*********************/ /* literal character */ /*********************/ if (*cptr == '=') { VarEqualsPtr = cptr; ValueEqualsPtr = sptr; } else if (!VarEqualsPtr && (*cptr == ' ' || *cptr == '\t')) VarHadSpace = true; /* escape character? */ if (*cptr == '\\') cptr++; if (*cptr && sptr < zptr) *sptr++ = *cptr++; continue; } /*************************/ /* variable substitution */ /*************************/ VarDidSubstitution = true; cptr++; vzptr = (vptr = VarName) + sizeof(VarName); while (*cptr && (isalnum(*cptr) || *cptr == '_' || *cptr == '$') && vptr < vzptr) *vptr++ = *cptr++; *vptr = '\0'; if (*cptr == ',') { cptr++; StartIndex = 0; ExtractCount = 999999999; if (isdigit(*cptr)) { /* substring */ StartIndex = atoi(cptr); while (isdigit(*cptr)) cptr++; if (*cptr == ',') cptr++; if (isdigit(*cptr)) { /* two numbers provide a start index and an extract count */ ExtractCount = atoi(cptr); while (isdigit(*cptr)) cptr++; if (*cptr == ',') cptr++; } else { /* one number extracts from the start of the string */ ExtractCount = StartIndex; StartIndex = 0; } } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!UL !UL", StartIndex, ExtractCount); if (isalpha(*cptr)) { /* "function" on variable */ if (strsame (cptr, "length", 6)) { static $DESCRIPTOR (LengthFaoDsc, "!UL\0"); int Length; char String [32]; $DESCRIPTOR (StringDsc, String); vptr = SsiGetVar (rqptr, VarName, NULL, false); if (rqptr->SsiTaskPtr->StopProcessing) return (cptr - String); Length = 0; while (StartIndex-- && *vptr) vptr++; while (ExtractCount-- && *vptr) { vptr++; Length++; } sys$fao (&LengthFaoDsc, 0, &StringDsc, Length); vptr = String; while (*vptr && sptr < zptr) *sptr++ = *vptr++; while (isalpha(*cptr)) cptr++; } else if (strsame (cptr, "exists", 6)) { vptr = SsiGetVar (rqptr, VarName, NULL, true); if (vptr) vptr = "true"; else vptr = ""; while (*vptr && sptr < zptr) *sptr++ = *vptr++; while (isalpha(*cptr)) cptr++; } else { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_VARIABLE), FI_LI); return (cptr - String); } } else { vptr = SsiGetVar (rqptr, VarName, NULL, false); if (rqptr->SsiTaskPtr->StopProcessing) return (cptr - String); while (StartIndex-- && *vptr) vptr++; while (ExtractCount-- && *vptr && sptr < zptr) *sptr++ = *vptr++; } } else { /* get all of variable */ vptr = SsiGetVar (rqptr, VarName, NULL, false); if (rqptr->SsiTaskPtr->StopProcessing) return (cptr - String); while (*vptr && sptr < zptr) *sptr++ = *vptr++; } if (*cptr != '}') { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_VARIABLE), FI_LI); return (cptr - String); } cptr++; } if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); return (cptr - String); } *sptr = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", Value); if (*cptr == '\"') cptr++; if (rqptr->SsiTaskPtr->ComplianceLevel >= SSI_VAR_FMT_COMPLIANCE_LEVEL && IsVarEquals && VarQuoted && !(VarDidSubstitution || VarHadSpace)) { if (ValueEqualsPtr) { /* terminate variable name at the equate symbol */ *(sptr = ValueEqualsPtr) = '\0'; /* get the format string from immediately following the equate */ cptr = VarEqualsPtr + 1; zptr = (sptr = FormatString) + sizeof(FormatString); while (*cptr && *cptr != '\"' && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); return (cptr - String); } *sptr = '\0'; vptr = SsiGetVar (rqptr, Value, FormatString, false); } else vptr = SsiGetVar (rqptr, Value, NULL, false); if (rqptr->SsiTaskPtr->StopProcessing) return (cptr - String); zptr = (sptr = Value) + SizeOfValue; while (*vptr && sptr < zptr) *sptr++ = *vptr++; if (sptr >= zptr) { SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); return (cptr - String); } *sptr = '\0'; } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", Value); return (cptr - String); } /*****************************************************************************/ /* Get a 'FILE="file_name"' or a 'VIRTUAL="file_name"'. Maximum number of characters allowed in value is 256. Returns the number of characters scanned to get the value. If the file name does not contain a device/directory (i.e. is specified as if in the current directory) then the device/directory or the current document is prepended to the file name. */ int SsiGetFileSpec ( REQUEST_STRUCT *rqptr, char *String, char *FileName, int SizeOfFileName ) { int len; char *cptr, *sptr, *zptr; char Scratch [256], VirtualFileName [256]; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiGetFileSpec() !&Z", String); tkptr = rqptr->SsiTaskPtr; len = SsiGetTagValue (rqptr, String, Scratch, sizeof(Scratch)); if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!UL !&Z", len, Scratch); if (TOUP(String[0]) == 'V') { MapUrl_VirtualPath (rqptr->rqHeader.PathInfoPtr, Scratch, VirtualFileName, sizeof(VirtualFileName)); if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", VirtualFileName); FileName[0] = '\0'; cptr = MapUrl_Map (VirtualFileName, 0, FileName, SizeOfFileName, NULL, 0, NULL, 0, NULL, 0, NULL, rqptr, NULL); if (!cptr[0]) { FileName[0] = '\0'; SsiProblem (rqptr, "!AZ", cptr+1, FI_LI); return (len); } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!UL !&Z", len, FileName); return (len); } else { zptr = (sptr = FileName) + SizeOfFileName; for (cptr = Scratch; *cptr && *cptr != ':' && *cptr != '[' && sptr < zptr; *sptr++ = *cptr++); if (*cptr) { /* looks like a full specification, just continue on */ while (*cptr && sptr < zptr) *sptr++ = *cptr++; if (sptr >= zptr) { FileName[0] = '\0'; SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); return (len); } *sptr = '\0'; return (len); } zptr = (sptr = FileName) + SizeOfFileName; if (isupper(Scratch[0])) { for (cptr = tkptr->FileContentPtr->FileName; *cptr && *cptr != ']' && !SAME2(cptr,'][') && sptr < zptr; *sptr++ = TOUP(*cptr++)); if (*cptr == ']' && sptr < zptr) *sptr++ = *cptr++; for (cptr = Scratch; *cptr && sptr < zptr; *sptr++ = TOUP(*cptr++)); } else { for (cptr = tkptr->FileContentPtr->FileName; *cptr && *cptr != ']' && !SAME2(cptr,'][') && sptr < zptr; *sptr++ = TOLO(*cptr++)); if (*cptr == ']' && sptr < zptr) *sptr++ = *cptr++; for (cptr = Scratch; *cptr && sptr < zptr; *sptr++ = TOLO(*cptr++)); } if (sptr >= zptr) { FileName[0] = '\0'; SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_GENERAL_OVERFLOW), FI_LI); return (len); } *sptr = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", FileName); return (len); } } /*****************************************************************************/ /* Retrieve and display the specified file's specified attribute (size, modification time, etc.) This function uses the ACP-QIO interface detailed in the "OpenVMS I/O User's Reference Manual". */ SsiFileDetails ( REQUEST_STRUCT *rqptr, int FileDetailsItem ) { int status, FileNameLength; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiFileDetails() !AZ !UL \'!AZ\'", rqptr->SsiTaskPtr->ScratchFileName, FileDetailsItem, rqptr->SsiTaskPtr->FormatString); tkptr = rqptr->SsiTaskPtr; /* initialize file variable data */ tkptr->TheFileNameVar[0] = '\0'; tkptr->FileDetailsItem = FileDetailsItem; FileNameLength = strlen(tkptr->ScratchFileName); AuthAccessEnable (rqptr, tkptr->ScratchFileName, AUTH_ACCESS_READ); OdsParse (&tkptr->DetailsOds, tkptr->ScratchFileName, FileNameLength, NULL, 0, 0, &SsiFileDetailsParseAst, rqptr); AuthAccessEnable (rqptr, 0, 0); } /*****************************************************************************/ /* AST called from SsiFileDetails() when asynchronous parse completes. If status OK set up and queue an ACP QIO to get file size and revision date/time, ASTing to SsiFileDetailsAcpInfoAst(). */ SsiFileDetailsParseAst (REQUEST_STRUCT *rqptr) { static $DESCRIPTOR (DeviceDsc, ""); int status; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiFileDetailsParseAst() !&F sts:!&S stv:!&S", &SsiFileDetailsParseAst, rqptr->SsiTaskPtr->DetailsOds.Fab.fab$l_sts, rqptr->SsiTaskPtr->DetailsOds.Fab.fab$l_stv); tkptr = rqptr->SsiTaskPtr; if (VMSnok (status = tkptr->DetailsOds.Fab.fab$l_sts)) { SsiProblem (rqptr, "!&m", status, FI_LI); SsiEnd (rqptr); return; } /* get the variable file name */ strcpy (tkptr->TheFileNameVar, tkptr->DetailsOds.ExpFileName); if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&Z", tkptr->TheFileNameVar); if (tkptr->DetailsOds.Nam_fnb & NAM$M_SEARCH_LIST && !tkptr->SearchListCount++) { /*******************************/ /* search to get actual device */ /*******************************/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SEARCH-LIST"); AuthAccessEnable (rqptr, tkptr->DetailsOds.ExpFileName, AUTH_ACCESS_READ); OdsSearch (&tkptr->DetailsOds, &SsiFileDetailsParseAst, rqptr); AuthAccessEnable (rqptr, 0, 0); return; } /************/ /* ACP info */ /************/ AuthAccessEnable (rqptr, tkptr->DetailsOds.ExpFileName, AUTH_ACCESS_READ); OdsFileAcpInfo (&tkptr->DetailsOds, &SsiFileDetailsAcpInfoAst, rqptr); AuthAccessEnable (rqptr, 0, 0); } /****************************************************************************/ /* AST called from SsiFileDetailsParseAST() when ACP QIO completes. If status indicates no such file then call any file open error processing function originally supplied, otherwise report the error. If status OK provide the request file details. This function uses the ACP-QIO interface detailed in the "OpenVMS I/O User's Reference Manual", and is probably as fast as we can get for this type of file system functionality! */ SsiFileDetailsAcpInfoAst (REQUEST_STRUCT *rqptr) { static $DESCRIPTOR (AbbrevOneByteFaoDsc, "!UL byte"); static $DESCRIPTOR (AbbrevBytesFaoDsc, "!UL bytes"); static $DESCRIPTOR (AbbrevOnekByteFaoDsc, "!UL kbyte"); static $DESCRIPTOR (AbbrevkBytesFaoDsc, "!UL kbytes"); static $DESCRIPTOR (AbbrevOneMByteFaoDsc, "!UL Mbyte"); static $DESCRIPTOR (AbbrevMBytesFaoDsc, "!UL Mbytes"); static $DESCRIPTOR (OneBlockFaoDsc, "!UL block"); static $DESCRIPTOR (BlocksFaoDsc, "!UL blocks"); static $DESCRIPTOR (BytesFaoDsc, "!AZ bytes"); static $DESCRIPTOR (NumberFaoDsc, "!UL"); int status, NumBytes, SizeInBytes; int64 ScratchTime64; unsigned short Length; unsigned long EndOfFileVbn; char *cptr, *sptr, *zptr, *FormatPtr; char Scratch [256], String [256]; SSI_TASK *tkptr; $DESCRIPTOR (StringDsc, String); $DESCRIPTOR (ScratchDsc, Scratch); /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiFileDetailsAcpInfoAst() !&F !&S", &SsiFileDetailsAcpInfoAst, rqptr->SsiTaskPtr->DetailsOds.FileQio.IOsb.Status); tkptr = rqptr->SsiTaskPtr; /* first, deassign the channel allocated by OdsFileAcpInfo() */ sys$dassgn (tkptr->DetailsOds.FileQio.AcpChannel); if ((status = tkptr->DetailsOds.FileQio.IOsb.Status) == SS$_NOSUCHFILE) status = RMS$_FNF; if (VMSnok (status)) { if (status == RMS$_FNF && tkptr->FormatString[0] == '?') { /* ignore file not found, just continue */ tkptr->TheFileNameVar[0] = '\0'; SysDclAst (&SsiParse, rqptr); return; } else { SsiProblem (rqptr, "!&m", status, FI_LI); SsiEnd (rqptr); return; } } /*******************/ /* process details */ /*******************/ if (tkptr->FileDetailsItem == FILE_LAST_MODIFIED) { /*****************/ /* last-modified */ /*****************/ if (!tkptr->LastModifiedTime64) { /* first file */ tkptr->LastModifiedTime64 = tkptr->DetailsOds.FileQio.RdtTime64; SysDclAst (&SsiParse, rqptr); return; } if (tkptr->LastModifiedTime64 == tkptr->DetailsOds.FileQio.RdtTime64) { /* times are identical */ SysDclAst (&SsiParse, rqptr); return; } /* if a positive time results the file has been modified */ ScratchTime64 = tkptr->LastModifiedTime64 - tkptr->DetailsOds.FileQio.RdtTime64; if (ScratchTime64 >= 0) { /* positive time, current content is later than this file's RDT */ SysDclAst (&SsiParse, rqptr); return; } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&S", status); if (ScratchTime64 < 0) { /* current contents are earlier than this file's RDT */ tkptr->LastModifiedTime64 = tkptr->DetailsOds.FileQio.RdtTime64; tkptr->LastModifiedTime64 = tkptr->DetailsOds.FileQio.RdtTime64; SysDclAst (&SsiParse, rqptr); return; } else { SsiProblem (rqptr, "!&m", status, FI_LI); SsiEnd (rqptr); return; } } if (tkptr->FormatString[0] == '?') { /* no output, just checking existance of file */ SysDclAst (&SsiParse, rqptr); return; } if (tkptr->FileDetailsItem == FILE_FCREATED) { /*********************/ /* date/time created */ /*********************/ /* output creation timestamp */ if (!SsiTimeString (rqptr, &tkptr->DetailsOds.FileQio.CdtTime64, tkptr->FormatString, String, sizeof(String))) { SsiEnd (rqptr); return; } Length = strlen(String); if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex]) { NetWriteBuffered (rqptr, &SsiParse, String, Length); tkptr->TraceOutput = true; } else SysDclAst (rqptr, &SsiParse); return; } if (tkptr->FileDetailsItem == FILE_FLASTMOD) { /*********************/ /* date/time revised */ /*********************/ /* output creation timestamp */ if (!SsiTimeString (rqptr, &tkptr->DetailsOds.FileQio.RdtTime64, tkptr->FormatString, String, sizeof(String))) { SsiEnd (rqptr); return; } Length = strlen(String); if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex]) { NetWriteBuffered (rqptr, &SsiParse, String, Length); tkptr->TraceOutput = true; } else SysDclAst (rqptr, &SsiParse); return; } if (tkptr->FileDetailsItem == FILE_FSIZE) { /*************/ /* file size */ /*************/ if (tkptr->FormatString[0]) FormatPtr = tkptr->FormatString; else FormatPtr = rqptr->SsiTaskPtr->SizeFmtPtr; EndOfFileVbn = ((tkptr->DetailsOds.FileQio.RecAttr.fat$l_efblk & 0xffff) << 16) | ((tkptr->DetailsOds.FileQio.RecAttr.fat$l_efblk & 0xffff0000) >> 16); if (EndOfFileVbn <= 1) SizeInBytes = tkptr->DetailsOds.FileQio.RecAttr.fat$w_ffbyte; else SizeInBytes = ((EndOfFileVbn-1) << 9) + tkptr->DetailsOds.FileQio.RecAttr.fat$w_ffbyte; if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "vbn:!UL ffb:!UL bytes:!UL", EndOfFileVbn, tkptr->DetailsOds.FileQio.RecAttr.fat$w_ffbyte, SizeInBytes); if (TOUP(FormatPtr[0]) == 'A') /* "abbrev" */ { if (SizeInBytes < 1024) { if (SizeInBytes == 1) sys$fao (&AbbrevOneByteFaoDsc, &Length, &StringDsc, SizeInBytes); else sys$fao (&AbbrevBytesFaoDsc, &Length, &StringDsc, SizeInBytes); } else if (SizeInBytes < 1048576) { if ((NumBytes = SizeInBytes / 1024) == 1) sys$fao (&AbbrevOnekByteFaoDsc, &Length, &StringDsc, NumBytes); else sys$fao (&AbbrevkBytesFaoDsc, &Length, &StringDsc, NumBytes); } else { if ((NumBytes = SizeInBytes / 1048576) == 1) sys$fao (&AbbrevOneMByteFaoDsc, &Length, &StringDsc, NumBytes); else sys$fao (&AbbrevMBytesFaoDsc, &Length, &StringDsc, NumBytes); } String[Length] = '\0'; } else if (TOUP(FormatPtr[0]) == 'B' && TOUP(FormatPtr[1]) == 'Y') /* "bytes" */ { sys$fao (&NumberFaoDsc, &Length, &ScratchDsc, SizeInBytes); Scratch[Length] = '\0'; sptr = String; cptr = Scratch; while (Length--) { *sptr++ = *cptr++; if (Length && !(Length % 3)) *sptr++ = ','; } for (cptr = " bytes"; *cptr; *sptr++ = *cptr++); *sptr = '\0'; Length = sptr - String; } else if (TOUP(FormatPtr[0]) == 'B' && TOUP(FormatPtr[1]) == 'L') /* "blocks" */ { if (EndOfFileVbn == 1) sys$fao (&OneBlockFaoDsc, &Length, &StringDsc, EndOfFileVbn); else sys$fao (&BlocksFaoDsc, &Length, &StringDsc, EndOfFileVbn); String[Length] = '\0'; } if (tkptr->FlowControlIsExecuting[tkptr->FlowControlIndex]) { NetWriteBuffered (rqptr, &SsiParse, String, Length); tkptr->TraceOutput = true; } else SysDclAst (rqptr, &SsiParse); return; } SsiProblem (rqptr, "!AZ", ErrorSanityCheck, FI_LI); SsiEnd (rqptr); return; } /*****************************************************************************/ /* Generate a general error, with explanation about the pre-processor error. */ SsiProblem ( REQUEST_STRUCT *rqptr, ... ) { static char ErrorMessageFao [] = "\n\

!AZ

\n\ !&@ (!AZ !UL)!&@\n"; int status, argcnt; unsigned long FaoVector [32]; unsigned long *vecptr; va_list argptr; SSI_TASK *tkptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiProblem() !UL", argcnt); if (argcnt > 32+1) { ErrorNoticed (rqptr, SS$_OVRMAXARG, NULL, FI_LI); return (SS$_OVRMAXARG); } tkptr = rqptr->SsiTaskPtr; tkptr->StopProcessing = true; vecptr = FaoVector; if (tkptr->ErrMsgPtr && tkptr->ErrMsgPtr[0]) *vecptr++ = tkptr->ErrMsgPtr; else *vecptr++ = MsgFor(rqptr,MSG_SSI_ERROR); va_start (argptr, rqptr); for (argcnt -= 1; argcnt; argcnt--) *vecptr++ = va_arg (argptr, unsigned long); va_end (argptr); *vecptr++ = MsgFor(rqptr,MSG_SSI_LINE); *vecptr++ = tkptr->StatementLineNumber; if (tkptr->StatementBeginPtr && tkptr->StatementBeginPtr[0]) { *vecptr++ = "  ...  \\!&;AZ\\"; *vecptr++ = tkptr->StatementBeginPtr; } else *vecptr++ = ""; *vecptr++ = tkptr->FileContentPtr->FileName; status = FaolToNet (rqptr, ErrorMessageFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } /*****************************************************************************/