/*****************************************************************************/ /* cgi.c Due to the many ephemeral elements (logical names, system data, time) used in many documents CGIplus doesn't do that much. Basically only short-circuts the image activation. VERSION HISTORY --------------- 21-FEB-2019 MGD initial */ /*****************************************************************************/ #include #include #include #include "wasdoc.h" #include int cgiPlusCount; extern int dbug, isCgi, isCgiPlus; /*****************************************************************************/ /* CGI/CGIplus usage. */ void CgiDoc () { if (isCgiPlus) { for (;;) { /* block waiting for the next request */ CgiLibVar (""); cgiPlusCount++; CgiRequest (); CgiLibCgiPlusEOF (); } } else CgiRequest (); } /*****************************************************************************/ /* Process a CGI/CGIplus request. With CGI/CGIplus the path can have a trailing "fragment" that is used to inform the browser any actual #fragment may be in a different part of the document. This is used when the "piecemeal" option is enabled. */ void CgiRequest () { static char obvious [] = "~~~~~~~~~~~~~~~~~~~~~~~~~"; int error = 0, idx, insight = 0; char ch; char *aptr, *cptr, *sptr, *zptr; char pinfo [256] = "", pchunk [32] = "", ptrans [256] = "", stats [128], uri [256] = ""; time_t utime; struct wasdoc_st *docptr, *tocptr; /* WASD stream mode will stop each fflush() carriage control added */ CgiLibEnvironmentBinaryOut (); if (cptr = getenv ("WASDOC$DBUG")) if (!(dbug = atoi(cptr))) dbug = 1; if (dbug) CgiLibResponseHeader (200, "text/plain", "Script-Control: X-stream-mode\n"); CgiLibEnvironmentSetDebug (dbug); if (!(cptr = CgiLibVar ("REQUEST_METHOD"))) EXIT_FI_LI (SS$_BUGCHECK); if (!MATCH4 (cptr, "GET") && !MATCH5 (cptr, "HEAD")) { CgiLibResponseHeader (403, "text/html"); fprintf (stdout, "\nGET only!\n"); return; } if (!(cptr = CgiLibVar ("PATH_INFO"))) EXIT_FI_LI (SS$_BUGCHECK); zptr = (sptr = pinfo) + sizeof(pinfo)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (!(cptr = CgiLibVar ("PATH_TRANSLATED"))) EXIT_FI_LI (SS$_BUGCHECK); zptr = (sptr = ptrans) + sizeof(ptrans)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (!(cptr = CgiLibVar ("REQUEST_URI"))) EXIT_FI_LI (SS$_BUGCHECK); zptr = (sptr = uri) + sizeof(uri)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; for (cptr = uri; *cptr && *cptr != '?'; cptr++); if (MATCH6 (cptr, "?draw")) { drawAdHoc (ptrans); return; } /* does the URI have a trailing numeric directory element? */ for (cptr = uri; *cptr; cptr++); while (cptr > uri && *cptr != '/') cptr--; for (sptr = cptr; sptr > uri && isdigit(*(sptr-1)); sptr--); pchunk[0] = '\0'; if (sptr < cptr && isdigit(*sptr) && *(sptr-1) == '/') { /* looks like the uri has a trailing chunk number */ cptr = aptr = sptr; zptr = (sptr = pchunk) + sizeof(pchunk)-1; while (*cptr && isdigit(*cptr) && sptr < zptr) *sptr++ = *cptr++; *sptr = *aptr = '\0'; /* path info then also needs adjustment */ for (cptr = pinfo; *cptr; cptr++); while (cptr > pinfo && *cptr != '/') cptr--; for (sptr = cptr; sptr > pinfo && isdigit(*(sptr-1)); sptr--); *sptr = '\0'; /* translated path then also needs adjustment */ for (cptr = ptrans; *cptr; cptr++); while (cptr > ptrans && *cptr != ']') cptr--; for (sptr = cptr; sptr > ptrans && isdigit(*(sptr-1)); sptr--); if (*(sptr-1) == '.') *(sptr-1) = ']'; else if (*(sptr-1) == '[') for (cptr = "[000000]"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } else { /* doesn't look like it */ for (cptr = ptrans; *cptr; cptr++); while (cptr > ptrans && *cptr != ']') cptr--; if (*cptr == ']') *(cptr+1) = '\0'; } if (dbug>1) dbugThis (FI_LI, "|%s|%s|%s|", pinfo, ptrans, pchunk); docptr = InitDoc (uri, pchunk, pinfo, ptrans); docptr->isDynamic = 1; CgiAccess (docptr); if (CgiNeedsUpdate (docptr)) { FreeDoc (docptr); CgiAccess (NULL); return; } wasDocStatistics (docptr, 1); /********/ /* read */ /********/ if (!docptr->dname[0]) { zptr = (sptr = docptr->dname) + sizeof(docptr->dname)-1; for (cptr = ptrans; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } docptr->styled = 0; error = ProcessDirectory (docptr); if (error) { CgiLibResponseHeader (404, "text/html"); fprintf (stdout, "\nwasDOC: %s\n", strerror(EVMSERR,error)); return; } if (!docptr->tlength) { CgiLibResponseHeader (404, "text/html"); fprintf (stdout, "\nwasDOC: no document found\n"); return; } /**********/ /* render */ /**********/ if (!docptr->hlength) error = renderParse (docptr); wasDocStatistics (docptr, 0); if (cgiPlusCount) sprintf (stats, "\n", cgiPlusCount, docptr->statistics); else sprintf (stats, "\n", docptr->statistics); if (!docptr->hlength) { CgiLibResponseHeader (404, "text/html"); if (error) fprintf (stdout, "\nwasDOC: %s\n", strerror(EVMSERR,error)); else fprintf (stdout, "\nwasDOC: empty document\n"); return; } /**********/ /* output */ /**********/ if (dbug) dbugThis (FI_LI, "%s%s%s%s", obvious, obvious, obvious, obvious); if (docptr->insight && CgiLibVarNull ("FORM_HTML")) { CgiLibResponseHeader (200, "text/plain", "Script-Control: X-stream-mode\n"); if (docptr->html) error = wasDocWrite (docptr, docptr->html, docptr->hlength); if (!error) error = wasDocWrite (docptr, stats, -1); if (!error) wasDocInsightAt (docptr); if (error) fprintf (stdout, "

wasDOC: %s\n", strerror(EVMSERR,error)); } else if (docptr->insight && CgiLibVarNull ("FORM_TEXT")) { CgiLibResponseHeader (200, "text/plain", "Script-Control: X-stream-mode\n"); if (docptr->text) error = wasDocWrite (docptr, docptr->text, docptr->tlength); if (error) fprintf (stdout, "

wasDOC: %s\n", strerror(EVMSERR,error)); } else if (docptr->insight && (cptr = CgiLibVarNull ("FORM_PASS"))) { error = EINVAL; if (*cptr == '0' || *cptr == '1' || *cptr == '2') { CgiLibResponseHeader (200, "text/plain", "Script-Control: X-stream-mode\n"); if (*cptr == '0') if (docptr->text) error = wasDocWrite (docptr, docptr->text, docptr->tlength); if (*cptr == '1') if (docptr->html1) error = wasDocWrite (docptr, docptr->html1, docptr->hlength1); if (*cptr == '2') if (docptr->html) error = wasDocWrite (docptr, docptr->html, docptr->hlength); if (error) fprintf (stdout, "

wasDOC: %s\n", strerror(EVMSERR,error)); } else fprintf (stdout, "wasDOC: %s\n", strerror(EVMSERR,error)); } else { CgiLibResponseHeader (200, "text/html", "Script-Control: X-stream-mode\n"); if (docptr->chunked > 0) { if (error = wasDocChunkOut (docptr)) error = wasDocWrite (docptr, docptr->html, docptr->hlength); } else error = wasDocWrite (docptr, docptr->html, docptr->hlength); if (!error) error = wasDocWrite (docptr, stats, -1); if (!error) wasDocInsightAt (docptr); if (error) fprintf (stdout, "

wasDOC: %s\n", strerror(EVMSERR,error)); } FreeDoc (docptr); CgiAccess (NULL); } /*****************************************************************************/ /* Reinitialise the rendered HTML. */ void CgiInitHTML (struct wasdoc_st *docptr) { int idx; struct wasdoc_st *tocptr; if (!docptr) return; memset (docptr->section, 0, sizeof(docptr->section)); for (idx = 0; idx < docptr->flagCount; idx++) free (docptr->flags[idx]); docptr->flagCount = 0; free (docptr->html); docptr->html = NULL; docptr->hlength = docptr->hsize = 0; } /*****************************************************************************/ /* Check if the 0.COM file exists in the document directory. If so, provide a message that the document requires and update and return true. If not, return false. Intended specifically for WASD documentation. */ int CgiNeedsUpdate (struct wasdoc_st *docptr) { char *cptr, *sptr, *zptr; char fname [256]; FILE *fp; zptr = (sptr = fname) + sizeof(fname)-1; for (cptr = docptr->dname; *cptr && sptr < zptr; *sptr++ = toupper(*cptr++)); for (cptr = "0.COM"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (!(fp = fopen (fname, "r"))) return (0); fclose (fp); CgiLibResponseHeader (200, "text/plain"); fprintf (stdout, "Document requires update. \ Execute @WASD_ROOT:[WASDOC]CHECK.COM\n"); return (1); } /*****************************************************************************/ /* Authorise access to the document. Enable SYSPRV if authorised. If not authorised then SYSPRV is not enabled. This allows a wasDOC script to have controlled access to configured resources that the scripting account would not otherwise be able to access. This relies on the wasDOC image being INSTALLed with SYSPRV, something that is not done by default, and the directory protections permitting SYSTEM access. Multivalued logical name WASD_ACCESS must have an entry that matches the document directory name. If called with the parameter NULL then SYSPRV is disabled. */ void CgiAccess (struct wasdoc_st *docptr) { static ulong SysPrvMask[2] = { PRV$M_SYSPRV, 0 }; static unsigned short slen; static unsigned long lnmAttributes, lnmIndex; static char lnmValue [256]; static $DESCRIPTOR (lnmNameDsc, ACCESS_LOGNAM); static $DESCRIPTOR (lnmSystemDsc, "LNM$SYSTEM"); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } lnmItems [] = { { sizeof(lnmIndex), LNM$_INDEX, &lnmIndex, 0 }, { sizeof(lnmAttributes), LNM$_ATTRIBUTES, &lnmAttributes, 0 }, { sizeof(lnmValue)-1, LNM$_STRING, lnmValue, &slen }, { 0,0,0,0 } }; int status; /*********/ /* begin */ /*********/ sys$setprv (0, &SysPrvMask, 0, 0); if (!docptr) return; for (lnmIndex = 0; lnmIndex <= 127; lnmIndex++) { status = sys$trnlnm (0, &lnmSystemDsc, &lnmNameDsc, 0, &lnmItems); if (dbug>1) dbugThis (FI_LI, "%%X%08.08X %d", status, (lnmAttributes & LNM$M_EXISTS)); if (!((status & 1) && (lnmAttributes & LNM$M_EXISTS))) break; lnmValue[slen] = '\0'; if (dbug>1) dbugThis (FI_LI, "|%s|%s|", docptr->dname, lnmValue); if (!strcasecmp (docptr->dname, lnmValue)) { sys$setprv (1, &SysPrvMask, 0, 0); break; } } } /*****************************************************************************/