home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: InfoMgt / InfoMgt.zip / dbadm101.zip / cgic.c next >
C/C++ Source or Header  |  1996-08-03  |  23KB  |  1,092 lines

  1. #if CGICDEBUG
  2. #define CGICDEBUGSTART \
  3.     { \
  4.         FILE *dout; \
  5.         dout = fopen("/home/boutell/public_html/debug", "a"); \
  6.     
  7. #define CGICDEBUGEND \
  8.         fclose(dout); \
  9.     }
  10. #else /* CGICDEBUG */
  11. #define CGICDEBUGSTART
  12. #define CGICDEBUGEND
  13. #endif /* CGICDEBUG */
  14.  
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <ctype.h>
  18. #include <stdlib.h>
  19. #ifndef NO_UNISTD
  20. #include <unistd.h>
  21. #endif /* NO_UNISTD */
  22. #include "cgic.h"
  23.  
  24. #define cgiStrEq(a, b) (!strcmp((a), (b)))
  25.  
  26. char *cgiServerSoftware;
  27. char *cgiServerName;
  28. char *cgiGatewayInterface;
  29. char *cgiServerProtocol;
  30. char *cgiServerPort;
  31. char *cgiRequestMethod;
  32. char *cgiPathInfo;
  33. char *cgiPathTranslated;
  34. char *cgiScriptName;
  35. char *cgiQueryString;
  36. char *cgiRemoteHost;
  37. char *cgiRemoteAddr;
  38. char *cgiAuthType;
  39. char *cgiRemoteUser;
  40. char *cgiRemoteIdent;
  41. char *cgiContentType;
  42. int cgiContentLength;
  43. char *cgiAccept;
  44. char *cgiUserAgent;
  45.  
  46. FILE *cgiIn;
  47. FILE *cgiOut;
  48.  
  49. /* One form entry, consisting of an attribute-value pair. */
  50.  
  51. typedef struct cgiFormEntryStruct {
  52.         char *attr;
  53.         char *value;
  54.         struct cgiFormEntryStruct *next;
  55. } cgiFormEntry;
  56.  
  57. /* The first form entry. */
  58. static cgiFormEntry *cgiFormEntryFirst;
  59.  
  60. /* True if CGI environment was restored from a file. */
  61. static int cgiRestored = 0;
  62.  
  63. static void cgiGetenv(char **s, char *var);
  64.  
  65. typedef enum {
  66.     cgiParseSuccess,
  67.     cgiParseMemory,
  68.     cgiParseIO
  69. } cgiParseResultType;
  70.  
  71. static cgiParseResultType cgiParseGetFormInput();
  72. static cgiParseResultType cgiParsePostFormInput();
  73. static cgiParseResultType cgiParseFormInput(char *data, int length);
  74. static void cgiSetupConstants();
  75. static void cgiFreeResources();
  76. static int cgiStrEqNc(char *s1, char *s2);
  77.  
  78. int main(int argc, char *argv[]) {
  79.     int result;
  80.     char *cgiContentLengthString;
  81.     cgiSetupConstants();
  82.     cgiGetenv(&cgiServerSoftware, "SERVER_SOFTWARE");
  83.     cgiGetenv(&cgiServerName, "SERVER_NAME");
  84.     cgiGetenv(&cgiGatewayInterface, "GATEWAY_INTERFACE");
  85.     cgiGetenv(&cgiServerProtocol, "SERVER_PROTOCOL");
  86.     cgiGetenv(&cgiServerPort, "SERVER_PORT");
  87.     cgiGetenv(&cgiRequestMethod, "REQUEST_METHOD");
  88.     cgiGetenv(&cgiPathInfo, "PATH_INFO");
  89.     cgiGetenv(&cgiPathTranslated, "PATH_TRANSLATED");
  90.     cgiGetenv(&cgiScriptName, "SCRIPT_NAME");
  91.     cgiGetenv(&cgiQueryString, "QUERY_STRING");
  92.     cgiGetenv(&cgiRemoteHost, "REMOTE_HOST");
  93.     cgiGetenv(&cgiRemoteAddr, "REMOTE_ADDR");
  94.     cgiGetenv(&cgiAuthType, "AUTH_TYPE");
  95.     cgiGetenv(&cgiRemoteUser, "REMOTE_USER");
  96.     cgiGetenv(&cgiRemoteIdent, "REMOTE_IDENT");
  97.     cgiGetenv(&cgiContentType, "CONTENT_TYPE");
  98.     cgiGetenv(&cgiContentLengthString, "CONTENT_LENGTH");
  99.     cgiContentLength = atoi(cgiContentLengthString);    
  100.     cgiGetenv(&cgiAccept, "HTTP_ACCEPT");
  101.     cgiGetenv(&cgiUserAgent, "HTTP_USER_AGENT");
  102. #ifdef CGICDEBUG
  103.     CGICDEBUGSTART
  104.     fprintf(dout, "%d\n", cgiContentLength);
  105.     fprintf(dout, "%s\n", cgiRequestMethod);
  106.     fprintf(dout, "%s\n", cgiContentType);
  107.     CGICDEBUGEND    
  108. #endif /* CGICDEBUG */
  109.     cgiFormEntryFirst = 0;
  110.     cgiIn = stdin;
  111.     cgiOut = stdout;
  112.     cgiRestored = 0;
  113.  
  114.  
  115.     /* These five lines keep compilers from
  116.         producing warnings that argc and argv
  117.         are unused. They have no actual function. */
  118.     if (argc) {
  119.         if (argv[0]) {
  120.             cgiRestored = 0;
  121.         }
  122.     }    
  123.  
  124.  
  125.     if (cgiStrEqNc(cgiRequestMethod, "post")) {
  126. #ifdef CGICDEBUG
  127.         CGICDEBUGSTART
  128.         fprintf(dout, "POST recognized\n");
  129.         CGICDEBUGEND
  130. #endif /* CGICDEBUG */
  131.         if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) {    
  132. #ifdef CGICDEBUG
  133.             CGICDEBUGSTART
  134.             fprintf(dout, "Calling PostFormInput\n");
  135.             CGICDEBUGEND    
  136. #endif /* CGICDEBUG */
  137.             if (cgiParsePostFormInput() != cgiParseSuccess) {
  138. #ifdef CGICDEBUG
  139.                 CGICDEBUGSTART
  140.                 fprintf(dout, "PostFormInput failed\n");
  141.                 CGICDEBUGEND    
  142. #endif /* CGICDEBUG */
  143.                 cgiFreeResources();
  144.                 return -1;
  145.             }    
  146. #ifdef CGICDEBUG
  147.             CGICDEBUGSTART
  148.             fprintf(dout, "PostFormInput succeeded\n");
  149.             CGICDEBUGEND    
  150. #endif /* CGICDEBUG */
  151.         }
  152.     } else if (cgiStrEqNc(cgiRequestMethod, "get")) {    
  153.         /* The spec says this should be taken care of by
  154.             the server, but... it isn't */
  155.         cgiContentLength = strlen(cgiQueryString);
  156.         if (cgiParseGetFormInput() != cgiParseSuccess) {
  157. #ifdef CGICDEBUG
  158.             CGICDEBUGSTART
  159.             fprintf(dout, "GetFormInput failed\n");
  160.             CGICDEBUGEND    
  161. #endif /* CGICDEBUG */
  162.             cgiFreeResources();
  163.             return -1;
  164.         } else {    
  165. #ifdef CGICDEBUG
  166.             CGICDEBUGSTART
  167.             fprintf(dout, "GetFormInput succeeded\n");
  168.             CGICDEBUGEND    
  169. #endif /* CGICDEBUG */
  170.         }
  171.     }
  172.     result = cgiMain();
  173.     cgiFreeResources();
  174.  
  175.     /* The following two lines allegedly produce better behavior
  176.         when run with the CERN server on some platforms. 
  177.         Remove them and experiment if you wish. */
  178.     fflush(stdout);
  179.     sleep(1);
  180.     return result;
  181. }
  182.  
  183. static void cgiGetenv(char **s, char *var){
  184.     *s = getenv(var);
  185.     if (!(*s)) {
  186.         *s = "";
  187.     }
  188. }
  189.  
  190. static cgiParseResultType cgiParsePostFormInput() {
  191.     char *input;
  192.     cgiParseResultType result;
  193.     if (!cgiContentLength) {
  194.         return cgiParseSuccess;
  195.     }
  196.     input = (char *) malloc(cgiContentLength);
  197.     if (!input) {
  198.         return cgiParseMemory;    
  199.     }
  200.     if (fread(input, 1, cgiContentLength, cgiIn) != cgiContentLength) {
  201.         return cgiParseIO;
  202.     }    
  203.     result = cgiParseFormInput(input, cgiContentLength);
  204.     free(input);
  205.     return result;
  206. }
  207.  
  208. static cgiParseResultType cgiParseGetFormInput() {
  209.     return cgiParseFormInput(cgiQueryString, cgiContentLength);
  210. }
  211.  
  212. typedef enum {
  213.     cgiEscapeRest,
  214.     cgiEscapeFirst,
  215.     cgiEscapeSecond
  216. } cgiEscapeState;
  217.  
  218. typedef enum {
  219.     cgiUnescapeSuccess,
  220.     cgiUnescapeMemory
  221. } cgiUnescapeResultType;
  222.  
  223. static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
  224.  
  225. static cgiParseResultType cgiParseFormInput(char *data, int length) {
  226.     /* Scan for pairs, unescaping and storing them as they are found. */
  227.     int pos = 0;
  228.     cgiFormEntry *n;
  229.     cgiFormEntry *l = 0;
  230.     while (pos != length) {
  231.         int foundEq = 0;
  232.         int foundAmp = 0;
  233.         int start = pos;
  234.         int len = 0;
  235.         char *attr;
  236.         char *value;
  237.         while (pos != length) {
  238.             if (data[pos] == '=') {
  239.                 foundEq = 1;
  240.                 pos++;
  241.                 break;
  242.             }
  243.             pos++;
  244.             len++;
  245.         }
  246.         if (!foundEq) {
  247.             break;
  248.         }
  249.         if (cgiUnescapeChars(&attr, data+start, len)
  250.             != cgiUnescapeSuccess) {
  251.             return cgiParseMemory;
  252.         }    
  253.         start = pos;
  254.         len = 0;
  255.         while (pos != length) {
  256.             if (data[pos] == '&') {
  257.                 foundAmp = 1;
  258.                 pos++;
  259.                 break;
  260.             }
  261.             pos++;
  262.             len++;
  263.         }
  264.         /* The last pair probably won't be followed by a &, but
  265.             that's fine, so check for that after accepting it */
  266.         if (cgiUnescapeChars(&value, data+start, len)
  267.             != cgiUnescapeSuccess) {
  268.             return cgiParseMemory;
  269.         }    
  270.         /* OK, we have a new pair, add it to the list. */
  271.         n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));    
  272.         if (!n) {
  273.             return cgiParseMemory;
  274.         }
  275.         n->attr = attr;
  276.         n->value = value;
  277.         n->next = 0;
  278.         if (!l) {
  279.             cgiFormEntryFirst = n;
  280.         } else {
  281.             l->next = n;
  282.         }
  283.         l = n;
  284.         if (!foundAmp) {
  285.             break;
  286.         }            
  287.     }
  288.     return cgiParseSuccess;
  289. }
  290.  
  291. static int cgiHexValue[256];
  292.  
  293. cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) {
  294.     char *s;
  295.     cgiEscapeState escapeState = cgiEscapeRest;
  296.     int escapedValue = 0;
  297.     int srcPos = 0;
  298.     int dstPos = 0;
  299.     s = (char *) malloc(len + 1);
  300.     if (!s) {
  301.         return cgiUnescapeMemory;
  302.     }
  303.     while (srcPos < len) {
  304.         int ch = cp[srcPos];
  305.         switch (escapeState) {
  306.             case cgiEscapeRest:
  307.             if (ch == '%') {
  308.                 escapeState = cgiEscapeFirst;
  309.             } else if (ch == '+') {
  310.                 s[dstPos++] = ' ';
  311.             } else {
  312.                 s[dstPos++] = ch;    
  313.             }
  314.             break;
  315.             case cgiEscapeFirst:
  316.             escapedValue = cgiHexValue[ch] << 4;    
  317.             escapeState = cgiEscapeSecond;
  318.             break;
  319.             case cgiEscapeSecond:
  320.             escapedValue += cgiHexValue[ch];
  321.             s[dstPos++] = escapedValue;
  322.             escapeState = cgiEscapeRest;
  323.             break;
  324.         }
  325.         srcPos++;
  326.     }
  327.     s[dstPos] = '\0';
  328.     *sp = s;
  329.     return cgiUnescapeSuccess;
  330. }        
  331.     
  332. static void cgiSetupConstants() {
  333.     int i;
  334.     for (i=0; (i < 256); i++) {
  335.         cgiHexValue[i] = 0;
  336.     }
  337.     cgiHexValue['0'] = 0;    
  338.     cgiHexValue['1'] = 1;    
  339.     cgiHexValue['2'] = 2;    
  340.     cgiHexValue['3'] = 3;    
  341.     cgiHexValue['4'] = 4;    
  342.     cgiHexValue['5'] = 5;    
  343.     cgiHexValue['6'] = 6;    
  344.     cgiHexValue['7'] = 7;    
  345.     cgiHexValue['8'] = 8;    
  346.     cgiHexValue['9'] = 9;
  347.     cgiHexValue['A'] = 10;
  348.     cgiHexValue['B'] = 11;
  349.     cgiHexValue['C'] = 12;
  350.     cgiHexValue['D'] = 13;
  351.     cgiHexValue['E'] = 14;
  352.     cgiHexValue['F'] = 15;
  353.     cgiHexValue['a'] = 10;
  354.     cgiHexValue['b'] = 11;
  355.     cgiHexValue['c'] = 12;
  356.     cgiHexValue['d'] = 13;
  357.     cgiHexValue['e'] = 14;
  358.     cgiHexValue['f'] = 15;
  359. }
  360.  
  361. static void cgiFreeResources() {
  362.     cgiFormEntry *c = cgiFormEntryFirst;
  363.     cgiFormEntry *n;
  364.     while (c) {
  365.         n = c->next;
  366.         free(c->attr);
  367.         free(c->value);
  368.         free(c);
  369.         c = n;
  370.     }
  371.     /* If the cgi environment was restored from a saved environment,
  372.         then these are in allocated space and must also be freed */
  373.     if (cgiRestored) {
  374.         free(cgiServerSoftware);
  375.         free(cgiServerName);
  376.         free(cgiGatewayInterface);
  377.         free(cgiServerProtocol);
  378.         free(cgiServerPort);
  379.         free(cgiRequestMethod);
  380.         free(cgiPathInfo);
  381.         free(cgiPathTranslated);
  382.         free(cgiScriptName);
  383.         free(cgiQueryString);
  384.         free(cgiRemoteHost);
  385.         free(cgiRemoteAddr);
  386.         free(cgiAuthType);
  387.         free(cgiRemoteUser);
  388.         free(cgiRemoteIdent);
  389.         free(cgiContentType);
  390.         free(cgiAccept);
  391.         free(cgiUserAgent);
  392.     }
  393. }
  394.  
  395. static cgiFormResultType cgiFormEntryString(
  396.     cgiFormEntry *e, char *result, int max, int newlines);
  397.  
  398. static cgiFormEntry *cgiFormEntryFindFirst(char *name);
  399. static cgiFormEntry *cgiFormEntryFindNext();
  400.  
  401. cgiFormResultType cgiFormString(
  402.         char *name, char *result, int max) {
  403.     cgiFormEntry *e;
  404.     e = cgiFormEntryFindFirst(name);
  405.     if (!e) {
  406.         strcpy(result, "");
  407.         return cgiFormNotFound;
  408.     }
  409.     return cgiFormEntryString(e, result, max, 1);
  410. }
  411.  
  412. cgiFormResultType cgiFormStringNoNewlines(
  413.         char *name, char *result, int max) {
  414.     cgiFormEntry *e;
  415.     e = cgiFormEntryFindFirst(name);
  416.     if (!e) {
  417.         strcpy(result, "");
  418.         return cgiFormNotFound;
  419.     }
  420.     return cgiFormEntryString(e, result, max, 0);
  421. }
  422.  
  423. cgiFormResultType cgiFormStringMultiple(
  424.         char *name, char ***result) {
  425.     char **stringArray;
  426.     cgiFormEntry *e;
  427.     int i;
  428.     int total = 0;
  429.     /* Make two passes. One would be more efficient, but this
  430.         function is not commonly used. The select menu and
  431.         radio box functions are faster. */
  432.     e = cgiFormEntryFindFirst(name);
  433.     if (e != 0) {
  434.         do {
  435.             total++;
  436.         } while ((e = cgiFormEntryFindNext()) != 0); 
  437.     }
  438.     stringArray = (char **) malloc(sizeof(char *) * (total + 1));
  439.     if (!stringArray) {
  440.         *result = 0;
  441.         return cgiFormMemory;
  442.     }
  443.     /* initialize all entries to null; the last will stay that way */
  444.     for (i=0; (i <= total); i++) {
  445.         stringArray[i] = 0;
  446.     }
  447.     /* Now go get the entries */
  448.     e = cgiFormEntryFindFirst(name);
  449. #ifdef CGICDEBUG
  450.     CGICDEBUGSTART
  451.     fprintf(dout, "StringMultiple Beginning\n");
  452.     CGICDEBUGEND
  453. #endif /* CGICDEBUG */
  454.     if (e) {
  455.         i = 0;
  456.         do {
  457.             int max = strlen(e->value) + 1;
  458.             stringArray[i] = (char *) malloc(max);
  459.             if (stringArray[i] == 0) {
  460.                 /* Memory problems */
  461.                 cgiStringArrayFree(stringArray);
  462.                 *result = 0;
  463.                 return cgiFormMemory;
  464.             }    
  465.             strcpy(stringArray[i], e->value);
  466.             cgiFormEntryString(e, stringArray[i], max, 1);
  467.             i++;
  468.         } while ((e = cgiFormEntryFindNext()) != 0); 
  469.         *result = stringArray;
  470. #ifdef CGICDEBUG
  471.         CGICDEBUGSTART
  472.         fprintf(dout, "StringMultiple Succeeding\n");
  473.         CGICDEBUGEND
  474. #endif /* CGICDEBUG */
  475.         return cgiFormSuccess;
  476.     } else {
  477.         *result = stringArray;
  478. #ifdef CGICDEBUG
  479.         CGICDEBUGSTART
  480.         fprintf(dout, "StringMultiple found nothing\n");
  481.         CGICDEBUGEND
  482. #endif /* CGICDEBUG */
  483.         return cgiFormNotFound;
  484.     }    
  485. }
  486.  
  487. cgiFormResultType cgiFormStringSpaceNeeded(
  488.         char *name, int *result) {
  489.     cgiFormEntry *e;
  490.     e = cgiFormEntryFindFirst(name);
  491.     if (!e) {
  492.         *result = 1;
  493.         return cgiFormNotFound; 
  494.     }
  495.     *result = strlen(e->value) + 1;
  496.     return cgiFormSuccess;
  497. }
  498.  
  499. static cgiFormResultType cgiFormEntryString(
  500.     cgiFormEntry *e, char *result, int max, int newlines) {
  501.     char *dp, *sp;
  502.     int truncated = 0;
  503.     int len = 0;
  504.     int avail = max-1;
  505.     int crCount = 0;
  506.     int lfCount = 0;    
  507.     dp = result;
  508.     sp = e->value;    
  509.     while (1) {
  510.         int ch;
  511.         ch = *sp;
  512.         if (len == avail) {
  513.             truncated = 1;
  514.             break;
  515.         }     
  516.         /* Fix the CR/LF, LF, CR nightmare: watch for
  517.             consecutive bursts of CRs and LFs in whatever
  518.             pattern, then actually output the larger number 
  519.             of LFs. Consistently sane, yet it still allows
  520.             consecutive blank lines when the user
  521.             actually intends them. */
  522.         if ((ch == 13) || (ch == 10)) {
  523.             if (ch == 13) {
  524.                 crCount++;
  525.             } else {
  526.                 lfCount++;
  527.             }    
  528.         } else {
  529.             if (crCount || lfCount) {
  530.                 int lfsAdd = crCount;
  531.                 if (lfCount > crCount) {
  532.                     lfsAdd = lfCount;
  533.                 }
  534.                 /* Stomp all newlines if desired */
  535.                 if (!newlines) {
  536.                     lfsAdd = 0;
  537.                 }
  538.                 while (lfsAdd) {
  539.                     if (len == avail) {
  540.                         truncated = 1;
  541.                         break;
  542.                     }
  543.                     *dp = 10;
  544.                     dp++;
  545.                     lfsAdd--;
  546.                     len++;        
  547.                 }
  548.                 crCount = 0;
  549.                 lfCount = 0;
  550.             }
  551.             if (ch == '\0') {
  552.                 /* The end of the source string */
  553.                 break;                
  554.             }    
  555.             *dp = ch;
  556.             dp++;
  557.             len++;
  558.         }
  559.         sp++;    
  560.     }    
  561.     *dp = '\0';
  562.     if (truncated) {
  563.         return cgiFormTruncated;
  564.     } else if (!len) {
  565.         return cgiFormEmpty;
  566.     } else {
  567.         return cgiFormSuccess;
  568.     }
  569. }
  570.  
  571. static int cgiFirstNonspaceChar(char *s);
  572.  
  573. cgiFormResultType cgiFormInteger(
  574.         char *name, int *result, int defaultV) {
  575.     cgiFormEntry *e;
  576.     int ch;
  577.     e = cgiFormEntryFindFirst(name);
  578.     if (!e) {
  579.         *result = defaultV;
  580.         return cgiFormNotFound; 
  581.     }    
  582.     if (!strlen(e->value)) {
  583.         *result = defaultV;
  584.         return cgiFormEmpty;
  585.     }
  586.     ch = cgiFirstNonspaceChar(e->value);
  587.     if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) {
  588.         *result = defaultV;
  589.         return cgiFormBadType;
  590.     } else {
  591.         *result = atoi(e->value);
  592.         return cgiFormSuccess;
  593.     }
  594. }
  595.  
  596. cgiFormResultType cgiFormIntegerBounded(
  597.         char *name, int *result, int min, int max, int defaultV) {
  598.     cgiFormResultType error = cgiFormInteger(name, result, defaultV);
  599.     if (error != cgiFormSuccess) {
  600.         return error;
  601.     }
  602.     if (*result < min) {
  603.         *result = min;
  604.         return cgiFormConstrained;
  605.     } 
  606.     if (*result > max) {
  607.         *result = max;
  608.         return cgiFormConstrained;
  609.     } 
  610.     return cgiFormSuccess;
  611. }
  612.  
  613. cgiFormResultType cgiFormDouble(
  614.         char *name, double *result, double defaultV) {
  615.     cgiFormEntry *e;
  616.     int ch;
  617.     e = cgiFormEntryFindFirst(name);
  618.     if (!e) {
  619.         *result = defaultV;
  620.         return cgiFormNotFound; 
  621.     }    
  622.     if (!strlen(e->value)) {
  623.         *result = defaultV;
  624.         return cgiFormEmpty;
  625.     } 
  626.     ch = cgiFirstNonspaceChar(e->value);
  627.     if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) {
  628.         *result = defaultV;
  629.         return cgiFormBadType;
  630.     } else {
  631.         *result = atof(e->value);
  632.         return cgiFormSuccess;
  633.     }
  634. }
  635.  
  636. cgiFormResultType cgiFormDoubleBounded(
  637.         char *name, double *result, double min, double max, double defaultV) {
  638.     cgiFormResultType error = cgiFormDouble(name, result, defaultV);
  639.     if (error != cgiFormSuccess) {
  640.         return error;
  641.     }
  642.     if (*result < min) {
  643.         *result = min;
  644.         return cgiFormConstrained;
  645.     } 
  646.     if (*result > max) {
  647.         *result = max;
  648.         return cgiFormConstrained;
  649.     } 
  650.     return cgiFormSuccess;
  651. }
  652.  
  653. cgiFormResultType cgiFormSelectSingle(
  654.     char *name, char **choicesText, int choicesTotal, 
  655.     int *result, int defaultV) 
  656. {
  657.     cgiFormEntry *e;
  658.     int i;
  659.     e = cgiFormEntryFindFirst(name);
  660. #ifdef CGICDEBUG
  661.     CGICDEBUGSTART
  662.     fprintf(dout, "%d\n", (int) e);
  663.     CGICDEBUGEND
  664. #endif /* CGICDEBUG */
  665.     if (!e) {
  666.         *result = defaultV;
  667.         return cgiFormNotFound;
  668.     }
  669.     for (i=0; (i < choicesTotal); i++) {
  670. #ifdef CGICDEBUG
  671.         CGICDEBUGSTART
  672.         fprintf(dout, "%s %s\n", choicesText[i], e->value);
  673.         CGICDEBUGEND
  674. #endif /* CGICDEBUG */
  675.         if (cgiStrEq(choicesText[i], e->value)) {
  676. #ifdef CGICDEBUG
  677.             CGICDEBUGSTART
  678.             fprintf(dout, "MATCH\n");
  679.             CGICDEBUGEND
  680. #endif /* CGICDEBUG */
  681.             *result = i;
  682.             return cgiFormSuccess;
  683.         }
  684.     }
  685.     *result = defaultV;
  686.     return cgiFormNoSuchChoice;
  687. }
  688.  
  689. cgiFormResultType cgiFormSelectMultiple(
  690.     char *name, char **choicesText, int choicesTotal, 
  691.     int *result, int *invalid) 
  692. {
  693.     cgiFormEntry *e;
  694.     int i;
  695.     int hits = 0;
  696.     int invalidE = 0;
  697.     for (i=0; (i < choicesTotal); i++) {
  698.         result[i] = 0;
  699.     }
  700.     e = cgiFormEntryFindFirst(name);
  701.     if (!e) {
  702.         *invalid = invalidE;
  703.         return cgiFormNotFound;
  704.     }
  705.     do {
  706.         int hit = 0;
  707.         for (i=0; (i < choicesTotal); i++) {
  708.             if (cgiStrEq(choicesText[i], e->value)) {
  709.                 result[i] = 1;
  710.                 hits++;
  711.                 hit = 1;
  712.                 break;
  713.             }
  714.         }
  715.         if (!(hit)) {
  716.             invalidE++;
  717.         }
  718.     } while ((e = cgiFormEntryFindNext()) != 0);
  719.  
  720.     *invalid = invalidE;
  721.  
  722.     if (hits) {
  723.         return cgiFormSuccess;
  724.     } else {
  725.         return cgiFormNotFound;
  726.     }
  727. }
  728.  
  729. cgiFormResultType cgiFormCheckboxSingle(
  730.     char *name)
  731. {
  732.     cgiFormEntry *e;
  733.     e = cgiFormEntryFindFirst(name);
  734.     if (!e) {
  735.         return cgiFormNotFound;
  736.     }
  737.     return cgiFormSuccess;
  738. }
  739.  
  740. extern cgiFormResultType cgiFormCheckboxMultiple(
  741.     char *name, char **valuesText, int valuesTotal, 
  742.     int *result, int *invalid)
  743. {
  744.     /* Implementation is identical to cgiFormSelectMultiple. */
  745.     return cgiFormSelectMultiple(name, valuesText, 
  746.         valuesTotal, result, invalid);
  747. }
  748.  
  749. cgiFormResultType cgiFormRadio(
  750.     char *name, 
  751.     char **valuesText, int valuesTotal, int *result, int defaultV)
  752. {
  753.     /* Implementation is identical to cgiFormSelectSingle. */
  754.     return cgiFormSelectSingle(name, valuesText, valuesTotal, 
  755.         result, defaultV);
  756. }
  757.  
  758. void cgiHeaderLocation(char *redirectUrl) {
  759.     fprintf(cgiOut, "Location: %s%c%c", redirectUrl, 10, 10); 
  760. }
  761.  
  762. void cgiHeaderStatus(int status, char *statusMessage) {
  763.     fprintf(cgiOut, "Status: %d %s%c%c", status, statusMessage, 
  764.     10, 10);
  765. }
  766.  
  767. void cgiHeaderContentType(char *mimeType) {
  768.     fprintf(cgiOut, "Content-type: %s%c%c", mimeType, 10, 10);
  769. }
  770.  
  771. #ifndef NO_SYSTEM
  772.  
  773. int cgiSaferSystem(char *command) {
  774.     char *s;
  775.     char *sp;
  776.     int i;
  777.     int len = (strlen(command) * 2) + 1;
  778.     s = (char *) malloc(len);
  779.     if (!s) {
  780.         return -1;
  781.     }
  782.     sp = s;
  783.     for (i=0; (i < len); i++) {
  784.         if (command[i] == ';') {
  785.             *sp = '\\';
  786.             sp++;
  787.         } else if (command[i] == '|') {
  788.             *sp = '\\';
  789.             sp++;
  790.         }
  791.         *sp = command[i];
  792.         sp++;
  793.     }
  794.     *sp = '\0';
  795.     return system(s);
  796. }        
  797.  
  798. #endif /* NO_SYSTEM */
  799.  
  800. static int cgiWriteString(FILE *out, char *s);
  801.  
  802. static int cgiWriteInt(FILE *out, int i);
  803.  
  804. cgiEnvironmentResultType cgiWriteEnvironment(char *filename) {
  805.     FILE *out;
  806.     cgiFormEntry *e;
  807.     /* Be sure to open in binary mode */
  808.     out = fopen(filename, "wb");
  809.     if (!out) {
  810.         /* Can't create file */
  811.         return cgiEnvironmentIO;
  812.     }
  813.     if (!cgiWriteString(out, cgiServerSoftware)) {
  814.         goto error;
  815.     }
  816.     if (!cgiWriteString(out, cgiServerName)) {
  817.         goto error;
  818.     }
  819.     if (!cgiWriteString(out, cgiGatewayInterface)) {
  820.         goto error;
  821.     }
  822.     if (!cgiWriteString(out, cgiServerProtocol)) {
  823.         goto error;
  824.     }
  825.     if (!cgiWriteString(out, cgiServerPort)) {
  826.         goto error;
  827.     }
  828.     if (!cgiWriteString(out, cgiRequestMethod)) {
  829.         goto error;
  830.     }
  831.     if (!cgiWriteString(out, cgiPathInfo)) {
  832.         goto error;
  833.     }
  834.     if (!cgiWriteString(out, cgiPathTranslated)) {
  835.         goto error;
  836.     }
  837.     if (!cgiWriteString(out, cgiScriptName)) {
  838.         goto error;
  839.     }
  840.     if (!cgiWriteString(out, cgiQueryString)) {
  841.         goto error;
  842.     }
  843.     if (!cgiWriteString(out, cgiRemoteHost)) {
  844.         goto error;
  845.     }
  846.     if (!cgiWriteString(out, cgiRemoteAddr)) {
  847.         goto error;
  848.     }
  849.     if (!cgiWriteString(out, cgiAuthType)) {
  850.         goto error;
  851.     }
  852.     if (!cgiWriteString(out, cgiRemoteUser)) {
  853.         goto error;
  854.     }
  855.     if (!cgiWriteString(out, cgiRemoteIdent)) {
  856.         goto error;
  857.     }
  858.     if (!cgiWriteString(out, cgiContentType)) {
  859.         goto error;
  860.     }
  861.     if (!cgiWriteString(out, cgiAccept)) {
  862.         goto error;
  863.     }
  864.     if (!cgiWriteString(out, cgiUserAgent)) {
  865.         goto error;
  866.     }
  867.     if (!cgiWriteInt(out, cgiContentLength)) {
  868.         goto error;
  869.     }
  870.     e = cgiFormEntryFirst;
  871.     while (e) {
  872.         if (!cgiWriteString(out, e->attr)) {
  873.             goto error;
  874.         }
  875.         if (!cgiWriteString(out, e->value)) {
  876.             goto error;
  877.         }
  878.         e = e->next;
  879.     }
  880.     fclose(out);
  881.     return cgiEnvironmentSuccess;
  882. error:
  883.     fclose(out);
  884.     /* If this function is not defined in your system,
  885.         you must substitute the appropriate 
  886.         file-deletion function. */
  887.     unlink(filename);
  888.     return cgiEnvironmentIO;
  889. }
  890.  
  891. static int cgiWriteString(FILE *out, char *s) {
  892.     int len = strlen(s);
  893.     cgiWriteInt(out, len);
  894.     if (fwrite(s, 1, len, out) != len) {
  895.         return 0;
  896.     }
  897.     return 1;
  898. }
  899.  
  900. static int cgiWriteInt(FILE *out, int i) {
  901.     if (!fwrite(&i, sizeof(int), 1, out)) {
  902.         return 0;
  903.     }
  904.     return 1;
  905. }
  906.  
  907. static int cgiReadString(FILE *out, char **s);
  908.  
  909. static int cgiReadInt(FILE *out, int *i);
  910.  
  911. cgiEnvironmentResultType cgiReadEnvironment(char *filename) {
  912.     FILE *in;
  913.     cgiFormEntry *e, *p;
  914.     /* Free any existing data first */
  915.     cgiFreeResources();
  916.     /* Be sure to open in binary mode */
  917.     in = fopen(filename, "rb");
  918.     if (!in) {
  919.         /* Can't access file */
  920.         return cgiEnvironmentIO;
  921.     }
  922.     if (!cgiReadString(in, &cgiServerSoftware)) {
  923.         goto error;
  924.     }
  925.     if (!cgiReadString(in, &cgiServerName)) {
  926.         goto error;
  927.     }
  928.     if (!cgiReadString(in, &cgiGatewayInterface)) {
  929.         goto error;
  930.     }
  931.     if (!cgiReadString(in, &cgiServerProtocol)) {
  932.         goto error;
  933.     }
  934.     if (!cgiReadString(in, &cgiServerPort)) {
  935.         goto error;
  936.     }
  937.     if (!cgiReadString(in, &cgiRequestMethod)) {
  938.         goto error;
  939.     }
  940.     if (!cgiReadString(in, &cgiPathInfo)) {
  941.         goto error;
  942.     }
  943.     if (!cgiReadString(in, &cgiPathTranslated)) {
  944.         goto error;
  945.     }
  946.     if (!cgiReadString(in, &cgiScriptName)) {
  947.         goto error;
  948.     }
  949.     if (!cgiReadString(in, &cgiQueryString)) {
  950.         goto error;
  951.     }
  952.     if (!cgiReadString(in, &cgiRemoteHost)) {
  953.         goto error;
  954.     }
  955.     if (!cgiReadString(in, &cgiRemoteAddr)) {
  956.         goto error;
  957.     }
  958.     if (!cgiReadString(in, &cgiAuthType)) {
  959.         goto error;
  960.     }
  961.     if (!cgiReadString(in, &cgiRemoteUser)) {
  962.         goto error;
  963.     }
  964.     if (!cgiReadString(in, &cgiRemoteIdent)) {
  965.         goto error;
  966.     }
  967.     if (!cgiReadString(in, &cgiContentType)) {
  968.         goto error;
  969.     }
  970.     if (!cgiReadString(in, &cgiAccept)) {
  971.         goto error;
  972.     }
  973.     if (!cgiReadString(in, &cgiUserAgent)) {
  974.         goto error;
  975.     }
  976.     if (!cgiReadInt(in, &cgiContentLength)) {
  977.         goto error;
  978.     }
  979.     p = 0;
  980.     while (1) {
  981.         e = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));
  982.         if (!e) {
  983.             cgiFreeResources();
  984.             fclose(in);
  985.             return cgiEnvironmentMemory;
  986.         }
  987.         if (!cgiReadString(in, &e->attr)) {
  988.             /* This means we've reached the end of the list. */
  989.             free(e);
  990.             break;
  991.         }
  992.         if (!cgiReadString(in, &e->value)) {
  993.             free(e);
  994.             goto error;
  995.         }
  996.         e->next = 0;
  997.         if (p) {
  998.             p->next = e;
  999.         } else {
  1000.             cgiFormEntryFirst = e;
  1001.         }    
  1002.         p = e;
  1003.     }
  1004.     fclose(in);
  1005.     cgiRestored = 1;
  1006.     return cgiEnvironmentSuccess;
  1007. error:
  1008.     cgiFreeResources();
  1009.     fclose(in);
  1010.     return cgiEnvironmentIO;
  1011. }
  1012.  
  1013. static int cgiReadString(FILE *in, char **s) {
  1014.     int len;
  1015.     cgiReadInt(in, &len);
  1016.     *s = (char *) malloc(len + 1);
  1017.     if (!(*s)) {
  1018.         return 0;
  1019.     }    
  1020.     if (fread(*s, 1, len, in) != len) {
  1021.         return 0;
  1022.     }
  1023.     (*s)[len] = '\0';
  1024.     return 1;
  1025. }
  1026.  
  1027. static int cgiReadInt(FILE *out, int *i) {
  1028.     if (!fread(i, sizeof(int), 1, out)) {
  1029.         return 0;
  1030.     }
  1031.     return 1;
  1032. }
  1033.  
  1034. static int cgiStrEqNc(char *s1, char *s2) {
  1035.     while(1) {
  1036.         if (!(*s1)) {
  1037.             if (!(*s2)) {
  1038.                 return 1;
  1039.             } else {
  1040.                 return 0;
  1041.             }
  1042.         } else if (!(*s2)) {
  1043.             return 0;
  1044.         }
  1045.         if (isalpha(*s1)) {
  1046.             if (tolower(*s1) != tolower(*s2)) {
  1047.                 return 0;
  1048.             }
  1049.         } else if ((*s1) != (*s2)) {
  1050.             return 0;
  1051.         }
  1052.         s1++;
  1053.         s2++;
  1054.     }
  1055. }
  1056.  
  1057. static char *cgiFindTarget = 0;
  1058. static cgiFormEntry *cgiFindPos = 0;
  1059.  
  1060. static cgiFormEntry *cgiFormEntryFindFirst(char *name) {
  1061.     cgiFindTarget = name;
  1062.     cgiFindPos = cgiFormEntryFirst;
  1063.     return cgiFormEntryFindNext();
  1064. }
  1065.  
  1066. static cgiFormEntry *cgiFormEntryFindNext() {
  1067.     while (cgiFindPos) {
  1068.         cgiFormEntry *c = cgiFindPos;
  1069.         cgiFindPos = c->next;
  1070.         if (!strcmp(c -> attr, cgiFindTarget)) {
  1071.             return c;
  1072.         }
  1073.     }
  1074.     return 0;
  1075. }
  1076.  
  1077. static int cgiFirstNonspaceChar(char *s) {
  1078.     int len = strspn(s, " \n\r\t");
  1079.     return s[len];
  1080. }
  1081.  
  1082. void cgiStringArrayFree(char **stringArray) {
  1083.     char *p;
  1084.     p = *stringArray;
  1085.     while (p) {
  1086.         free(p);
  1087.         stringArray++;
  1088.         p = *stringArray;
  1089.     }
  1090. }    
  1091.  
  1092.