[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
/*****************************************************************************/
/*
                                    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 <lnmdef.h>
#include <prvdef.h>
#include <starlet.h>

#include "wasdoc.h"
#include <cgilib.h>

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, "<!DOCTYPE html>\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, "<!DOCTYPE html>\nwasDOC: %s\n",
               strerror(EVMSERR,error));
      return;
   }
   if (!docptr->tlength)
   {
      CgiLibResponseHeader (404, "text/html");
      fprintf (stdout, "<!DOCTYPE html>\nwasDOC: no document found\n");
      return;
   }

   /**********/
   /* render */
   /**********/

   if (!docptr->hlength) error = renderParse (docptr);

   wasDocStatistics (docptr, 0);

   if (cgiPlusCount)
      sprintf (stats, "<!-- CGIplus:%d %s -->\n",
                      cgiPlusCount, docptr->statistics);
   else
      sprintf (stats, "<!-- %s -->\n", docptr->statistics);

   if (!docptr->hlength)
   {
      CgiLibResponseHeader (404, "text/html");
      if (error)
         fprintf (stdout, "<!DOCTYPE html>\nwasDOC: %s\n",
                  strerror(EVMSERR,error));
      else
         fprintf (stdout, "<!DOCTYPE html>\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, "<p>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, "<p>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, "<p>wasDOC: %s\n", strerror(EVMSERR,error));
      }
      else
         fprintf (stdout, "<!DOCTYPE html>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, "<p>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;
      }
   }
}

/*****************************************************************************/