/*****************************************************************************/ /* Menu.c This module implements a full multi-threaded, AST-driven, asynchronous "menu" file send. 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. This module uses the NetWriteBuffered() function to buffer any output it can into larger chunks before sending it to the client. Implements the WASD menu file. A WASD menu comprises a file with 1, 2, 3 or more blank-line delimitted sections. The optional first is the document title. The optional second is a menu description. The mandatory section consists of the menu items. Any blank-line delimtted sections thereafter alternate between descriptive paragraphs and menu sections. For example: |This is the HTML line. It must be only one line.| || |This is the descriptive paragraph.| |It can be multi-line.| || |HT_DATA:FILE1.TXT This is the first text file.| |HT_DATA:FILE2.TXT This is the second text file.| |HT_DATA:FILE1.HTML This would be an HTML file.| || |This is another (optional) descriptive paragraph.| |It can also be multi-line.| || |HT_DATA:FILE1.TXT This is the first file of the second menu.| |HT_DATA:FILE2.TXT This is the second file, etc.| |!This is a comment| |*.TXT? This is a search item |!The next item lists all matching files from their internal description| |*.HTML| VERSION HISTORY --------------- 14-JUL-2006 MGD bugfix; MenuFileDescription() status from OdsParse() 05-OCT-2002 MGD refine VMS security profile usage 27-APR-2002 MGD make SYSPRV enabled ASTs asynchronous 04-AUG-2001 MGD support module WATCHing 30-DEC-2000 MGD rework for FILE.C getting file contents in-memory 01-SEP-2000 MGD add optional, local path authorization (for calls from the likes of SSI.C) 04-MAR-2000 MGD use FaoToBuffer(), et.al. 27-DEC-1999 MGD support ODS-2 and ODS-5 using ODS module 07-NOV-1998 MGD WATCH facility 17-AUG-1997 MGD message database, SYSUAF-authenticated users security-profile 27-FEB-1997 MGD delete on close for "temporary" files 01-FEB-1997 MGD HTTPd version 4 23-MAY-1996 MGD added listing from file descriptions (i.e. Description()) 28-MAR-1996 MGD bugfix, MenuMextContent() caused RequestEnd() to be called twice (two log entries only deleterious effect :^) 01-DEC-1995 MGD HTTPd version 3 27-SEP-1995 MGD added "If-Modified-Since:" functionality 07-AUG-1995 MGD ConfigcfReport.MetaInfoEnabled to allow physical file specification to be included as commentary within menu output 20-DEC-1994 MGD initial development for multi-threaded daemon */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include <ctype.h> #include <stdio.h> #include <string.h> /* VMS related header files */ #include <descrip.h> #include <libdef.h> #include <rms.h> #include <ssdef.h> #include <stsdef.h> /* application-related header files */ #include "wasd.h" #define WASD_MODULE "MENU" /********************/ /* external storage */ /********************/ extern int ToLowerCase[], ToUpperCase[]; extern unsigned long SysPrvMask[]; extern char ErrorSanityCheck[], SoftwareID[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern MSG_STRUCT Msgs; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Parse the contents of the file according to the menu statements. */ MenuBegin (REQUEST_STRUCT *rqptr) { FILE_CONTENT *fcptr; MENU_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (rqptr, FI_LI, WATCH_MOD__OTHER, "MenuBegin()"); if (ERROR_REPORTED (rqptr)) { /* previous error, cause threaded processing to unravel */ SysDclAst (rqptr->FileContentPtr->NextTaskFunction, rqptr); return; } /* set up the task structure (only ever one per request!) */ rqptr->MenuTaskPtr = tkptr = (MENU_TASK*)VmGetHeap (rqptr, sizeof(MENU_TASK)); /* take control of the file contents structure */ tkptr->FileContentPtr = fcptr = rqptr->FileContentPtr; rqptr->FileContentPtr = NULL; /* set up read to parse the file's body */ tkptr->LinePtr = tkptr->ParsePtr = fcptr->ContentPtr; if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, WATCH_RESPONSE, "MENU !AZ !UL bytes", fcptr->FileName, fcptr->ContentLength); /* network writes are checked for success, fudge the first one! */ rqptr->rqNet.WriteIOsb.Status = SS$_NORMAL; MenuContents (rqptr); } /*****************************************************************************/ /* End menu processing. No need to explicitly free the contents structure as there will only ever be one lot of menu processing per request and the contents structure will be cleaned up in the general request heap freeing. */ MenuEnd (REQUEST_STRUCT *rqptr) { MENU_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (rqptr, FI_LI, WATCH_MOD__OTHER, "MenuEnd()"); tkptr = rqptr->MenuTaskPtr; /* release internal RMS parse structures */ if (tkptr->FileOds.ParseInUse) OdsParseRelease (&tkptr->SearchOds); SysDclAst (tkptr->FileContentPtr->NextTaskFunction, rqptr); } /*****************************************************************************/ /* Process the lines of the menu file according to the total count of the sections (blank-line delimited text) in the file and the section number the current line falls within. */ MenuContents (REQUEST_STRUCT *rqptr) { int status; char *cptr, *sptr, *zptr; MENU_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (rqptr, FI_LI, WATCH_MOD__OTHER, "MenuContents()"); tkptr = rqptr->MenuTaskPtr; if (!*tkptr->ParsePtr) { if (!((tkptr->MenuSectionNumber-1) % 2)) NetWriteBuffered (rqptr, &MenuEnd, "<UL>\n</body>\n</html>\n", 21); else NetWriteBuffered (rqptr, &MenuEnd, "</body>\n</html>\n", 16); return; } tkptr->LinePtr = tkptr->ParsePtr; for (;;) { for (cptr = tkptr->ParsePtr; *cptr && *cptr != '\n'; cptr++); if (cptr > tkptr->ParsePtr && cptr[-1] == '\\') { /* continuation character, move line down two characters */ sptr = (zptr = cptr) - 2; while (sptr >= tkptr->LinePtr) *zptr-- = *sptr--; tkptr->LinePtr += 2; continue; } if (*cptr) *cptr++ = '\0'; tkptr->ParsePtr = cptr; break; } tkptr->LineLength = tkptr->ParsePtr - tkptr->LinePtr; if (!tkptr->LinePtr[0]) { /**************/ /* empty line */ /**************/ tkptr->MenuBlankLineCount++; tkptr->MenuLineCount = 0; SysDclAst (&MenuContents, rqptr); return; } if (tkptr->LinePtr[0] == '!') { /*********************/ /* comment-only line */ /*********************/ SysDclAst (&MenuContents, rqptr); return; } /******************/ /* non-blank line */ /******************/ if (tkptr->MenuBlankLineCount) tkptr->MenuSectionNumber++; tkptr->MenuLineCount++; tkptr->MenuBlankLineCount = 0; if (!tkptr->MenuSectionNumber) tkptr->MenuSectionNumber++; /******************/ /* interpret line */ /******************/ if (tkptr->MenuSectionNumber == 1 && tkptr->MenuLineCount == 1) MenuTitle (rqptr); else if (!(tkptr->MenuSectionNumber % 2)) MenuDescription (rqptr); else if (!((tkptr->MenuSectionNumber-1) % 2)) { if (tkptr->MenuLineCount == 1) NetWriteBuffered (rqptr, &MenuItems, "<p>\n<UL>\n", 9); else MenuItems (rqptr); } } /*****************************************************************************/ /* The line just read is the menu title. */ MenuTitle (REQUEST_STRUCT *rqptr) { static char TitleFao [] = "!AZ\ <html>\n\ <head>\n\ !AZ\ <title>!AZ\n\ \n\ !AZ\n\

!AZ

\n"; int status; unsigned long FaoVector [16]; unsigned long *vecptr; MENU_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (rqptr, FI_LI, WATCH_MOD__OTHER, "MenuTitle()"); tkptr = rqptr->MenuTaskPtr; /* should only be one line, ignore all but the first */ if (tkptr->MenuLineCount > 1) SysDclAst (&MenuContents, rqptr); vecptr = FaoVector; *vecptr++ = WASD_DOCTYPE; *vecptr++ = HtmlMetaInfo (rqptr, tkptr->FileContentPtr->FileName); *vecptr++ = tkptr->LinePtr; if (rqptr->ServicePtr->BodyTag[0]) *vecptr++ = rqptr->ServicePtr->BodyTag; else *vecptr++ = Config.cfServer.ReportBodyTag; *vecptr++ = tkptr->LinePtr; status = FaolToNet (rqptr, TitleFao, &FaoVector); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); SysDclAst (&MenuContents, rqptr); } /*****************************************************************************/ /* If this is the first (and possibly only) description section separate using a

tag. If the second (or more) description section first terminate the

\n

!AZ\n", tkptr->LinePtr); } } else { /* second or subsequent line of the description section */ FaoToNet (rqptr, "!AZ\n", tkptr->LinePtr); } SysDclAst (&MenuContents, rqptr); } /*****************************************************************************/ /* Interpret this line as a menu item comprising a URI, white-space, and a description comprising the rest of the line after the first non-space. Each line in the item section is output as an item in an non-ordered list, or