/*****************************************************************************/ /* 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.
", 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, NULL); } else DclBegin (rqptr, &SsiParse, NULL, ScriptName, MappedScript, NULL, MappedRunTime, NULL, NULL); } else DclBegin (rqptr, &SsiParse, DclCommand, NULL, 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[0] && !tkptr->LastModifiedTime64[1]) { /* no files' RDTs have been checked */ SsiProblem (rqptr, "!AZ", MsgFor(rqptr,MSG_SSI_TAG_VALUE), FI_LI); SsiEnd (rqptr); return; } if (!rqptr->NotFromCache) { if (VMSnok (status = HttpIfModifiedSince (rqptr, &tkptr->LastModifiedTime64, -1))) { /****************/ /* not modified */ /****************/ /* status LIB$_NEGTIM if not modified, header sent, quit now */ 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[0] && !tkptr->LastModifiedTime64[1]) { /* 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; unsigned short Length; unsigned long Time64 [2]; 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); TimeAdjustGMT (true, &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 'BinaryTimePtr' 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, unsigned long *BinaryTimePtr, char *TimeFmtPtr, char *TimeString, int SizeOfTimeString ) { static BOOL InitDone; int Length; unsigned long BinaryTime [2]; struct tm UnixTime; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "SsiTimeString() !%D \'!AZ\'", BinaryTimePtr, TimeFmtPtr); if (!TimeFmtPtr) TimeFmtPtr = rqptr->SsiTaskPtr->TimeFmtPtr; else if (!TimeFmtPtr[0]) TimeFmtPtr = rqptr->SsiTaskPtr->TimeFmtPtr; if (!BinaryTimePtr) sys$gettim (BinaryTimePtr = &BinaryTime); TimeVmsToUnix (BinaryTimePtr, &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; unsigned short Length; unsigned long EndOfFileVbn; unsigned long ScratchTime64 [2]; 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[0] && !tkptr->LastModifiedTime64[1]) { /* first file */ PUT_QUAD_QUAD (tkptr->DetailsOds.FileQio.RdtTime64, tkptr->LastModifiedTime64); SysDclAst (&SsiParse, rqptr); return; } if (tkptr->LastModifiedTime64[0] == tkptr->DetailsOds.FileQio.RdtTime64[0] && tkptr->LastModifiedTime64[1] == tkptr->DetailsOds.FileQio.RdtTime64[1]) { /* times are identical */ SysDclAst (&SsiParse, rqptr); return; } /* if a positive time results the file has been modified */ if (VMSok (status = lib$sub_times (&tkptr->LastModifiedTime64, &tkptr->DetailsOds.FileQio.RdtTime64, &ScratchTime64))) { /* positive time, current content is later than this file' RDT */ SysDclAst (&SsiParse, rqptr); return; } if (WATCHMOD (rqptr, WATCH_MOD_SSI)) WatchThis (WATCHITM(rqptr), WATCH_MOD_SSI, "!&S", status); if (status == LIB$_NEGTIM) { /* current contents are earlier than this file's RDT */ tkptr->LastModifiedTime64[0] = tkptr->DetailsOds.FileQio.RdtTime64[0]; PUT_QUAD_QUAD (tkptr->DetailsOds.FileQio.RdtTime64, tkptr->LastModifiedTime64); 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\