/*****************************************************************************/ /* HyperShelf.c A CGI-compliant script. Can be supported by any server that supplies the CGI variables listed below. ABOUT HYPERSHELF ---------------- Emulates the "Bookshelf Navigation Utility" (BNU) of 1997ff on-line documentation CD releases, and the Bookreader shelf navigation capability of earlier times. It has some additional capabilities not found in either, the ability to provide access to PostScript, plain-text documents and PDF documents. Page colouration may be specified via the appropriate command-line qualifiers (or corresponding logical name). Defaults for any not specified. Specifiy as /BLAH="" to NOT specify the corresponding colour (i.e. leave it to the browser). Server mapping must include rules for all file paths (devices) which have shelves residing on them. This script munges VMS file paths into URL-style equivalents. For example: DISK$AXPDOCSEP971:[DATABASE]D39QAAA8.BKB would be represented as /disk$axpdocsep971/database/d39qaaa8.bkb Therefore the server must map the "/disk$axpdocsep971/*" into the VMS equivalent, and of course all file paths that can possibly be returned. Actual a useful rule for all BNU CDs is (depending on your architecture): pass /disk$axpdoc* /disk$axpdoc* pass /disk$vaxdoc* /disk$vaxdoc* Thanks to Jeff Goodwin of Fairchild Semiconductor for these VMS Apache mappings AliasMatch ^/disk\$axpdoc(.+) /disk$axpdoc$1 AliasMatch ^/disk\$vaxdoc(.+) /disk$vaxdoc$1 Digital Bookreader supports Bookreader format documents. Digital BNU (currently) supports Bookreader and HTML documents. WASD Hypershelf supports both plus the following extensions; PostScript, plain- text and Adobe PDF documents. It does not support Bristol HyperHelp. Note that the Hypershelf extensions are preceded by two comment characters, either "##" (as in the example) or "!!". The prevents the BNU from barfing on them. If a title is the same as the previous entry (or is missing altogether) the document title becomes a description of the document type. For examples of how they behave do a http:///hypershelf/wasd_root/exercise/library.odl http:///hypershelf/wasd_root/exercise/library.decw$bookshelf HYPERSHELF EXTENSIONS (see examples for examples) --------------------- ##post PostScript document ##plain plain-text document ##pdf Adobe Portable Document Format ##url relative or full Web URL (e.g. 'http://the.host.name/library/') EXAMPLE BNU SHELF (extension: .ODL) ----------------- Title VMS Hypertext Services Library (BNU version!) book /wasd_root/doc/htd/htd.decw$book Technical Overview html /wasd_root/doc/htd/htd_0000.html Technical Overview ##post /wasd_root/doc/htd/htd.ps Technical Overview ##plain /wasd_root/doc/htd/htd.txt Technical Overview ##pdf /wasd_root/doc/htd/htd.pdf Technical Overview book /wasd_root/doc/env/env.decw$book Hypertext Environment html /wasd_root/doc/env/env_0000.html Hypertext Environment # don't have to specify the title for the same document but different type ##post /wasd_root/doc/env/env.ps ##plain /wasd_root/doc/env/env.txt book /wasd_root/doc/sdm2htm/sdm2htm.decw$book SDML to HTML Converter html /wasd_root/doc/sdm2htm/sdm2htm_0000.html SDML to HTML Converter ##post /wasd_root/doc/sdm2htm/sdm2htm.ps SDML to HTML Converter book /wasd_root/doc/menu_primer/menu_primer.decw$book Menu Primer html /wasd_root/doc/menu_primer/menu_primer_0000.html Menu Primer ##url http://the.host.name/library/ The Host Name Library ##url /docs/ Local Server Documents EXAMPLE BOOKREADER SHELF (extension: .DECW$BOOKSHELF or .BKS) ------------------------ title\\VMS Hypertext Services Library (Bookreader version!) book\wasd_root:[doc.htd]htd\Technical Overview ##html\wasd_root:[doc.htd]htd_0000.html\ ##ps\wasd_root:[doc.htd]htd.ps\ ##plain\wasd_root:[doc.htd]htd.txt\ ##pdf\wasd_root:[doc.htd]htd.pdf\ book\wasd_root:[doc.env]env\Hypertext Environment ##html\wasd_root:[doc.env]env_0000.html\ ##post\wasd_root:[doc.env]env.ps\ ##plain\wasd_root:[doc.env]env.txt\ book\wasd_root:[doc.sdm2htm]sdm2htm\SDML to HTML Converter ##html\wasd_root:[doc.sdm2htm]sdm2htm_0000.html\ ##ps\wasd_root:[doc.sdm2htm]sdm2htm.ps\ book\wasd_root:[doc.menu_primer]menu_primer\Menu Primer ##html\wasd_root:[doc.menu_primer]menu_primer_0000.html\Menu Primer ##url\http://the.host.name/library/\The Host Name Library ##url\/docs/\Local Server Documents BNU OR BOOKREADER BEHAVIOUR? ---------------------------- HyperShelf adjusts it's behaviour according to the shelf file it is given in the path. If the file has a type of .ODL it is considered to be in BNU shelf format (see above). Any other type (e.g. .BKS or .DECW$BOOKSHELF) it considers to be in Bookreader format (again, see above). If a path to a shelf is not provided HyperShelf looks into the environment for the DECW$BOOK and DECW$BOOKSHELF logicals. If it finds DECW$BOOK it considers it is a Bookreader environment and BNU is not in use. It they are not found it considers it a BNU environment. To specify a default BNU library for when none is supplied in the path use the /LIBRARY qualifier in a script support procedure. In a Bookreader environment HyperShelf will open any library specified by DECW$BOOKSHELF (or libraries if it has multiple translations). If not defined it searches for a file using DECW$BOOK:LIBRARY.* The first found will become the default library. This will select all libraries (Digital and third party) if DECW$BOOK is defined with multiple translations. The first library encountered is expanded to display it's contents. Subsequent libraries have a shelf link created for them with information appended to the description indicating it is a library. The use of search lists can considerably extend library access (search) times, particularly when slow CD-ROM readers, InfoServers or DECnet/FAL is involved. A faster alternative is to incorporate references to these in an explicit DECW$BOOKSHELF logical. The BNU environment does not use logical names or logical search lists. Everthing appears to be "hard-wired" into the shelves in a URL-style or Unix-style file path (understandable when you consider part of the BNU viewing environment is a browser (Netscape Navigator)). If HyperShelf is not supplied with a path to an ODL shelf it will search for one of those listed in 'OdlShelfDefaults' storage below. The qualifiers /BNU and /BOOKREADER can be used to force one behaviour or the other. SPECIFYING SHELVES ------------------ Shelves are specified in one of four ways, with the indicated precedence. 1. ?file= .......... request query string form field (VMS file spec) 2. /path/to/file ... path into VMS file spec via WWW_PATH_TRANSLATED 3. /LIBRARY= ....... HYPERSHELF script qualifier (VMS file spec) 4. no path ......... DECW$BOOK, DECW$BOOKSHELF, BNU default libraries OSU ENVIRONMENT --------------- Script responses are returned in OSU "raw" mode; the script taking care of the full response header and correctly carriage-controlled data stream, text or binary!! Uses the CGILIB.C to engage in the dialog phase generating, storing and then making available the equivalent of CGI variables. To use the executable directly (no wrapper procedure), as in http://the.host.name/htbin/hypershelf Define a system-wide logical during server startup $ HYPERSHELF_PARAM = - "/buttons=""Close$Help=/wasd/runtime/hypershelfhelp.html""" +- "/icons=""/wasd/runtime""" $ DEFINE /SYSTEM HYPERSHELF$PARAM HYPERSHELF_PARAM APACHE CGI ENVIRONMENT ---------------------- To use the executable directly (no wrapper procedure), as in http://the.host.name/cgi-bin/hypershelf Define a system-wide logical during server startup $ HYPERSHELF_PARAM = - "/buttons=""Close$Help=/wasd/runtime/hypershelfhelp.html""" +- "/icons=""/wasd/runtime""" $ DEFINE /SYSTEM HYPERSHELF$PARAM HYPERSHELF_PARAM CGI VARIABLES ------------- Server generated ... WWW_HTTP_IF_MODIFIED_SINCE if "304 Not modified" are desired (optional) WWW_HTTP_PRAGMA "no-cache" (optional) WWW_HTTP_REFERER "close" button becomes active WWW_PATH_INFO URL path to shelf WWW_PATH_TRANSLATED VMS file specification for shelf WWW_REQUEST_METHOD "GET" only is supported WWW_SCRIPT_NAME path to script WWW_SERVER_CHARSET default character set of server WWW_SERVER_NAME host on which the script is executing WWW_SERVER_SOFTWARE HTTPd identifying string HyperShelf generated ... WWW_FORM_FILE VMS file name for shelf (when path cannot be supplied) WWW_FORM_REFERER overrides the HTTP_REFERER URL WWW_FORM_TITLE explicit title of book QUALIFIERS ---------- /BOOKREADER Bookreader is the default shelf processor /BNU Bookshelf Navigation Utility is the default shelf processor /CHARSET= "Content-Type: text/html; charset=...", empty suppresses charset /DBUG turns on all "if (Debug)" statements (don't use with OSU) /HYPERREADER= name of 'HyperReader' script (defaults to "/hyperreader") /ICON= path to icons /LIBRARY= specifies the default library (shelf) file (VMS path) /[NO]ODS5 control extended file specification (basically for testing) /PRINT= name of 'Print' script (optional) LOGICAL NAMES ------------- HYPERSHELF$DBUG turns on all "if (Debug)" statements HYPERSHELF$PARAM equivalent to (overrides) the command line parameters/qualifiers (define as a system-wide logical) HTTPD$GMT timezone offset from Greenwich Mean Time (e.g. "+09:30" and only needed if DTSS is not in use) BUILD DETAILS ------------- See BUILD_HYPERSHELF.COM procedure. COPYRIGHT --------- Copyright (C) 1994-2022 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY (update SOFTWAREVN as well!) --------------- 04-MAR-2022 MGD v5.0.2, alert() [Close] for other than origin server 31-AUG-2020 MGD v5.0.1, bugfix; eliminate &referer= XSS vulnerability 06-OCT-2014 MGD v5.0.0, although now largely only of historical interest ... ... a nod to the twenty-first century (and happy 90th birthday Dad) 10-MAY-2005 MGD v4.7.8, SWS 2.0 ignore query string components supplied as command-line parameters differently to CSWS 1.2/3 23-DEC-2003 MGD v4.7.7, minor conditional mods to support IA64 27-APR-2003 MGD v4.7.6, change DEFAULT_ICON to all lower-case for CSWS 12-APR-2003 MGD v4.7.5, link colour changed to 0000cc 20-AUG-2002 MGD v4.7.4, allow for HTTP/1.1 'Cache-control:' field used by Mozilla variants (instead of HTTP/1.0 'pragma:') 15-AUG-2002 MGD v4.7.3, make MFD ('000000') suppression WASD-only 30-JUN-2002 MGD v4.7.2, bugfix; link path in ProcessShelf() 01-JUL-2001 MGD v4.7.1, add 'SkipParameters' for direct OSU support 28-OCT-2000 MGD v4.7.0, use CGILIB object module 31-AUG-2000 MGD v4.6.0, add URL as bookshelf entry type (good suggestion Gerry..Czadowski@@nav-international..com) 18-JAN-2000 MGD v4.5.0, support extended file specifications (ODS-5) 07-AUG-1999 MGD v4.4.0, use more of the CGILIB functionality 24-APR-1999 MGD v4.3.0, use CGILIB.C, standard CGI environment (Netscape FastTrack) 02-OCT-1998 MGD v4.2.0, provide content-type "; charset=..." 27-AUG-1998 MGD v4.1.1, iteratively translate DECW$BOOK translations 08-AUG-1998 MGD v4.1.0, refinements in shelf and library parsing, OSU output processing reworked, bugfix; TimeSetTimezone() 'Seconds' unsigned->signed 06-AUG-1998 MGD v4.0.2, accomodation for OSU ... reduce HTTP response header carriage control from to only (OSU/IE4 combination problematic) 01-AUG-1998 MGD v4.0.1, suppress table background colours if empty, accomodations for OSU environment 29-APR-1998 MGD v4.0.0, remove the need for WASD MapUrl() functions (making it a GENERIC CGI script!!), some tidying up and cosmetic changes 26-FEB-1998 MGD v3.3.5, TimeSetGmt() modified to be in line with HTTPd 06-NOV-1997 MGD v3.3.4, discovered "hyperhelp" in latest 7.1 ODL added "unknown" document type 19-AUG-1997 MGD v3.2.2, MapUrl() to MapUrl_Map() for conditional mapping 01-AUG-1997 MGD v3.2.1, 'HttpHasBeenOutput' not initialize for CGIplus 20-JUL-1997 MGD v3.2.0, added /BODY= qualifier, added Adobe PDF document type 07-JUL-1997 MGD v3.1.0, CGIplus capable 30-JUN-1997 MGD v3.0.0, major changes to support the BNU format shelves, "Pragma: no-cache" now overrides "If-Modified-Since:" 16-MAY-1996 MGD v2.2.3, change all .XBMs to transparent .GIFs 23-FEB-1996 MGD v2.2.2, bugfix; after modifying the HTTPD$GMT format for the HTTPd server I had forgotten about some scripts 22-NOV-1995 MGD v2.2.1, discovered the TITLE keyword in libraries 12-OCT-1995 MGD v2.2.0, add 'Referer:', 'Last-Modified:'/'If-Modified-Since:' 24-MAY-1995 MGD v2.1.0, minor changes for AXP compatibility 15-APR-1995 MGD v2.0.0, complete rewrite 10-JUN-1994 MGD v1.0.0, initial development */ /*****************************************************************************/ #define SOFTWAREVN "5.0.2" #define SOFTWARENM "HYPERSHELF" #define SOFTWARECR "Copyright (C) 1996-2022 Mark G.Daniel" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* standard C header files */ #include #include #include #include #include #include #include /* VMS-related header files */ #include #include #include #include #include #include #include #include #include /* application header file */ #include "enamel.h" #include "hyperreader.h" #include "cgilib.h" #ifndef __VAX # pragma nomember_alignment #endif #define boolean int #define true 1 #define false 0 #define FI_LI __FILE__, __LINE__ #ifndef __VAX # ifndef NO_ODS_EXTENDED # define ODS_EXTENDED 1 /* this is smaller than the technical maximum, but still quite large! */ # define ODS_MAX_FILE_NAME_LENGTH 511 # define ODS_MAX_FILESYS_NAME_LENGTH 264 # endif #endif #define ODS2_MAX_FILE_NAME_LENGTH 255 #ifndef ODS_MAX_FILE_NAME_LENGTH # define ODS_MAX_FILE_NAME_LENGTH ODS2_MAX_FILE_NAME_LENGTH #endif #if ODS_MAX_FILE_NAME_LENGTH < ODS2_MAX_FILE_NAME_LENGTH # define ODS_MAX_FILE_NAME_LENGTH ODS2_MAX_FILE_NAME_LENGTH #endif #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) (!((x) & STS$M_SUCCESS)) #define DEFAULT_ICON "/hypershelf/-/" char *BookshelfTypes [] = { ".ODL", ".BKS", ".DECW$BOOKSHELF", "" }; char OdlShelfDefaults [] = "DECW$USER_DEFAULTS:LIBRARY.ODL, \ DECW$USER_DEFAULTS:CONTENTS.ODL, \ DECW$SYSTEM_DEFAULTS:LIBRARY.ODL, \ DECW$SYSTEM_DEFAULTS:CONTENTS.ODL"; #define DEFAULT_BUTTONS \ "↶Back=javascript:parent.history.go(-1)$\ Close$^Help=" DEFAULT_HYPERSHELF_HELP char ErrorBookshelfType [] = "Doesn't look like a bookshelf type!"; char Utility [] = "HYPERSHELF"; boolean Debug, DecwBookDefined, DecwBookshelfDefined, DoBnu, DoBookreader, ErrorReported, IsCgiPlus, StdCgiEnvironment, PageBegun, TimeAheadOfGmt, WasdEnvironment; unsigned long IfModifiedSinceBinaryTime [2], TimeGmtDeltaBinary [2]; int BookshelfCount, OdsExtended, PrevBookshelfCount; char ContentTypeCharset [64], DefaultButtons [] = DEFAULT_BUTTONS, DefaultStyle [] = DEFAULT_HYPERSHELF_STYLE, LastModifiedGmDateTime [32], HtmlReferer [512], HyperReaderReferer [512], PrevTitle [256], ShelfPath [ODS_MAX_FILE_NAME_LENGTH+1], SoftwareID [48], TimeGmtString [32], TimeGmtVmsString [32], UrlEncReferer [512], UrlTitle [256], ShelfSpec [ODS_MAX_FILE_NAME_LENGTH+1]; char *ButtonsPtr = DefaultButtons, *CgiEnvironmentPtr, *CgiFormFilePtr, *CgiFormRefererPtr, *CgiFormTitlePtr, *CgiHttpCacheControlPtr, *CgiHttpIfModifiedSincePtr, *CgiHttpPragmaPtr, *CgiPathInfoPtr, *CgiPathTranslatedPtr, *CgiRequestMethodPtr, *CgiScriptNamePtr, *CgiServerSoftwarePtr, *CharsetPtr, *CliCharsetPtr, *HyperReaderScriptNamePtr, *IconLocationPtr = DEFAULT_ICON, *LibraryFileNamePtr = "", *PrintScriptNamePtr = "", *SoftwareCopy = SOFTWARECR, *StyleSheetPtr = ""; /* required function prototypes */ char* VmsToPath (char*, char*); char* ButtonBarButton (char*, char*); char* XSSuspect (char*); /*****************************************************************************/ /* 'argc' and 'argv' are only required to support CgiLibEnvironmentInit(), in particular the OSU environment. */ main ( int argc, char *argv[] ) { /*********/ /* begin */ /*********/ sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion()); if (getenv ("HYPERSHELF$DBUG")) Debug = true; CgiLibEnvironmentSetDebug (Debug); CgiLibEnvironmentInit (argc, argv, false); WasdEnvironment = CgiLibEnvironmentIsWasd(); GetParameters (); CgiLibResponseSetCharset (CliCharsetPtr); CgiLibResponseSetSoftwareID (SoftwareID); CgiLibResponseSetErrorMessage ("Reported by HyperShelf"); if (StyleSheetPtr[0]) { char *cptr = calloc (1, 64+strlen(StyleSheetPtr)); sprintf (cptr, "\n", StyleSheetPtr); StyleSheetPtr = cptr; } #ifdef ODS_EXTENDED OdsExtended = (GetVmsVersion() >= 72); ENAMEL_NAML_SANITY_CHECK #endif /* ODS_EXTENDED */ /* if not CLI set and old bookreader logicals then bookreader */ DecwBookDefined = (boolean)(getenv ("DECW$BOOK")); DecwBookshelfDefined = (boolean)(getenv ("DECW$BOOKSHELF")); if (!(DoBnu || DoBookreader) && (DecwBookDefined || DecwBookshelfDefined)) { DoBookreader = true; DoBnu = false; } else { DoBnu = true; DoBookreader = false; } if (Debug) fprintf (stdout, "DecwBookDefined: %d DecwBookshelfDefined: %d DoBookreader: %d DoBnu: %d\n", DecwBookDefined, DecwBookshelfDefined, DoBookreader, DoBnu); IsCgiPlus = CgiLibEnvironmentIsCgiPlus (); if (IsCgiPlus) { for (;;) { /* block waiting for the next request */ CgiLibVar (""); if (Debug) fputs ("Content-Type: text/plain\n\n", stdout); ProcessRequest (); CgiLibCgiPlusEOF (); } } else ProcessRequest (); exit (SS$_NORMAL); } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line or from a configuration symbol or logical containing the equivalent. OSU scripts have the 'method', 'url' and 'protocol' supplied as P1, P2, P3 (these being detected and used by CGILIB), and are of no interest to this function. */ GetParameters () { static char CommandLine [256]; static unsigned long Flags = 0; int status, SkipParameters; unsigned short Length; char ch; char *aptr, *cptr, *clptr, *sptr; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if (!(clptr = getenv ("HYPERSHELF$PARAM"))) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) exit (status); (clptr = CommandLine)[Length] = '\0'; } /* if [C]SWS (VMS Apache) */ if (CgiLibEnvironmentIsApache()) { /* CSWS 1.2/3 look for something non-space outside of quotes */ for (cptr = clptr; *cptr; cptr++) { if (isspace(*cptr)) continue; if (*cptr != '\"') break; cptr++; while (*cptr && *cptr != '\"') cptr++; if (*cptr) cptr++; } /* CSWS 1.2/3 if nothing outside of quotes then ignore command line */ if (!*cptr) return; /* SWS 2.0 doesn't begin with /APACHE from DCL procedure wrapper */ if (!strsame (cptr, "/APACHE", 7)) return; } /* if OSU environment then skip P1, P2, P3 */ if (CgiLibEnvironmentIsOsu()) SkipParameters = 3; else SkipParameters = 0; aptr = NULL; ch = *clptr; for (;;) { if (aptr) *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (SkipParameters) { SkipParameters--; continue; } if (strsame (aptr, "/APACHE", 4)) { /* just skip this marker for command-line parameters under SWS 2.0 */ continue; } if (strsame (aptr, "/DBUG", -1)) { Debug = true; continue; } if (strsame (aptr, "/BNU", -1)) { DoBookreader = false; DoBnu = true; continue; } if (strsame (aptr, "/BOOKREADER", 4)) { DoBookreader = true; DoBnu = false; continue; } if (strsame (aptr, "/BUTTONS=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; if (*cptr == '+') { /* append buttons to defaults */ aptr = calloc (1, strlen(ButtonsPtr)+strlen(cptr)+2); strcpy (aptr, ButtonsPtr); strcat (aptr, "$"); strcat (aptr, cptr+1); ButtonsPtr = aptr; } else ButtonsPtr = cptr; continue; } if (strsame (aptr, "/CHARSET=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliCharsetPtr = cptr; continue; } if (strsame (aptr, "/HYPERREADER=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; HyperReaderScriptNamePtr = cptr; continue; } if (strsame (aptr, "/ICON=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; IconLocationPtr = cptr; continue; } if (strsame (aptr, "/LIBRARY=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; LibraryFileNamePtr = cptr; continue; } if (strsame (aptr, "/ODS5", 5)) { OdsExtended = true; continue; } if (strsame (aptr, "/NOODS5", 7)) { OdsExtended = false; continue; } if (strsame (aptr, "/PRINT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PrintScriptNamePtr = cptr; continue; } if (strsame (aptr, "/STYLE=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; StyleSheetPtr = cptr; continue; } if (*aptr != '/') { fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } else { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } } /*****************************************************************************/ /* */ ProcessRequest () { int status, idx; char c; char *cptr, *sptr, *zptr, *ShelfFilePtr; char ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1], ResFileName [ODS_MAX_FILE_NAME_LENGTH+1], Scratch [256], SearchDefault [ODS_MAX_FILE_NAME_LENGTH+1]; struct FAB SearchFab; #ifdef ODS_EXTENDED struct NAML SearchNaml; #endif /* ODS_EXTENDED */ struct NAM SearchNam; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequest()\n"); CgiEnvironmentPtr = CgiLibEnvironmentName (); ErrorReported = false; CgiServerSoftwarePtr = CgiLibVar ("WWW_SERVER_SOFTWARE"); CgiRequestMethodPtr = CgiLibVar ("WWW_REQUEST_METHOD"); if (strcmp (CgiRequestMethodPtr, "GET")) { CgiLibResponseHeader (501, "text/html"); fprintf (stdout, "Not implemented!\n"); return; } CgiPathInfoPtr = CgiLibVar ("WWW_PATH_INFO"); CgiPathTranslatedPtr = CgiLibVar ("WWW_PATH_TRANSLATED"); CgiScriptNamePtr = CgiLibVar ("WWW_SCRIPT_NAME"); CgiHttpCacheControlPtr = CgiLibVar ("WWW_HTTP_CACHE_CONTROL"); CgiHttpPragmaPtr = CgiLibVar ("WWW_HTTP_PRAGMA"); CgiFormTitlePtr = CgiLibVar ("WWW_FORM_TITLE"); CgiFormFilePtr = CgiLibVar ("WWW_FORM_FILE"); if (!CgiFormFilePtr[0]) CgiFormFilePtr = CgiLibVar ("WWW_FORM_SHELF"); if (VMSnok (status = TimeSetGmt ())) { if (status != SS$_NOLOGNAM) { CgiLibResponseError (FI_LI, status, "GMT offset"); return; } } CgiHttpIfModifiedSincePtr = CgiLibVar ("WWW_HTTP_IF_MODIFIED_SINCE"); if (CgiHttpIfModifiedSincePtr[0]) { if (VMSnok (HttpGmTime (CgiHttpIfModifiedSincePtr, &IfModifiedSinceBinaryTime))) { if (Debug) fprintf (stdout, "If-Modified-Since: NBG!\n"); IfModifiedSinceBinaryTime[0] = IfModifiedSinceBinaryTime[0] = 0; CgiHttpIfModifiedSincePtr = ""; } } if (!HyperReaderScriptNamePtr) { if (CgiLibEnvironmentIsWasd() || CgiLibEnvironmentIsCgiPlus()) HyperReaderScriptNamePtr = DEFAULT_WASD_HYPERREADER; else if (CgiLibEnvironmentIsOsu()) HyperReaderScriptNamePtr = DEFAULT_OSU_HYPERREADER; else HyperReaderScriptNamePtr = DEFAULT_CGI_HYPERREADER; } if (CgiPathInfoPtr[0] == '/' && !CgiPathInfoPtr[1]) CgiPathInfoPtr = CgiPathTranslatedPtr = ""; if (!CgiFormTitlePtr[0]) CgiFormTitlePtr = "Library"; CgiFormRefererPtr = CgiLibVar ("WWW_FORM_REFERER"); if (!CgiFormRefererPtr[0]) CgiFormRefererPtr = CgiLibVar ("WWW_HTTP_REFERER"); if (cptr = XSSuspect (CgiFormRefererPtr)) { CgiLibResponseError (FI_LI, 0, cptr); return; } if (CgiFormRefererPtr[0]) { /* re-escape the URL-escaped percentages */ CgiLibUrlEncode (CgiFormRefererPtr, -1, UrlEncReferer, sizeof(UrlEncReferer)); /* check that any referer is for the same server */ for (cptr = UrlEncReferer; *cptr && *(short*)cptr != '//'; cptr++); if (cptr) cptr += 2; sptr = CgiLibVar ("WWW_SERVER_NAME"); while (*cptr && *sptr && tolower(*cptr) == tolower(*sptr)) { cptr++; sptr++; } if ((*cptr && *cptr != '/') || *sptr) { /* not same server, substitute alert() */ zptr = (sptr = HtmlReferer) + sizeof(HtmlReferer)-8; for (cptr = "javascript:alert(\\\'"; *cptr; *sptr++ = *cptr++); for (cptr = UrlEncReferer; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "\\\');"; *cptr; *sptr++ = *cptr++); *sptr = '\0'; } else CgiLibHtmlEscape (CgiFormRefererPtr, -1, HtmlReferer, sizeof(HtmlReferer)); } else UrlEncReferer[0] = HtmlReferer[0] = '\0'; ShelfSpec[0] = '\0'; if (!ShelfSpec[0]) strcpy (ShelfSpec, CgiFormFilePtr); if (!ShelfSpec[0]) strcpy (ShelfSpec, CgiPathTranslatedPtr); if (!ShelfSpec[0]) strcpy (ShelfSpec, LibraryFileNamePtr); if (Debug) fprintf (stdout, "ShelfSpec |%s|\n", ShelfSpec); if (ShelfSpec[0]) { /* find file type (extension) */ for (cptr = ShelfSpec; *cptr; cptr++); if (*(cptr-1) == ';') *(cptr-1) = '\0'; while (cptr > ShelfSpec && *cptr != '.' && *cptr != ']' && *cptr != ':') cptr--; if (*cptr == '.') { /* check if looks like a bookshelf file type */ for (idx = 0; BookshelfTypes[idx][0]; idx++) { if (Debug) fprintf (stdout, "|%s|%s|\n", BookshelfTypes[idx], cptr); if (strsame (BookshelfTypes[idx], cptr, -1)) break; } if (!BookshelfTypes[idx][0]) { CgiLibResponseError (FI_LI, 0, ErrorBookshelfType); return; } } } PageBegun = false; BookshelfCount = PrevBookshelfCount = 0; if (DoBookreader) { /**************/ /* Bookreader */ /**************/ idx = 0; for (;;) { SearchFab = cc$rms_fab; if (ShelfSpec[0]) { SearchFab.fab$l_dna = "DECW$BOOK:.DECW$BOOKSHELF"; SearchFab.fab$b_dns = 25; } else { if (DecwBookshelfDefined) { SysTrnLnm ("DECW$BOOKSHELF", idx++, SearchDefault); if (!SearchDefault[0]) break; } else { SysTrnLnm ("DECW$BOOK", idx++, SearchDefault); if (!SearchDefault[0]) break; for (;;) { SysTrnLnm (SearchDefault, 0, Scratch); if (!Scratch[0]) break; strcpy (SearchDefault, Scratch); } strcat (SearchDefault,"LIBRARY.*;"); } SearchFab.fab$l_dna = SearchDefault; SearchFab.fab$b_dns = strlen(SearchDefault); } SearchFab.fab$b_shr = FAB$M_SHRGET; #ifdef ODS_EXTENDED if (OdsExtended) { SearchFab.fab$l_fna = (char*)-1; SearchFab.fab$b_fns = 0; SearchFab.fab$l_nam = (struct namdef*)&SearchNaml; ENAMEL_RMS_NAML(SearchNaml) SearchNaml.naml$l_long_filename = ShelfSpec; SearchNaml.naml$l_long_filename_size = strlen(ShelfSpec); SearchNaml.naml$l_long_expand = ExpFileName; SearchNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1; SearchNaml.naml$l_long_result = ResFileName; SearchNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1; } else #endif /* ODS_EXTENDED */ { SearchFab.fab$l_fna = ShelfSpec; SearchFab.fab$b_fns = strlen(ShelfSpec); SearchFab.fab$l_nam = &SearchNam; SearchNam = cc$rms_nam; SearchNam.nam$l_esa = ExpFileName; SearchNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; SearchNam.nam$l_rsa = ResFileName; SearchNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } status = sys$parse (&SearchFab, 0, 0); if (Debug) fprintf (stdout, "sys$parse() %%X%08.08X\n", status); if (VMSnok (status)) { /* subsequent translations ignore directories not being there! */ if (status == RMS$_DNF && idx > 0) continue; if (ShelfSpec[0]) cptr = ShelfSpec; else cptr = SearchFab.fab$l_dna; CgiLibResponseError (FI_LI, status, cptr); return (status); } #ifdef ODS_EXTENDED if (OdsExtended) SearchNaml.naml$l_long_ver[SearchNaml.naml$l_long_ver_size] = '\0'; else #endif /* ODS_EXTENDED */ SearchNam.nam$l_ver[SearchNam.nam$b_ver] = '\0'; if (Debug) fprintf (stdout, "ExpFileName |%s|\n", ExpFileName); status = sys$search (&SearchFab, 0, 0); if (Debug) fprintf (stdout, "sys$search() %%X%08.08X\n", status); if (VMSok (status)) { #ifdef ODS_EXTENDED if (OdsExtended) *SearchNaml.naml$l_long_ver = '\0'; else #endif /* ODS_EXTENDED */ *SearchNam.nam$l_ver = '\0'; if (Debug) fprintf (stdout, "ResFileName |%s|\n", ResFileName); status = ProcessShelf (ResFileName); if (Debug) fprintf (stdout, "ProcessShelf() %%X%08.08X\n", status); } if (status == RMS$_NMF) status = SS$_NORMAL; if (VMSnok (status)) { if (ShelfSpec[0]) cptr = ShelfSpec; else cptr = SearchFab.fab$l_dna; CgiLibResponseError (FI_LI, status, cptr); return (SS$_NORMAL); } /* continue to go through this rigmoral only if no shelf specified! */ if (ShelfSpec[0]) break; } } else { /*******/ /* BNU */ /*******/ ShelfFilePtr = OdlShelfDefaults; while (*ShelfFilePtr) { cptr = ShelfFilePtr; while (*cptr == ' ' || *cptr == ',') cptr++; if (!*cptr) break; if (Debug) fprintf (stdout, "cptr |%s|\n", cptr); ShelfFilePtr = cptr; while (*cptr && *cptr != ' ' && *cptr != ',') cptr++; c = *cptr; SearchFab = cc$rms_fab; SearchFab.fab$l_dna = ShelfFilePtr; SearchFab.fab$b_dns = cptr - ShelfFilePtr; SearchFab.fab$b_shr = FAB$M_SHRGET; #ifdef ODS_EXTENDED if (OdsExtended) { SearchFab.fab$l_fna = (char*)-1; SearchFab.fab$b_fns = 0; SearchFab.fab$l_nam = (struct namdef*)&SearchNaml; ENAMEL_RMS_NAML(SearchNaml) SearchNaml.naml$l_long_filename = ShelfSpec; SearchNaml.naml$l_long_filename_size = strlen(ShelfSpec); SearchNaml.naml$l_long_expand = ExpFileName; SearchNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1; SearchNaml.naml$l_long_result = ResFileName; SearchNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1; } else #endif /* ODS_EXTENDED */ { SearchFab.fab$l_fna = ShelfSpec; SearchFab.fab$b_fns = strlen(ShelfSpec); SearchFab.fab$l_nam = &SearchNam; SearchNam = cc$rms_nam; SearchNam.nam$l_esa = ExpFileName; SearchNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; SearchNam.nam$l_rsa = ResFileName; SearchNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } if (VMSnok (status = sys$parse (&SearchFab, 0, 0))) { if (ShelfSpec[0]) CgiLibResponseError (FI_LI, status, ShelfSpec); else CgiLibResponseError (FI_LI, status, ShelfFilePtr); *cptr = c; return (status); } *(ShelfFilePtr = cptr) = c; #ifdef ODS_EXTENDED if (OdsExtended) SearchNaml.naml$l_long_ver[SearchNaml.naml$l_long_ver_size] = '\0'; else #endif /* ODS_EXTENDED */ SearchNam.nam$l_ver[SearchNam.nam$b_ver] = '\0'; if (Debug) fprintf (stdout, "ExpFileName |%s|\n", ExpFileName); status = sys$search (&SearchFab, 0, 0); if (Debug) fprintf (stdout, "sys$search() %%X%08.08X\n", status); if (VMSok (status)) { #ifdef ODS_EXTENDED if (OdsExtended) *SearchNaml.naml$l_long_ver = '\0'; else #endif /* ODS_EXTENDED */ *SearchNam.nam$l_ver = '\0'; if (Debug) fprintf (stdout, "ResFileName |%s|\n", ResFileName); status = ProcessShelf (ResFileName); if (Debug) fprintf (stdout, "ProcessShelf() %%X%08.08X\n", status); break; } /* try with the next combination of defaults (if any) */ if (status == RMS$_FNF) continue; if (status == RMS$_DNF) continue; break; } if (!*ShelfFilePtr) { status = RMS$_FNF; CgiLibResponseError (FI_LI, status, OdlShelfDefaults); return (status); } else { if (VMSnok (status)) { if (ShelfSpec[0]) cptr = ShelfSpec; else cptr = ExpFileName; CgiLibResponseError (FI_LI, status, cptr); return (status); } } } if (BookshelfCount) fprintf (stdout, "\n"); if (VMSok (status) && BookshelfCount) { ButtonBar (2); fprintf (stdout, "\n\n"); } else if (VMSnok (status)) { if (ShelfSpec[0]) cptr = ShelfSpec; else { #ifdef ODS_EXTENDED if (OdsExtended) SearchNaml.naml$l_long_ver[SearchNaml.naml$l_long_ver_size] = '\0'; else #endif /* ODS_EXTENDED */ SearchNam.nam$l_ver[SearchNam.nam$b_ver] = '\0'; cptr = ExpFileName; } CgiLibResponseError (FI_LI, status, cptr); } /* release any parse and search internal data structures (for CGIplus) */ SearchFab.fab$l_fna = "a:[b]c.d;"; SearchFab.fab$b_fns = 9; SearchFab.fab$b_dns = 0; #ifdef ODS_EXTENDED if (OdsExtended) SearchNaml.naml$b_nop = NAM$M_SYNCHK; else #endif /* ODS_EXTENDED */ SearchNam.nam$b_nop = NAM$M_SYNCHK; sys$parse (&SearchFab, 0, 0); } /*****************************************************************************/ /* Open the supplied shelf file name. Read each line in the file parsing the entry type (shelf or book), the file specification, and entry description (title of shelf or book). Close the file. */ int ProcessShelf (char* ShelfFileName) { boolean IsDecwShelf, IsOdlShelf, IsSameAsPrevTitle; int status, idx, LineCount, LineOutputCount; char ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1], FileName [ODS_MAX_FILE_NAME_LENGTH+1], HtmlTitle [256], Line [256], LinkPath [ODS_MAX_FILE_NAME_LENGTH+1], Scratch [ODS_MAX_FILE_NAME_LENGTH*2], ShelfDirectory [ODS_MAX_FILE_NAME_LENGTH+1], Title [256], Type [256], VmsLinkPath [ODS_MAX_FILE_NAME_LENGTH+1]; char *cptr, *sptr, *zptr, *ExpDevicePtr, *ExpNodePtr, *ExpNamePtr, *ExpTypePtr, *PossibleOdlShelfTitlePtr; struct FAB ShelfFab; #ifdef ODS_EXTENDED struct NAML ShelfNaml; #endif /* ODS_EXTENDED */ struct NAM ShelfNam; struct RAB ShelfRab; struct XABDAT ShelfXabDat; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessShelf() |%s|\n", ShelfFileName); ShelfFab = cc$rms_fab; ShelfFab.fab$b_fac = FAB$M_GET; ShelfFab.fab$b_shr = FAB$M_SHRGET; ShelfFab.fab$l_xab = &ShelfXabDat; ShelfXabDat = cc$rms_xabdat; #ifdef ODS_EXTENDED if (OdsExtended) { ShelfFab.fab$l_fna = (char*)-1; ShelfFab.fab$b_fns = 0; ShelfFab.fab$l_nam = (struct namdef*)&ShelfNaml; ENAMEL_RMS_NAML(ShelfNaml) ShelfNaml.naml$l_long_filename = ShelfFileName; ShelfNaml.naml$l_long_filename_size = strlen(ShelfFileName); ShelfNaml.naml$l_long_expand = ExpFileName; ShelfNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1; } else #endif /* ODS_EXTENDED */ { ShelfFab.fab$l_fna = ShelfFileName; ShelfFab.fab$b_fns = strlen(ShelfFileName); ShelfFab.fab$l_nam = &ShelfNam; ShelfNam = cc$rms_nam; ShelfNam.nam$l_esa = ExpFileName; ShelfNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; } status = sys$open (&ShelfFab, 0, 0); if (VMSnok (status)) { if (Debug) fprintf (stdout, "sys$open() %%X%08.08X\n", status); if (ShelfSpec[0]) cptr = ShelfSpec; else cptr = ShelfFileName; CgiLibResponseError (FI_LI, status, cptr); return (status); } #ifdef ODS_EXTENDED if (OdsExtended) { ExpNodePtr = ShelfNaml.naml$l_long_node; ExpDevicePtr = ShelfNaml.naml$l_long_dev; ExpNamePtr = ShelfNaml.naml$l_long_name; ExpTypePtr = ShelfNaml.naml$l_long_type; *ShelfNaml.naml$l_long_ver = '\0'; } else #endif /* ODS_EXTENDED */ { ExpNodePtr = ShelfNam.nam$l_node; ExpDevicePtr = ShelfNam.nam$l_dev; ExpNamePtr = ShelfNam.nam$l_name; ExpTypePtr = ShelfNam.nam$l_type; *ShelfNam.nam$l_ver = '\0'; } if (Debug) fprintf (stdout, "|%s|\n", ExpFileName); /* don't last-modified check if using DECW$BOOK to search for libraries */ if ((ShelfSpec[0] || DoBnu) && !DecwBookshelfDefined) { if (CgiHttpIfModifiedSincePtr[0]) { if (VMSnok (ModifiedSince (&IfModifiedSinceBinaryTime, &ShelfXabDat.xab$q_rdt))) { /* book has not been modified since the date/time, don't send */ sys$close (&ShelfFab, 0, 0); return (STS$K_ERROR); } } } HttpGmTimeString (LastModifiedGmDateTime, &ShelfXabDat.xab$q_rdt); ShelfRab = cc$rms_rab; ShelfRab.rab$l_fab = &ShelfFab; /* 2 buffers and read ahead performance option */ ShelfRab.rab$b_mbf = 2; ShelfRab.rab$l_rop = RAB$M_RAH; ShelfRab.rab$l_ubf = Line; ShelfRab.rab$w_usz = sizeof(Line)-1; if (VMSnok (status = sys$connect (&ShelfRab, 0, 0))) { if (Debug) fprintf (stdout, "sys$connect() %%X%08.08X\n", status); sys$close (&ShelfFab, 0, 0); if (ShelfSpec[0]) cptr = ShelfSpec; else cptr = ShelfFileName; CgiLibResponseError (FI_LI, status, cptr); return (status); } /* convert the VMS file path into a slash-separated, sort-of equivalent */ VmsToPath (ShelfPath, ExpFileName); if (Debug) fprintf (stdout, "|%s|\n", ExpTypePtr); IsDecwShelf = IsOdlShelf = false; if (!strcmp (ExpTypePtr, ".ODL")) IsOdlShelf = true; else IsDecwShelf = true; if (++BookshelfCount == 1) { /********************/ /* first/only shelf */ /********************/ sptr = HyperReaderReferer; sptr += CgiLibUrlEncode (CgiScriptNamePtr, -1, sptr, -1); if (CgiPathInfoPtr[0] != '/') *sptr++ = '/'; sptr += CgiLibUrlEncode (CgiPathInfoPtr, -1, sptr, -1); sptr += CgiLibUrlEncode ("?title=", -1, sptr, -1); /* This escapes the escape percentages leaving the URI still escaped when the script (HyperReader) receives it at the other end :^) */ CgiLibUrlEncode (CgiFormTitlePtr, -1, Scratch, -1); sptr += CgiLibUrlEncode (Scratch, -1, sptr, -1); sptr += CgiLibUrlEncode ("&referer=", -1, sptr, -1); sptr += CgiLibUrlEncode (UrlEncReferer, -1, sptr, -1); *sptr = '\0'; if (Debug) fprintf (stdout, "HyperReaderReferer |%s|\n", HyperReaderReferer); /***************/ /* HTTP header */ /***************/ CgiLibResponseHeader (200, "text/html", "Last-Modified: %s\n", LastModifiedGmDateTime); } /****************/ /* file records */ /****************/ LineCount = LineOutputCount = 0; HtmlTitle[0] = PrevTitle[0] = UrlTitle[0] = '\0'; while (VMSok (status = sys$get (&ShelfRab, 0, 0))) { LineCount++; Line[ShelfRab.rab$w_rsz] = '\0'; if (Debug) fprintf (stdout, "Line |%s|\n", Line); if (!Line[0]) continue; /********************/ /* parse components */ /********************/ cptr = Line; if ((cptr[0] == '#' && cptr[1] == '#' && isalpha(cptr[2])) || (cptr[0] == '!' && cptr[1] == '!' && isalpha(cptr[2]))) cptr += 2; /* ignore unknown line types */ if (!isalpha(*cptr)) continue; Type[0] = FileName[0] = Title[0] = '\0'; sptr = Type; while (*cptr && isspace(*cptr)) cptr++; if (IsOdlShelf) { while (*cptr && !isspace(*cptr)) *sptr++ = toupper(*cptr++); *sptr = '\0'; while (*cptr && isspace(*cptr)) cptr++; } else { while (*cptr && *cptr != '\\' && !isspace(*cptr)) *sptr++ = toupper(*cptr++); *sptr = '\0'; while (*cptr && isspace(*cptr)) cptr++; if (*cptr == '\\') cptr++; while (*cptr && isspace(*cptr)) cptr++; } PossibleOdlShelfTitlePtr = cptr; if (IsOdlShelf) { sptr = FileName; while (*cptr && !isspace(*cptr)) { *sptr++ = *cptr++; if (*cptr == '%') { *sptr++ = '2'; *sptr++ = '5'; } } *sptr = '\0'; while (*cptr && isspace(*cptr)) cptr++; } else { /* check if this specification has device and directory components */ for (zptr = cptr; *zptr && *zptr != ':' && *zptr != '[' && *zptr != '/' && *zptr != '\\' && !isspace(*zptr); zptr++); if (*zptr == ':' || *zptr == '[' || *zptr == '/') { /* includes directory components, straight-up copy */ sptr = FileName; while (*cptr && *cptr != '\\' && !isspace(*cptr)) *sptr++ = tolower(*cptr++); *sptr = '\0'; } else { /* use the device and directory of the current shelf */ sptr = FileName; for (zptr = ExpNodePtr; zptr < ExpNamePtr; *sptr++ = tolower(*zptr++)); while (*cptr && *cptr != '\\' && !isspace(*cptr)) *sptr++ = tolower(*cptr++); *sptr = '\0'; } while (*cptr && isspace(*cptr)) cptr++; if (*cptr == '\\') cptr++; while (*cptr && isspace(*cptr)) cptr++; } sptr = Title; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; /***************/ /* shelf title */ /***************/ if (Type[0] == 'T' && strsame (Type, "TITLE", -1)) { /* title of this shelf */ if (IsOdlShelf) { sptr = Title; for (cptr = PossibleOdlShelfTitlePtr; *cptr; *sptr++ = *cptr++); *sptr = '\0'; cptr = "ODL"; } else cptr = "DECW$BOOKSHELF"; if (!Title[0]) sprintf (Title, "[Unable to parse %s shelf title (line %d)]", cptr, LineCount); if (Debug) fprintf (stdout, "TITLE |%s|\n", Title); if (!HtmlTitle[0]) { CgiLibHtmlEscape (Title, -1, HtmlTitle, -1); CgiLibUrlEncode (Title, -1, UrlTitle, -1); } } if (BookshelfCount == 1) { /***********************************/ /* first/only shelf ... page title */ /***********************************/ if (!PageBegun) { BeginPage (HtmlTitle); PageBegun = true; } } if (BookshelfCount == 1 && BookshelfCount != PrevBookshelfCount) { /* note that we are now working on a new bookshelf file */ PrevBookshelfCount = BookshelfCount; fprintf (stdout, "\n", ExpFileName); } if (BookshelfCount > 1) { /* Bookreader type search list through libraries */ if (Type[0] == 'T' && strsame (Type, "TITLE", -1)) break; continue; } if (Type[0] == 'T' && strsame (Type, "TITLE", -1)) continue; /*********************/ /* shelf or document */ /*********************/ CgiLibHtmlEscape (Title, -1, HtmlTitle, -1); CgiLibUrlEncode (Title, -1, UrlTitle, -1); if (strsame (Title, PrevTitle, -1) || strsame (Title, "ditto", 5)) IsSameAsPrevTitle = true; else IsSameAsPrevTitle = false; LinkPath[0] = VmsLinkPath[0] = '\0'; if (FileName[0]) { if (IsOdlShelf) { /*************/ /* ODL shelf */ /*************/ if (FileName[0] == '%') { /* full path was specified */ sptr = LinkPath; for (cptr = "/disk$"; *cptr; *sptr++ = *cptr++); for (cptr = FileName+1; *cptr; *sptr++ = tolower(*cptr++)); *sptr = '\0'; } else if (FileName[0] == '/') { /* full path was specified */ sptr = LinkPath; for (cptr = FileName; *cptr; *sptr++ = tolower(*cptr++)); *sptr = '\0'; } else { sptr = LinkPath; for (cptr = CgiPathInfoPtr; *cptr; *sptr++ = *cptr++); *sptr = '\0'; while (*sptr != '/' && sptr > LinkPath) sptr--; if (*sptr == '/') sptr++; for (cptr = FileName; *cptr; *sptr++ = tolower(*cptr++)); *sptr = '\0'; } if (Debug) fprintf (stdout, "LinkPath |%s|\n", LinkPath); } else { /************************/ /* DECW$BOOKSHELF shelf */ /************************/ strcpy (VmsLinkPath, FileName); } } if (Debug) fprintf (stdout, "|%s|%s|%s|\n", FileName, LinkPath, VmsLinkPath); /*******************/ /* output the HTML */ /*******************/ LineOutputCount++; fprintf (stdout, "\n"); if (!Type[0] || !FileName[0]) fprintf (stdout, "ERROR-IN-SHELF-ENTRY: Line %d", LineCount); else if (Type[0] == 'S' && strsame (Type, "SHELF", -1)) { /*********/ /* shelf */ /*********/ if (IsOdlShelf) ItemLink (UrlTitle, CgiScriptNamePtr, LinkPath, ".odl", "shelf.gif", " _|\\_"); else ItemLink (UrlTitle, CgiScriptNamePtr, VmsLinkPath, ".decw$bookshelf", "shelf.gif", "_|\\_"); } else if (Type[0] == 'B' && strsame (Type, "BOOK", -1)) { /********/ /* book */ /********/ if (!Title[0] || IsSameAsPrevTitle) strcpy (HtmlTitle, "(Bookreader version)"); if (IsOdlShelf) ItemLink (UrlTitle, HyperReaderScriptNamePtr, LinkPath, ".bkb", "book.gif", "[}{]"); else ItemLink (UrlTitle, HyperReaderScriptNamePtr, VmsLinkPath, ".decw$book", "book.gif", "[}{]"); } else if (Type[0] == 'H' && strsame (Type, "HTML", -1)) { /****************/ /* HTML version */ /****************/ if (!Title[0] || IsSameAsPrevTitle) strcpy (HtmlTitle, "(html version)"); /* convert the VMS file path into a sort-of URL equivalent */ if (VmsLinkPath[0]) VmsToPath (LinkPath, VmsLinkPath); if (IsOdlShelf) cptr = ".htm"; else cptr = ".html"; ItemLink (NULL, "", LinkPath, cptr, "html.gif", "[htm]"); } else if (Type[0] == 'P' && strsame (Type, "PDF", -1)) { /*********************************************/ /* Adobe PDF version (HyperReader extension) */ /*********************************************/ if (!Title[0] || IsSameAsPrevTitle) strcpy (HtmlTitle, "(pdf version)"); /* convert the VMS file path into a sort-of URL equivalent */ if (VmsLinkPath[0]) VmsToPath (LinkPath, VmsLinkPath); ItemLink (NULL, "", LinkPath, ".pdf", "pdf.gif", "[pdf]"); } else if (Type[0] == 'P' && strsame (Type, "PLAIN", -1)) { /**********************************************/ /* plain-text version (HyperReader extension) */ /**********************************************/ if (!Title[0] || IsSameAsPrevTitle) strcpy (HtmlTitle, "(plain-text version)"); /* convert the VMS file path into a sort-of URL equivalent */ if (VmsLinkPath[0]) VmsToPath (LinkPath, VmsLinkPath); ItemLink (NULL, "", LinkPath, ".txt", "plain.gif", "[txt]"); if (PrintScriptNamePtr[0]) ItemLink (NULL, PrintScriptNamePtr, LinkPath, ".txt", "print.gif", "[prn]"); } else if ((Type[0] == 'P' && strsame (Type, "POST", 4)) || (Type[0] == 'P' && strsame (Type, "PS", -1))) { /**********************************************/ /* PostScript version (HyperReader extension) */ /**********************************************/ if (!Title[0] || IsSameAsPrevTitle) strcpy (HtmlTitle, "(PostScript version)"); /* convert the VMS file path into a sort-of URL equivalent */ if (VmsLinkPath[0]) VmsToPath (LinkPath, VmsLinkPath); ItemLink (NULL, "", LinkPath, ".ps", "ps.gif", "[.ps]"); if (PrintScriptNamePtr[0]) ItemLink (NULL, PrintScriptNamePtr, LinkPath, ".ps", "print.gif", "[prn]"); } else if (Type[0] == 'U' && strsame (Type, "URL", -1)) { /*******************************************/ /* URL (HTML link - HyperReader extension) */ /*******************************************/ if (!Title[0]) strcpy (HtmlTitle, FileName); ItemLink (NULL, "", FileName, NULL, "url.gif", "[url]"); } else { /*****************/ /* unknown agent */ /*****************/ if (!Title[0] || IsSameAsPrevTitle) strcpy (HtmlTitle, "(ditto)"); ItemLink (UrlTitle, "", LinkPath, "", "unknown.gif", "[???]"); } fprintf (stdout, "%s\n", HtmlTitle); strcpy (PrevTitle, Title); } /****************/ /* end of shelf */ /****************/ sys$close (&ShelfFab, 0, 0); if (status == RMS$_EOF) status = SS$_NORMAL; if (VMSnok (status)) { CgiLibHtmlEscape (CgiFormTitlePtr, -1, HtmlTitle, -1); if (HtmlTitle[0]) cptr = HtmlTitle; else cptr = CgiPathInfoPtr; CgiLibResponseError (FI_LI, status, cptr); return (status); } if (BookshelfCount > 1) { /************************************/ /* bookreader search list libraries */ /************************************/ if (BookshelfCount != PrevBookshelfCount) { /* note that we are now working on a new bookshelf file */ PrevBookshelfCount = BookshelfCount; fprintf (stdout, "\n", ExpFileName); } if (LineCount) { fprintf (stdout, "\n"); if (IsOdlShelf) ItemLink (UrlTitle, CgiScriptNamePtr, VmsToPath(NULL, ShelfFileName), ".odl", "shelf.gif", " _|\\_"); else { for (cptr = ShelfFileName; *cptr; cptr++) *cptr = tolower(*cptr); ItemLink (UrlTitle, CgiScriptNamePtr, ShelfFileName, ".decw$bookshelf", "shelf.gif", "_|\\_"); } if (HtmlTitle[0]) fprintf (stdout, " %s\   [library]\n", HtmlTitle); else fprintf (stdout, " library %d\   [library]\n", BookshelfCount); } else fprintf (stdout, " Empty bookshelf: %s\n", VmsToPath (NULL, ShelfFileName)); } else if (!LineOutputCount) { if (!PageBegun) { BeginPage (HtmlTitle); PageBegun = true; } if (BookshelfCount != PrevBookshelfCount) { /* note that we are now working on a new bookshelf file */ PrevBookshelfCount = BookshelfCount; fprintf (stdout, "\n", ExpFileName); } fprintf (stdout, " Empty bookshelf: %s\n", VmsToPath (NULL, ShelfFileName)); } return (SS$_NORMAL); } /*****************************************************************************/ /* */ BeginPage (char *HtmlTitle) { char *cptr, *ShelfModePtr; char UnixDateTime [32]; unsigned long UnixTime; struct tm *UnixTmPtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "BeginPage() |%s|\n", HtmlTitle); time (&UnixTime); UnixTmPtr = localtime (&UnixTime); if (!strftime (UnixDateTime, sizeof(UnixDateTime), "%a, %d %b %Y %T", UnixTmPtr)) strcpy (UnixDateTime, "[error]"); if (Debug) fprintf (stdout, "UnixDateTime |%s|\n", UnixDateTime); if (!HtmlTitle[0]) CgiLibHtmlEscape (CgiFormTitlePtr, -1, HtmlTitle, -1); if (DoBnu) ShelfModePtr = "BNU"; else ShelfModePtr = "Bookreader"; fprintf (stdout, "\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ HyperShelf ... %s\n\ \n\ %s\ \n\ \n", SoftwareID, SoftwareCopy, CgiEnvironmentPtr, ShelfModePtr, UnixDateTime, HtmlTitle, DefaultStyle, StyleSheetPtr); fprintf (stdout, "
%s
\n", HtmlTitle); ButtonBar (1); fflush (stdout); fputs ("\n", stdout); } /*****************************************************************************/ /* Create a link for a single shelf item. */ ItemLink ( char *UrlTitle, char *ScriptNamePtr, char *LinkPath, char *DefaultType, char *IconName, char *AltText ) { char *cptr; char EncodedLinkPath [ODS_MAX_FILE_NAME_LENGTH+64+1]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ItemLink() |%s|%s|%s|%s|%s|%s|\n", UrlTitle, ScriptNamePtr, LinkPath, DefaultType, IconName, AltText); if (!DefaultType) { /* default file type is specified as NULL, must be a raw URL */ fprintf (stdout, "\"%s\"\n", LinkPath, IconLocationPtr, IconName, AltText); return; } /* look for file type in specification */ for (cptr = LinkPath; *cptr; cptr++); while (cptr > LinkPath && *cptr != '.' && *cptr != '/' && *cptr != ']' && *cptr != ':') cptr--; if (*cptr != '.') { /* no file type supplied, default to one! */ while (*cptr) cptr++; strcat (LinkPath, DefaultType); } /* now scan backwards to check for likely VMS specification */ while (cptr > LinkPath && *cptr != '/' && *cptr != ']' && *cptr != ':') cptr--; if (*cptr == ']' || *cptr == ':') { /* link has VMS file specification */ CgiLibUrlEncodeFileName (LinkPath, EncodedLinkPath, sizeof(EncodedLinkPath), true, true); if (!UrlTitle) { fprintf (stdout, "\ \"%s\"\n", ScriptNamePtr, EncodedLinkPath, IconLocationPtr, IconName, AltText); } else { fprintf (stdout, "\ \"%s\"\n", ScriptNamePtr, EncodedLinkPath, UrlTitle, UrlEncReferer, IconLocationPtr, IconName, AltText); } } else { /* link has URL-style path */ CgiLibUrlEncode (LinkPath, -1, EncodedLinkPath, sizeof(EncodedLinkPath)); if (!UrlTitle) { fprintf (stdout, "\ \"%s\"\n", ScriptNamePtr, EncodedLinkPath, IconLocationPtr, IconName, AltText); } else { fprintf (stdout, "\ \"%s\"\n", ScriptNamePtr, EncodedLinkPath, UrlTitle, UrlEncReferer, IconLocationPtr, IconName, AltText); } } } /*****************************************************************************/ /* Create the navigation buttons. */ ButtonBar (int Top1Bottom2) { int idx; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ButtonBar()\n"); fprintf (stdout, "
\n", Top1Bottom2 == 1 ? " butbartop" : ""); cptr = ButtonsPtr; /* back */ cptr = ButtonBarButton (cptr, NULL); /* close */ if (HtmlReferer[0]) cptr = ButtonBarButton (cptr, HtmlReferer); else cptr = ButtonBarButton (cptr, NULL); /* further buttons (starting with "help") */ while (*cptr) cptr = ButtonBarButton (cptr, NULL); fprintf (stdout, "
\n"); } /*****************************************************************************/ /* Generate a single "button" inside the context created by ButtonBar(). */ char* ButtonBarButton ( char *ButtonLabel, char *ButtonPath ) { char *cptr, *sptr, *tptr, *uptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ButtonBarButton() |%s|\n", ButtonLabel); if (!*ButtonLabel) for (cptr = ButtonLabel = "*ERROR*"; *cptr; cptr++); else for (cptr = ButtonLabel; *cptr && *cptr != '=' && *cptr != '$'; cptr++); if (*ButtonLabel == '^') { ButtonLabel++; tptr = " target=\"_blank\""; } else tptr = ""; if (*cptr == '=') for (sptr = uptr = cptr+1; *sptr && *sptr != '$'; sptr++); else if (ButtonPath) sptr = (uptr = ButtonPath) + strlen(ButtonPath); else sptr = (uptr = "*ERROR*") + 8; if (!memcmp(uptr,"javascript:", 11)) fprintf (stdout, "\n", tptr, sptr-uptr, sptr-uptr, uptr, cptr-ButtonLabel, cptr-ButtonLabel, ButtonLabel); else fprintf (stdout, "%*.*s\n", tptr, sptr-uptr, sptr-uptr, uptr, cptr-ButtonLabel, cptr-ButtonLabel, ButtonLabel); while (*cptr && *cptr != '$') cptr++; while (*cptr && *cptr == '$') cptr++; return (cptr); } /*****************************************************************************/ /* */ int SysTrnLnm ( char *LogicalName, int IndexNumber, char *LogicalValue ) { static unsigned short Length; static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static $DESCRIPTOR (LogicalNameDsc, ""); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { sizeof(int), LNM$_INDEX, 0, 0, }, { 255, LNM$_STRING, 0, &Length }, { 0,0,0,0 } }; int status; char *SignPtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SysTrnLnm() |%s| %d\n", LogicalName, IndexNumber); LogicalValue[0] = '\0'; LogicalNameDsc.dsc$a_pointer = LogicalName; LogicalNameDsc.dsc$w_length = strlen(LogicalName); LnmItems[0].buf_addr = &IndexNumber; LnmItems[1].buf_addr = LogicalValue; status = sys$trnlnm (0, &LnmFileDevDsc, &LogicalNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (VMSnok (status)) return (status); LogicalValue[Length] = '\0'; if (Debug) fprintf (stdout, "LogicalValue |%s|\n", LogicalValue); return (SS$_NORMAL); } /*****************************************************************************/ /* Convert a VMS file specification into a URL-style specification. For example: "DEVICE:[DIR1.DIR2]FILE.TXT" into "/device/dir1/dir2/file.txt". OSU and Apache environments require a MFD (i.e. 000000) to remain in a Unix-stlye specification, whereas with WASD it's optional (and usually not present). */ char* VmsToPath ( char *PathPtr, char *VmsPtr ) { static char Path [ODS_MAX_FILE_NAME_LENGTH+1]; char *pptr, *vptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VmsToPath() |%s|\n", VmsPtr); vptr = VmsPtr; if (!(pptr = PathPtr)) pptr = PathPtr = Path; *pptr++ = '/'; /* copy the device and directory components */ while (*vptr) { if (*vptr == ':' && *(vptr+1) == '[') { vptr++; vptr++; /* remove any reference to a Master File Directory */ if (WasdEnvironment && strncmp (vptr, "000000", 6) == 0) vptr += 6; else *pptr++ = '/'; } if (*vptr == '.') { if (vptr[1] == '.' && vptr[2] == '.') { *pptr++ = '/'; *pptr++ = *vptr++; *pptr++ = *vptr++; *pptr++ = *vptr++; } else { vptr++; *pptr++ = '/'; } } else if (*vptr == ']') { vptr++; *pptr++ = '/'; break; } else *pptr++ = tolower(*vptr++); } /* copy the file component */ while (*vptr) *pptr++ = tolower(*vptr++); *pptr++ = '\0'; *pptr = '\0'; if (Debug) fprintf (stdout, "PathPtr |%s|\n", PathPtr); return (PathPtr); } /*****************************************************************************/ /* If the object has been modified since the specified date and time then return a normal status indicating that the data transfer is to continue. If not modified then send a "not modified" HTTP header and return an error status to indicate the object should not be sent. */ int ModifiedSince ( unsigned long *SinceBinaryTimePtr, unsigned long *BinaryTimePtr ) { static unsigned long OneSecondDelta [2] = { -10000000, -1 }; int status; unsigned long AdjustedBinTime [2], ScratchBinTime [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ModifiedSince()\n"); /* if request asks for a "reload" (not cached) then give it regardless */ if (strsame (CgiHttpPragmaPtr, "no-cache", -1)) return (SS$_NORMAL); if (strsame (CgiHttpCacheControlPtr, "no-cache", 8)) return (SS$_NORMAL); if (strsame (CgiHttpCacheControlPtr, "no-store", 8)) return (SS$_NORMAL); if (strsame (CgiHttpCacheControlPtr, "max-age=0", 9)) return (SS$_NORMAL); /* Add one second to the modified time. Ensures a negative time for VMS where fractional seconds may result in inconclusive results when the target time is being specified in whole seconds. */ if (VMSnok (status = lib$add_times (SinceBinaryTimePtr, &OneSecondDelta, &AdjustedBinTime))) { CgiLibResponseError (FI_LI, status, "sys$add_times()"); return (status); } if (Debug) fprintf (stdout, "sys$add_times() %%X%08.08X\n", status); /* if a positive time results the file has been modified */ if (VMSok (status = lib$sub_times (BinaryTimePtr, &AdjustedBinTime, &ScratchBinTime))) return (status); if (Debug) fprintf (stdout, "sys$sub_times() %%X%08.08X\n", status); if (status != LIB$_NEGTIM) { CgiLibResponseError (FI_LI, status, "sys$sub_times()"); return (status); } CgiLibResponseHeader (304, "text/html"); fprintf (stdout, "Not modified!\n"); return (LIB$_NEGTIM); } /*****************************************************************************/ /* Create an HTTP format Greenwich Mean Time (UTC) time string in the storage pointed at by 'TimeString', e.g. "Fri, 25 Aug 1995 17:32:40 GMT" (RFC 1123). This must be at least 30 characters capacity. If 'BinTimePtr' is null the time string represents the current time. If it points to a quadword, VMS time value the string represents that time. 'TimeString' must point to storage large enough for 31 characters. */ int HttpGmTimeString ( char *TimeString, unsigned long *BinTimePtr ) { static char *DayNames [] = { "", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; static char *MonthName [] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static $DESCRIPTOR (HttpTimeFaoDsc, "!AZ, !2ZW !AZ !4ZW !2ZW:!2ZW:!2ZW GMT"); static $DESCRIPTOR (TimeStringDsc, ""); int status; unsigned long BinTime [2], GmTime [2]; unsigned short Length; unsigned short NumTime [7]; unsigned long DayOfWeek; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HttpGmTimeString()\n"); if (!BinTimePtr) sys$gettim (&GmTime); else { GmTime[0] = BinTimePtr[0]; GmTime[1] = BinTimePtr[1]; } if (VMSnok (status = TimeAdjustGMT (true, &GmTime))) return (status); status = sys$numtim (&NumTime, &GmTime); if (Debug) fprintf (stdout, "sys$numtim() %%X%08.08X %d %d %d %d %d %d %d\n", status, NumTime[0], NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6]); if (VMSnok (status = lib$day_of_week (&GmTime, &DayOfWeek))) return (status); if (Debug) fprintf (stdout, "lib$day_of_week() %%X%08.08X is %d\n", status, DayOfWeek); /* set the descriptor address and size of the resultant time string */ TimeStringDsc.dsc$w_length = 30; TimeStringDsc.dsc$a_pointer = TimeString; if (VMSnok (status = sys$fao (&HttpTimeFaoDsc, &Length, &TimeStringDsc, DayNames[DayOfWeek], NumTime[2], MonthName[NumTime[1]], NumTime[0], NumTime[3], NumTime[4], NumTime[5]))) { if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status); TimeString[0] = '\0'; } else TimeString[Length] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", TimeString); return (status); } /*****************************************************************************/ /* Given a string such as "Fri, 25 Aug 1995 17:32:40 GMT" (RFC 1123), or "Friday, 25-Aug-1995 17:32:40 GMT" (RFC 1036), create an internal, local, binary time with the current GMT offset. See complementary function HttpGmTimeString(). */ int HttpGmTime ( char *TimeString, unsigned long *BinTimePtr ) { static char *MonthName [] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; int status; unsigned short Length; unsigned short NumTime [7] = { 0,0,0,0,0,0,0 }; unsigned long DayOfWeek; char *tptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HttpGmTime() |%s|\n", TimeString); tptr = TimeString; /* hunt straight for the comma after the weekday name! */ while (*tptr && *tptr != ',') tptr++; if (*tptr) tptr++; /* span white space between weekday name and date */ while (*tptr && isspace(*tptr)) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the date and then skip to month name */ if (isdigit(*tptr)) NumTime[2] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; while (*tptr && (*tptr == '-' || isspace(*tptr))) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the month number from the name and skip to the year */ for (NumTime[1] = 1; NumTime[1] <= 12; NumTime[1]++) if (strsame (tptr, MonthName[NumTime[1]], 3)) break; if (NumTime[1] > 12) return (STS$K_ERROR); while (*tptr && isalpha(*tptr)) tptr++; while (*tptr && (*tptr == '-' || isspace(*tptr))) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the year and then skip to the hour */ if (isdigit(*tptr)) { NumTime[0] = atoi (tptr); if (NumTime[0] < 100) NumTime[0] += 1900; } while (*tptr && isdigit(*tptr)) tptr++; while (*tptr && isspace(*tptr)) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the hour, minute and second */ if (isdigit(*tptr)) NumTime[3] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; if (*tptr == ':') tptr++; if (isdigit(*tptr)) NumTime[4] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; if (*tptr == ':') tptr++; if (isdigit(*tptr)) NumTime[5] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; if (*tptr == ':') tptr++; if (!*tptr) return (STS$K_ERROR); /* the only thing remaining should be the "GMT" */ while (*tptr && isspace(*tptr)) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!strsame (tptr, "GMT", 3)) return (STS$K_ERROR); /*******************************************/ /* convert what looks like legitimate GMT! */ /*******************************************/ if (Debug) fprintf (stdout, "NumTime[] %d %d %d %d %d %d %d\n", NumTime[0], NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6]); status = lib$cvt_vectim (&NumTime, BinTimePtr); if (VMSnok (status)) return (status); if (Debug) fprintf (stdout, "lib$cvt_vectim() %%X%08.08X\n", status); return (TimeAdjustGMT (false, BinTimePtr)); } /*****************************************************************************/ /* Determine the offset from GMT (UTC) using either the HTTPD$GMT or SYS$TIMEZONE_DIFFERENTIAL logicals. If HTTPD$GMT is not defined time should be set from the timezone differential logical. Function RequestBegin() calls this every hour to recheck GMT offset and detect daylight saving or other timezone changes. */ int TimeSetGmt () { static boolean UseTimezoneDifferential = false; int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeSetGmt()\n"); if (!UseTimezoneDifferential) { status = TimeSetHttpdGmt(); /* return if error and that error was not that the name did not exist */ if (VMSok (status) || status != SS$_NOLOGNAM) return (status); } UseTimezoneDifferential = true; return (TimeSetTimezone()); } /*****************************************************************************/ /* The SYS$TIMEZONE_DIFFERENTIAL logical contains the number of seconds offset from GMT (UTC) as a positive (ahead) or negative (behind) number. Set the 'TimeGmtString' global storage to a "+hh:mm" or "-hh:mm" string equivalent, and the 'TimeAheadOfGmt' global boolean and 'TimeGmtVmsString' delta-time global string. */ int TimeSetTimezone () { static unsigned short Length; static $DESCRIPTOR (TimezoneLogicalNameDsc, "SYS$TIMEZONE_DIFFERENTIAL"); static $DESCRIPTOR (LnmSystemDsc, "LNM$SYSTEM"); static $DESCRIPTOR (TimeGmtStringFaoDsc, "!AZ!2ZL:!2ZL"); static $DESCRIPTOR (TimeGmtVmsStringFaoDsc, "0 !2ZL:!2ZL"); static $DESCRIPTOR (TimeGmtStringDsc, TimeGmtString); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { sizeof(TimeGmtString)-1, LNM$_STRING, TimeGmtString, &Length }, { 0,0,0,0 } }; int status; long Hours, Minutes, Seconds; char *SignPtr; $DESCRIPTOR (TimeGmtVmsStringDsc, TimeGmtVmsString); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeSetTimezone()\n"); status = sys$trnlnm (0, &LnmSystemDsc, &TimezoneLogicalNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (VMSnok (status)) return (status); TimeGmtString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString); Seconds = atol(TimeGmtString); if (Seconds < 0) { TimeAheadOfGmt = false; Seconds = -Seconds; SignPtr = "-"; } else { TimeAheadOfGmt = true; SignPtr = "+"; } Hours = Seconds / 3600; Minutes = (Seconds - Hours * 3600) / 60; if (Debug) fprintf (stdout, "%d %s%d:%d\n", Seconds, SignPtr, Hours, Minutes); sys$fao (&TimeGmtStringFaoDsc, &Length, &TimeGmtStringDsc, SignPtr, Hours, Minutes); TimeGmtString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString); sys$fao (&TimeGmtVmsStringFaoDsc, &Length, &TimeGmtVmsStringDsc, Hours, Minutes); TimeGmtVmsString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtVmsString |%s|\n", TimeGmtVmsString); TimeGmtVmsStringDsc.dsc$w_length = Length; if (VMSnok (status = sys$bintim (&TimeGmtVmsStringDsc, &TimeGmtDeltaBinary))) return (status); if (TimeGmtDeltaBinary[0] || TimeGmtDeltaBinary[1]) return (status); /* time must have been zero, make it one, one-hundreth of a second */ TimeGmtDeltaBinary[0] = -100000; TimeGmtDeltaBinary[1] = -1; return (SS$_NORMAL); } /*****************************************************************************/ /* Translate the logical HTTPD$GMT (defined to be something like "+10:30" or "- 01:15") and convert it into a delta time structure and store in 'TimeGmtDeltaBinary'. Store whether it is in advance or behind GMT in boolean 'TimeAheadOfGmt'. Store the logical string in 'TimeGmtString'. */ int TimeSetHttpdGmt () { static unsigned short Length; static $DESCRIPTOR (GmtLogicalNameDsc, "HTTPD$GMT"); static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { sizeof(TimeGmtString)-1, LNM$_STRING, TimeGmtString, &Length }, { 0,0,0,0 } }; int status; char *cptr, *sptr; $DESCRIPTOR (TimeGmtVmsStringDsc, TimeGmtVmsString); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeSetHttpdGmt()\n"); status = sys$trnlnm (0, &LnmFileDevDsc, &GmtLogicalNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (VMSnok (status)) return (status); TimeGmtString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString); if (TimeGmtString[0] == '$') return (SS$_NORMAL); if (*(cptr = TimeGmtString) == '-') TimeAheadOfGmt = false; else TimeAheadOfGmt = true; if (*cptr == '+' || *cptr == '-') cptr++; sptr = TimeGmtVmsString; *sptr++ = '0'; *sptr++ = ' '; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; if (Debug) fprintf (stdout, "TimeGmtVmsString |%s|\n", TimeGmtVmsString); TimeGmtVmsStringDsc.dsc$w_length = sptr - TimeGmtVmsString; if (VMSnok (status = sys$bintim (&TimeGmtVmsStringDsc, &TimeGmtDeltaBinary))) return (status); if (TimeGmtDeltaBinary[0] || TimeGmtDeltaBinary[1]) return (status); /* time must have been zero, make it one, one-hundreth of a second */ TimeGmtDeltaBinary[0] = -100000; TimeGmtDeltaBinary[1] = -1; return (SS$_NORMAL); } /*****************************************************************************/ /* The GMT is generated by calculating using an offset using 'TimeGmtDeltaOffset' and boolean 'TimeAheadOfGmt'. Adjust either to or from GMT. */ int TimeAdjustGMT ( boolean ToGmTime, unsigned long *BinTimePtr ) { int status; unsigned long AdjustedTime [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeAdjustGMT() ToGmTime: %d\n", ToGmTime); if ((ToGmTime && TimeAheadOfGmt) || (!ToGmTime && !TimeAheadOfGmt)) { /* to GMT from local and ahead of GMT, or to local from GMT and behind */ status = lib$sub_times (BinTimePtr, &TimeGmtDeltaBinary, &AdjustedTime); if (Debug) fprintf (stdout, "lib$sub_times() %%X%08.08X\n", status); } else { /* to GMT from local and behind GMT, or to local from GMT and ahead */ status = lib$add_times (BinTimePtr, &TimeGmtDeltaBinary, &AdjustedTime); if (Debug) fprintf (stdout, "lib$add_times() %%X%08.08X\n", status); } if (Debug) { unsigned short Length; char String [64]; $DESCRIPTOR (AdjustedTimeFaoDsc, "AdjustedTime: |!%D|\n"); $DESCRIPTOR (StringDsc, String); sys$fao (&AdjustedTimeFaoDsc, &Length, &StringDsc, &AdjustedTime); String[Length] = '\0'; fputs (String, stdout); } BinTimePtr[0] = AdjustedTime[0]; BinTimePtr[1] = AdjustedTime[1]; return (status); } /****************************************************************************/ /* Return an integer reflecting the major and minor version of VMS (e.g. 60, 61, 62, 70, 71, 72, etc.) */ #ifdef ODS_EXTENDED int GetVmsVersion () { static unsigned char SyiVersion [16]; static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } SyiItems [] = { { 8, SYI$_VERSION, &SyiVersion, 0 }, { 0,0,0,0 } }; int status, version; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetVmsVersion()\n"); if (VMSnok (status = sys$getsyiw (0, 0, 0, &SyiItems, 0, 0, 0))) exit (status); SyiVersion[8] = '\0'; version = ((SyiVersion[1]-48) * 10) + (SyiVersion[3]-48); if (Debug) fprintf (stdout, "|%s| %d\n", SyiVersion, version); return (version); } #endif /* ODS_EXTENDED */ /*****************************************************************************/ /* Return non-NULL pointer if looks a bit suspect! */ char* XSSuspect (char *cptr) { static char XSSredacted [] = "XSS redacted! (tsk-tsk)"; char *sptr, *zptr; char buf [1024]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "XSSuspect() |%s|\n", cptr); if (!cptr || !*cptr) return (NULL); if (strstr (cptr, "= zptr || strstr (buf, "