home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 1999 January / dppcpro0199a.iso / January / Fp98 / SDK / cgi / cgiwin32 / cgiwin32.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-18  |  29.7 KB  |  1,326 lines

  1.  
  2. /* cgiwin32.c */
  3.  
  4. /* emulate CGI-WIN interface */
  5.  
  6. #include "stdio.h"
  7. #include "fcntl.h"
  8. #include "windows.h"
  9. #include "process.h"
  10.  
  11. extern char *getenv();
  12. extern char *strrchr();
  13. extern int atoi();
  14.  
  15. int glob_argc = 0;
  16. int glob_clen = 0;
  17. char *glob_content = NULL;
  18. char *glob_ifile = NULL;
  19. char *glob_ofile = NULL;
  20. char *glob_cfile = NULL;
  21. char **glob_argv = NULL;
  22. char **glob_env = NULL;
  23. int glob_tnum = 0;  /* all tempfiles will share this number, with unique prefix */
  24.  
  25. int glob_cgiwin = 0;  /* if true, running under CGI-WIN interface */
  26. char glob_cgiwin_out[_MAX_PATH];
  27.  
  28. typedef struct
  29. {
  30.     int size;   /* number of slots allocated */
  31.     int num;    /* number of slots filled */
  32.     char **slots;
  33. } DynArray, *DynArrayPtr;
  34.  
  35. /* maintain list of external temp files */
  36. DynArrayPtr glob_extfiles = NULL;
  37.  
  38. /* globals for re-written environment vars */
  39. char glob_log_path[_MAX_PATH];
  40. char glob_phy_path[_MAX_PATH];
  41. char glob_scr_path[_MAX_PATH];
  42.  
  43. FILE *errlog = NULL;
  44. char errmsg[1024];
  45.  
  46. /* ---- log_error ---- */
  47.  
  48. int log_error(char *s)
  49. {
  50.     if(!s || !*s)
  51.         return -1;
  52.     if(!errlog)
  53.         errlog = fopen("C:\\cgiwin.out","w");
  54.     fprintf(errlog,"%s\n",s);
  55.     fflush(errlog);
  56.     return 0;
  57. }
  58.  
  59. /* ---- strsave ---- */
  60.  
  61. char *strsave(char *s)
  62. {
  63.     char *p = NULL;
  64.     int len = 0;
  65.     if(!s)
  66.         return NULL;
  67.     len = strlen(s);
  68.     p = malloc(len+1);
  69.     if(!p)
  70.         return NULL;
  71.     strcpy(p,s);
  72.     return p;
  73. }
  74.  
  75. /* ---- dos_path ---- */
  76.  
  77. int dos_path(char *s)
  78. {
  79.     if(!s)
  80.         return -1;
  81.     for(;s && *s;s++)
  82.         if(*s == '/')
  83.             *s = '\\';
  84.     return 0;
  85. }
  86.  
  87. /* ---- url_path ---- */
  88.  
  89. int url_path(char *s)
  90. {
  91.     if(!s)
  92.         return -1;
  93.     for(;s && *s;s++)
  94.         if(*s == '\\')
  95.             *s = '/';
  96.     return 0;
  97. }
  98.  
  99. /* ---- DynArray utils */
  100.  
  101. DynArrayPtr da_create(int initsize)
  102. {
  103.     DynArrayPtr dp;
  104.  
  105.     dp = calloc(1,sizeof(DynArray));
  106.     if(!dp)
  107.         return NULL;
  108.  
  109.     if(initsize < 16)
  110.         initsize = 16;
  111.  
  112.     dp->num = 0;
  113.     dp->size = initsize;
  114.     dp->slots = (char **)calloc(initsize,sizeof(char *));
  115.  
  116.     return dp;
  117. }
  118.  
  119. int da_grow(DynArrayPtr dp)
  120. {
  121.     if(!dp)
  122.         return -1;
  123.     if(dp->size == 0)
  124.         dp->size = 32;
  125.     else
  126.         dp->size = 2 * dp->size;
  127.     dp->slots = (char **)realloc(dp->slots,dp->size * sizeof(char *));
  128.     return 0;
  129. }
  130.  
  131. int da_add(DynArrayPtr dp, char *s)
  132. {
  133.     char *p;
  134.  
  135.     if(!dp)
  136.         return -1;
  137.     
  138.     if(dp->num == dp->size)
  139.         da_grow(dp);
  140.  
  141.     p = strsave(s);
  142.     dp->slots[dp->num++] = p;
  143.  
  144.     return 0;
  145. }
  146.  
  147. int da_destroy(DynArrayPtr dp)
  148. {
  149.     int i;
  150.  
  151.     if(!dp)
  152.         return -1;
  153.     for(i=0;i<dp->num;i++)
  154.         if(dp->slots[i] != NULL)
  155.             free(dp->slots[i]);
  156.     free(dp->slots);
  157.     free(dp);
  158.     return 0;
  159. }
  160.  
  161. int has_bad_ini_chars(char *s)
  162. {
  163.     if(!s || !s[0])
  164.         return 0;
  165.     for(;*s;s++)
  166.         if(*s == '\"' || !isprint(*s))
  167.             return 1;
  168.  
  169.     return 0;
  170. }
  171.  
  172. /* ---- various utilities (from NCSA sample file 'util.c') ---- */
  173.  
  174. #define LF 10
  175. #define CR 13
  176.  
  177. void getword(char *word, char *line, char stop) {
  178.     int x = 0,y;
  179.  
  180.     for(x=0;((line[x]) && (line[x] != stop));x++)
  181.         word[x] = line[x];
  182.  
  183.     word[x] = '\0';
  184.     if(line[x]) ++x;
  185.     y=0;
  186.  
  187.     while(line[y++] = line[x++]);
  188. }
  189.  
  190. char *makeword(char *line, char stop) {
  191.     int x = 0,y;
  192.     char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));
  193.  
  194.     for(x=0;((line[x]) && (line[x] != stop));x++)
  195.         word[x] = line[x];
  196.  
  197.     word[x] = '\0';
  198.     if(line[x]) ++x;
  199.     y=0;
  200.  
  201.     while(line[y++] = line[x++]);
  202.     return word;
  203. }
  204.  
  205. char *fmakeword(FILE *f, char stop, int *cl) {
  206.     int wsize;
  207.     char *word;
  208.     int ll;
  209.  
  210.     wsize = 102400;
  211.     ll=0;
  212.     word = (char *) malloc(sizeof(char) * (wsize + 1));
  213.  
  214.     while(1) {
  215.         word[ll] = (char)fgetc(f);
  216.         if(ll==wsize) {
  217.             word[ll+1] = '\0';
  218.             wsize+=102400;
  219.             word = (char *)realloc(word,sizeof(char)*(wsize+1));
  220.         }
  221.         --(*cl);
  222.         if((word[ll] == stop) || (feof(f)) || (!(*cl))) {
  223.             if(word[ll] != stop) ll++;
  224.             word[ll] = '\0';
  225.             return word;
  226.         }
  227.         ++ll;
  228.     }
  229. }
  230.  
  231. char x2c(char *what) {
  232.     register char digit;
  233.  
  234.     digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
  235.     digit *= 16;
  236.     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
  237.     return(digit);
  238. }
  239.  
  240. void unescape_url(char *url) {
  241.     register int x,y;
  242.  
  243.     for(x=0,y=0;url[y];++x,++y) {
  244.         if((url[x] = url[y]) == '%') {
  245.             url[x] = x2c(&url[y+1]);
  246.             y+=2;
  247.         }
  248.     }
  249.     url[x] = '\0';
  250. }
  251.  
  252. void plustospace(char *str) {
  253.     register int x;
  254.  
  255.     for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
  256. }
  257.  
  258. int rind(char *s, char c) {
  259.     register int x;
  260.     for(x=strlen(s) - 1;x != -1; x--)
  261.         if(s[x] == c) return x;
  262.     return -1;
  263. }
  264.  
  265. int getline(char *s, int n, FILE *f) {
  266.     register int i=0;
  267.  
  268.     while(1) {
  269.         s[i] = (char)fgetc(f);
  270.  
  271.         if(s[i] == CR)
  272.             s[i] = fgetc(f);
  273.  
  274.         if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
  275.             s[i] = '\0';
  276.             return (feof(f) ? 1 : 0);
  277.         }
  278.         ++i;
  279.     }
  280. }
  281.  
  282. void send_fd(FILE *f, FILE *fd)
  283. {
  284.     int num_chars=0;
  285.     char c;
  286.  
  287.     while (1) {
  288.         c = fgetc(f);
  289.         if(feof(f))
  290.             return;
  291.         fputc(c,fd);
  292.     }
  293. }
  294.  
  295. int ind(char *s, char c) {
  296.     register int x;
  297.  
  298.     for(x=0;s[x];x++)
  299.         if(s[x] == c) return x;
  300.  
  301.     return -1;
  302. }
  303.  
  304. void escape_shell_cmd(char *cmd) {
  305.     register int x,y,l;
  306.  
  307.     l=strlen(cmd);
  308.     for(x=0;cmd[x];x++) {
  309.         if(ind("&;`'\"|*?~<>^()[]{}$\\",cmd[x]) != -1){
  310.             for(y=l+1;y>x;y--)
  311.                 cmd[y] = cmd[y-1];
  312.             l++; /* length has been increased */
  313.             cmd[x] = '\\';
  314.             x++; /* skip the character */
  315.         }
  316.     }
  317. }
  318.  
  319. /* ---- cgi_error ---- */
  320.  
  321. int cgi_error(char *s, int ret_code)
  322. {
  323. int i = 0;
  324. FILE *fp = stdout;
  325.  
  326.     if(glob_cgiwin)
  327.         fp = fopen(glob_cgiwin_out,"w");
  328.  
  329.     fprintf(fp,"Content-type: text/plain\n\n");
  330.     
  331.     fprintf(fp,"Error in CGI-WIN gateway script.\n\nReason: %s.\n\n",s);
  332.  
  333.     fprintf(fp,"-----------------------------------------\n");
  334.     fprintf(fp,"Command-line arguments (%d)\n\n",glob_argc);
  335.     for(i=0;i<glob_argc;i++)
  336.         fprintf(fp,"\t[%d]: '%s'\n",i,glob_argv[i]);
  337.     fprintf(fp,"-----------------------------------------\n");
  338.     fprintf(fp,"Environment variables\n\n");
  339.     for(i=0;glob_env[i] != NULL;i++)
  340.         fprintf(fp,"\t'%s'\n",glob_env[i]);
  341.  
  342.     if(glob_clen > 0)
  343.     {
  344.         fprintf(fp,"-----------------------------------------\n");
  345.         fprintf(fp,"Content (%d)\n\n",glob_clen);
  346.         for(i=0;i<glob_clen;i++)
  347.             fputc(glob_content[i],fp);
  348.     }
  349.  
  350.     fflush(fp);
  351.  
  352.     return ret_code;
  353. }
  354.  
  355. /* ---- tempfile creation ----- */
  356.  
  357. int get_tempfile(char *dir, char *prefix, int num, char **fn)
  358. {
  359.     char *tempdir = NULL;
  360.     char path[_MAX_PATH];
  361.     char dirbuf[_MAX_PATH];
  362.     int code = 0;
  363.  
  364.     /* will try looking for vermeer.ini if it doesn't get a
  365.        TMP or TEMP var in CGI environment */
  366.  
  367.     tempdir = dir;
  368.     if(!tempdir)
  369.         tempdir = getenv("TMP");
  370.     if(!tempdir)
  371.         tempdir = getenv("TEMP");
  372.     if(!tempdir)
  373.     {
  374.         /* could be a Vermeer server without TMP/TEMP vars; use FrontPage temp */
  375.         DWORD count;
  376.  
  377.         dirbuf[0] = 0;
  378.         count = GetPrivateProfileString("FrontPage","FrontPageRoot","*",
  379.                     dirbuf,_MAX_PATH-1,"vermeer.ini");
  380.         if(dirbuf[0] != 0 && dirbuf[0] != '*') /* key was found */
  381.         {
  382.             char c;
  383.  
  384.             /* need to append TEMP */
  385.             c = dirbuf[strlen(dirbuf)-1];
  386.             if(c != '\\')
  387.                 strcat(dirbuf,"\\TEMP");
  388.             else
  389.                 strcat(dirbuf,"TEMP");
  390.             tempdir = dirbuf;
  391.         }
  392.     }
  393.     if(!tempdir)
  394.         tempdir = "C:\\TEMP";  /* try hardwired location as a last resort */
  395.  
  396.     if(!prefix)
  397.         prefix = "";
  398.  
  399.     path[0] = 0;
  400.     code = GetTempFileName(tempdir,prefix,num,path);
  401.     *fn = strsave(path);
  402.  
  403.     return code;
  404. }
  405.  
  406. /* ---- get_exe_path ---- */
  407.  
  408. /*
  409.     construct absolute path to the *real* script.
  410.     
  411.     subtract PATH_INFO from PATH_TRANSLATED to get the server's DocumentRoot. 
  412.  
  413.     if PATH_TRANSLATED is not provided, try to figure it out from
  414.     either argv[0] or _getcwd().
  415.     
  416.     if the *first component* of PATH_INFO does *not* end in ".exe",
  417.     then it is a *server-relative* path to the requested CGI-WIN program;
  418.     otherwise, it is the name of the requested CGI-WIN program in the same
  419.     dir as this program.
  420.  
  421.     success = 0, failure = -1
  422. */
  423.  
  424. int get_exe_path(char **path_return)
  425. {
  426.     char *path_info = NULL;
  427.     char *path_trans = NULL;
  428.     char *script_name = NULL;
  429.     char abs_path[_MAX_PATH];
  430.     char exe_dir[_MAX_PATH];
  431.     char path_upper[_MAX_PATH];
  432.     char path_trans_upper[_MAX_PATH];
  433.     char script_path[_MAX_PATH];
  434.     char synth_path_trans[_MAX_PATH];
  435.     char *p = NULL;
  436.     char *q = NULL;
  437.     int len = 0;
  438.     int i = 0;
  439.  
  440.     /* REVIEW (everywhere): need to check more carefully for string overruns */
  441.  
  442.     /* initialize auto strings to be empty */
  443.     abs_path[0] = 0;
  444.     exe_dir[0] = 0;
  445.     path_upper[0] = 0;
  446.     path_trans_upper[0] = 0;
  447.     script_path[0] = 0;
  448.     synth_path_trans[0] = 0;
  449.  
  450.     path_info = getenv("PATH_INFO");
  451.     if(!path_info)
  452.         return cgi_error("expected PATH_INFO cgi variable (giving path to script file)",-1);
  453.  
  454.     script_name = getenv("SCRIPT_NAME");
  455.     if(!script_name)
  456.         return cgi_error("expected SCRIPT_NAME cgi variable",-1);
  457.  
  458.     path_trans = getenv("PATH_TRANSLATED");
  459.     if(!path_trans)
  460.     {
  461.         /* ouch; have to figure it out on our own! use argv[0] if it looks good,
  462.             else, if server did a 'cd' before exec, then use getcwd and argv[0]
  463.             to figure out abs path to program, try to subtract SCRIPT_NAME from
  464.             this to get DocumentRoot */
  465.         if(strchr(glob_argv[0],':')) 
  466.         {
  467.             /* it's a Windows absolute path to this program */
  468.             strcpy(synth_path_trans,glob_argv[0]);
  469.         }
  470.         else
  471.         {
  472.             extern char *_getcwd();
  473.             /* it's a relative path; get current dir and append it */
  474.             if(!_getcwd(synth_path_trans,_MAX_PATH-1))
  475.                 return cgi_error("couldn't get current dir to synthesize PATH_TRANSLATED cgi variable",-1);
  476.             /* append path to this program */
  477.             if(*glob_argv[0] != '\\' && *glob_argv[0] != '/')
  478.                 strcat(synth_path_trans,"\\");
  479.             strcat(synth_path_trans,glob_argv[0]);
  480.         }
  481.         /* now have full exe name; subtract SCRIPT_NAME to get DocumentRoot */
  482.         if(strlen(synth_path_trans) < strlen(script_name))
  483.             return cgi_error("absolute path to CGI program is shorter than SCRIPT_NAME",-1);
  484.         synth_path_trans[strlen(synth_path_trans)-strlen(script_name)] = 0;
  485.         /* add the extra path info */
  486.         strcat(synth_path_trans,path_info);
  487.         path_trans = synth_path_trans;
  488.     }
  489.  
  490.     /* removing PATH_INFO from PATH_TRANSLATED gives us DocumentRoot */
  491.     len = strlen(path_trans)-strlen(path_info);
  492.     strncpy(abs_path,path_trans,len);
  493.     abs_path[len] = 0;
  494.  
  495.     /* convert to all upper-case for matching (converted path can have different case);
  496.         also, convert all *_upper strings to have slashes all the same way */
  497.     strcpy(path_upper,path_info);
  498.     len = strlen(path_upper);
  499.     for(i=0;i<len;i++)
  500.     {
  501.         if(path_upper[i] == '/')
  502.             path_upper[i] = '\\';
  503.         else
  504.             path_upper[i] = toupper(path_upper[i]);
  505.     }
  506.  
  507.     strcpy(path_trans_upper,path_trans);
  508.     len = strlen(path_trans_upper);
  509.     for(i=0;i<len;i++)
  510.     {
  511.         if(path_trans_upper[i] == '/')
  512.             path_trans_upper[i] = '\\';
  513.         path_trans_upper[i] = toupper(path_trans_upper[i]);
  514.     }
  515.  
  516.     p = strstr(path_trans_upper,path_upper);
  517.     if(!p)
  518.         return cgi_error("couldn't find PATH_INFO inside PATH_TRANSLATED",-1);
  519.  
  520.     /* 
  521.         see if first component of PATH_INFO ends in .exe;
  522.         if so, then script is in same dir as this program,
  523.         else it's a server-relative path
  524.     */    
  525.  
  526.     p = strstr(path_upper,".EXE");
  527.     if(p)
  528.     {
  529.         if(*(p+4) != '\\' && *(p+4) != '\0')
  530.         {
  531.             /* error: .exe must terminate a path component */
  532.             return cgi_error("found a non-terminal .EXE string in PATH_INFO cgi variable",-1);
  533.         }
  534.  
  535.         /* get path to *real* script */
  536.         len = (p - path_upper) + 4;
  537.         strncpy(script_path,path_info,len);
  538.         script_path[len] = 0;
  539.  
  540.         /* save script path for script we invoke */
  541.         strcat(glob_scr_path,script_name);
  542.         strcat(glob_scr_path,script_path);
  543.  
  544.         /* save remainder of PATH_INFO for script we invoke */
  545.         strcpy(glob_log_path,path_info+(p-path_upper)+4);
  546.  
  547.         /* we have an EXE, but is it the *first* component? */
  548.         for(q = p; q > path_upper && *q != '\\'; q--);
  549.         if(q == path_upper)  /* yup, it's in same dir */
  550.         {
  551.             /* discover current dir from SCRIPT_NAME cgi var */
  552.             q = strrchr(script_name,'/');
  553.             if(q)
  554.             {
  555.                 len = strlen(script_name) - strlen(q);
  556.                 strncpy(exe_dir,script_name,len);
  557.                 exe_dir[len] = 0;
  558.                 strcat(abs_path,exe_dir);
  559.                 strcat(abs_path,script_path);
  560.             }
  561.             else
  562.                 return cgi_error("couldn't find trailing '/' in SCRIPT_NAME cgi variable",-1);
  563.         }
  564.         else  /* nope; it's server-relative */
  565.         {
  566.             /* append server-relative path to script */
  567.             strcat(abs_path,script_path);            
  568.         }
  569.     }
  570.     else
  571.         return cgi_error("expected a path to .EXE program in PATH_INFO cgi variable",-1);
  572.  
  573.     p = strsave(abs_path);
  574.     if(!p)
  575.         return cgi_error("out of memory",-1);
  576.  
  577.     *path_return = p;
  578.     dos_path(*path_return);
  579.  
  580.     /* save exe path for script we invoke */
  581.     strcpy(glob_phy_path,*path_return);
  582.  
  583.     /* make sure slashes are all correct direction */
  584.     dos_path(glob_phy_path);
  585.     url_path(glob_log_path);
  586.     url_path(glob_scr_path);
  587.  
  588.     return 0;
  589. }
  590.  
  591. /* ---- wait_for_exe ---- */
  592.  
  593. /*
  594.     (the following is now done in-line in main())
  595.  
  596.     launch a program using an *absolute* path,
  597.     with a given arg list and environment,
  598.     and wait until it finishes running before returning;
  599.     success = 0, failure = -1
  600. */
  601.  
  602. int wait_for_exe(char *cmd, char **argvp, char **envp)
  603. {
  604.     int exit_code = 0;
  605.     
  606.     exit_code = _spawnve(_P_WAIT,cmd,argvp,envp);
  607.     if(exit_code == -1)
  608.         return cgi_error("couldn't spawn process",-1);
  609.  
  610.     /* the following mess is what Microsoft suggests,
  611.        but I found the _spawn family to do the same thing */
  612.  
  613. #if 0
  614.     BOOL fSuccess;
  615.     BOOL fExit;
  616.     DWORD dwExitCode;
  617.     DWORD dw;
  618.     STARTUPINFO SI;
  619.     PROCESS_INFORMATION pi;
  620.     HANDLE hProcess;
  621.     HANDLE hThread;
  622.  
  623.     memset(&SI,'\0',sizeof(SI));
  624.     memset(&pi,'\0',sizeof(SI));
  625.  
  626.     SI.cb = sizeof(STARTUPINFO); 
  627.     SI.lpReserved = NULL;
  628.     SI.lpDesktop=NULL;
  629.     SI.lpTitle="CGIWIN32";
  630.     SI.cbReserved2=0;
  631.     SI.lpReserved2=NULL;
  632.  
  633.     fSuccess = CreateProcess((LPTSTR)NULL, (LPTSTR)cmd_line,
  634.                (LPSECURITY_ATTRIBUTES)NULL, 
  635.                (LPSECURITY_ATTRIBUTES)NULL,
  636.                (BOOL)TRUE,(DWORD)NORMAL_PRIORITY_CLASS,NULL,NULL,
  637.                (LPSTARTUPINFO)&SI,(LPPROCESS_INFORMATION)&pi); 
  638.  
  639.     if (fSuccess) 
  640.     {  
  641.         hProcess = pi.hProcess;    
  642.         hThread = pi.hThread;
  643.         dw = WaitForSingleObject(hProcess, INFINITE) ;
  644.         if (dw != 0xFFFFFFFF) 
  645.         { /* if we saw success ... */
  646.             /* pick up an exit code for the process */
  647.             fExit = GetExitCodeProcess(hProcess, &dwExitCode);
  648.         }
  649.         /* close the process and thread object handles */
  650.         CloseHandle(hThread);    
  651.         CloseHandle(hProcess);
  652.     }
  653.     else
  654.         return cgi_error("couldn't create process",-1);
  655.  
  656.     return 0;
  657. #endif
  658. }
  659.  
  660. /* ---- write_to_server ---- */
  661.  
  662. int write_to_server(char *filename)
  663. {
  664.     FILE *fp = NULL;
  665.     int ch = 0;
  666.  
  667.     if(!filename || !filename[0])
  668.         return cgi_error("write_to_server given empty filename",-1);
  669.  
  670.     fp = fopen(filename,"r"); 
  671.     if(!fp)
  672.         return cgi_error("write_to_server can't open file",-1);
  673.  
  674.     while(1)
  675.     {
  676.         ch = fgetc(fp);
  677.         if(ch == EOF)
  678.             break;
  679.         fputc(ch,stdout);
  680.     }
  681.     fclose(fp);
  682.  
  683.     return 0;
  684. }
  685.  
  686. /* ---- write_cgi_content ---- */
  687.  
  688. int write_cgi_content(char *filename)
  689. {
  690.     FILE *fp = NULL;
  691.     int ch = 0;
  692.  
  693.     if(glob_clen < 1)  /* nothing to write */
  694.         return 0;
  695.  
  696.     if(!filename || !filename[0])
  697.         return cgi_error("write_cgi_content given empty filename",-1);
  698.  
  699.     fp = fopen(filename,"w"); 
  700.     if(!fp)
  701.         return cgi_error("write_cgi_content can't open file",-1);
  702.  
  703.     if(fwrite(glob_content,1,glob_clen,fp) != (unsigned int)glob_clen)
  704.         return cgi_error("write_cgi_content didn't write all requested data",-1);
  705.  
  706.     fclose(fp);
  707.  
  708.     return 0;
  709. }
  710.  
  711. /* ---- write_accept_section ---- */
  712.  
  713. int write_accept_section(FILE *fp)
  714. {
  715.     char *cur = NULL;
  716.     char *p = NULL;
  717.     char *buf = NULL;
  718.     int len = 0;
  719.  
  720.     if(!fp)
  721.         return -1;
  722.  
  723.     p = getenv("HTTP_ACCEPT");
  724.     if(!p)
  725.         return -1;
  726.     buf = malloc(strlen(p));
  727.     if(!buf)
  728.         return -1;
  729.     buf[0] = 0;
  730.  
  731.     fprintf(fp,"\n[Accept]\n");
  732.  
  733.     cur = p;
  734.     while(cur && *cur)
  735.     {
  736.         char *q = NULL;
  737.         p = strchr(cur,',');
  738.         if(!p)  /* no more */
  739.         {
  740.             /* get options, if any */
  741.             q = strchr(cur,';');
  742.             if(q)
  743.             {
  744.                 q++;
  745.                 if(*q == ' ')
  746.                     q++;
  747.             }
  748.             fprintf(fp,"%s=%s\n",cur,q ? q : "Yes");
  749.             break;
  750.         }
  751.         else  /* found one */
  752.         {
  753.             len = p - cur;
  754.             strncpy(buf,cur,len);
  755.             buf[len] = 0;
  756.             /* get options, if any */
  757.             q = strchr(buf,';');
  758.             if(q)
  759.             {
  760.                 q++;
  761.                 if(*q == ' ')
  762.                     q++;
  763.             }
  764.             fprintf(fp,"%s=%s\n",buf,q ? q : "Yes");
  765.             cur = ++p;
  766.             if(*cur == ' ')
  767.                 cur++;
  768.         }
  769.     }
  770.  
  771.     free(buf);
  772.  
  773.     return 0;
  774. }
  775.  
  776. /* ---- write_form_fields ---- */
  777.  
  778. int write_form_fields(FILE *fp)
  779. {
  780.     char *cur = NULL;
  781.     char *p = NULL;
  782.     char *buf = NULL;
  783.     char *last = NULL;
  784.     int len = 0;
  785.     char *prev_key = NULL;
  786.     int ndup = 0;
  787.     int ntempfiles = 0;
  788.     int keyval_len = 0;
  789.     int i = 0;
  790.  
  791.     DynArrayPtr huge_vars = NULL;
  792.     DynArrayPtr ext_vars = NULL;
  793.  
  794.     /* the Win 3.1 server only checks for duplicates on the previous key,
  795.         not on all keys in the form; this code does the same */
  796.  
  797.     /* REVIEW - for an faster interface, we might want to just give pointers into
  798.         the content file, rather than creating a separate temp file for each
  799.         long field like the old Win 3.1 interface did; however, it appears
  800.         that would require a modification to CGI32.BAS */
  801.  
  802.     if(!fp || glob_clen < 1 || !glob_content)
  803.         return -1;
  804.  
  805.     huge_vars = da_create(16);
  806.     ext_vars = da_create(128);
  807.  
  808.     buf = malloc(glob_clen + 128);
  809.     if(!buf)
  810.         return -1;
  811.     prev_key = malloc(glob_clen + 128);
  812.     if(!prev_key)
  813.         return -1;
  814.     buf[0] = 0;
  815.     prev_key[0] = 0;
  816.  
  817.     /* write out Form Literal section, 
  818.         then Form External and Form Huge sections */
  819.  
  820.     fprintf(fp,"\n[Form Literal]\n");
  821.  
  822.     cur = glob_content;
  823.     last = glob_content + glob_clen - 1;
  824.  
  825.     while(cur <= last)
  826.     {
  827.         /* fetch next batch of chars, breaking at word or end */
  828.         p = strchr(cur,'&');
  829.         if(!p)
  830.         {
  831.             /* at end */
  832.             strcpy(buf,cur);
  833.             cur = cur + strlen(cur);
  834.         }
  835.         else
  836.         {
  837.             *p = 0;
  838.             strcpy(buf,cur);
  839.             *p = '&';
  840.             cur = p + 1;
  841.         }
  842.  
  843.         keyval_len = strlen(buf);
  844.  
  845.         plustospace(buf);
  846.         unescape_url(buf);
  847.  
  848.         p = strchr(buf,'=');
  849.         if(!p)  /* error */
  850.         {
  851.             fprintf(fp,"%s=ERROR - no equal sign in form value\n",buf);
  852.         }
  853.         else
  854.         {
  855.             char *cp = NULL;
  856.             char ext_key_val[_MAX_PATH];
  857.             char key_str[_MAX_PATH];
  858.  
  859.             key_str[0] = 0;
  860.             ext_key_val[0] = 0;
  861.             
  862.             *p++ = 0;
  863.  
  864.             if(!strcmp(buf,prev_key))
  865.             {
  866.                 ndup++;
  867.                 sprintf(key_str,"%s_%d",buf,ndup);
  868.             }
  869.             else
  870.             {
  871.                 ndup = 0;
  872.                 strcpy(key_str,buf);
  873.             }
  874.  
  875.             /* see if value string is too long for INI, or has control chars */
  876.  
  877.             len = strlen(p);
  878.  
  879.             if(len > 65535)
  880.             {
  881.                 /* falls into 'HUGE' range; 
  882.                     save it for writing out later;
  883.                     use offset and length of value */
  884.                 sprintf(ext_key_val,"%s=%d %d",key_str,
  885.                     p-glob_content,keyval_len);
  886.                 da_add(huge_vars,ext_key_val);
  887.             }
  888.             else if(len > 254 || has_bad_ini_chars(p))
  889.             {
  890.                 /* write to temp file */
  891.                 char temp_fn[_MAX_PATH];
  892.                 FILE *fp = NULL;
  893.  
  894.                 if(!glob_extfiles)
  895.                     glob_extfiles = da_create(32);
  896.  
  897.                 ntempfiles++;
  898.                 sprintf(temp_fn,"cwf%d.%03d",glob_tnum,ntempfiles);
  899.                 fp = fopen(temp_fn,"wb");
  900.                 if(!fp)
  901.                     return cgi_error("can't open temp file for field output",-1);
  902.                 if(fwrite(p,1,len,fp) != (unsigned int)len)
  903.                     return cgi_error("can't write field to temp file",-1);
  904.                 fclose(fp);
  905.                 da_add(glob_extfiles,temp_fn);
  906.  
  907.                 /* create string with tempfile name, write it out later */
  908.                 sprintf(ext_key_val,"%s=%s %d",key_str,temp_fn,len);
  909.                 da_add(ext_vars,ext_key_val);
  910.             }
  911.             else
  912.             {
  913.                 /* place in INI file */
  914.                 fprintf(fp,"%s=%s\n",key_str,p);
  915.             }
  916.  
  917.             strcpy(prev_key,buf);
  918.             *--p = '=';
  919.         }
  920.     }
  921.  
  922.     free(buf);
  923.  
  924.     /* now write out Form External section */
  925.     if(ext_vars->num > 0)
  926.     {
  927.         fprintf(fp,"\n[Form External]\n");
  928.         for(i=0; i<ext_vars->num; i++)
  929.             fprintf(fp,"%s\n",ext_vars->slots[i]);
  930.     }
  931.  
  932.     /* now write out Form Huge section */
  933.     if(huge_vars->num > 0)
  934.     {
  935.         fprintf(fp,"\n[Form Huge]\n");
  936.         for(i=0; i<huge_vars->num; i++)
  937.             fprintf(fp,"%s\n",huge_vars->slots[i]);
  938.     }
  939.  
  940.     da_destroy(ext_vars);
  941.     da_destroy(huge_vars);
  942.  
  943.     return 0;
  944. }
  945.  
  946. /* ---- write_cgi_input ---- */
  947.  
  948. int write_cgi_input(char *filename)
  949. {
  950.     FILE *fp = NULL;
  951.     char *p = NULL;
  952.  
  953.     if(!filename || !filename[0])
  954.         return cgi_error("write_cgi_input given empty filename",-1);
  955.  
  956.     fp = fopen(filename,"wt"); /* force Windows translation mode (INI format) */
  957.     if(!fp)
  958.         return cgi_error("write_cgi_input can't open file",-1);
  959.  
  960.     /* [CGI] section */
  961.     fprintf(fp,"\n[CGI]\n");
  962.     p = getenv("SERVER_PROTOCOL");
  963.     if(p)
  964.         fprintf(fp,"Request Protocol=%s\n",p);
  965.     p = getenv("REQUEST_METHOD");
  966.     if(p)
  967.         fprintf(fp,"Request Method=%s\n",p);
  968.  
  969.     /* SCRIPT_PATH, PATH_INFO, and PATH_TRANSLATED were rewritten during get_exe_path */
  970.     fprintf(fp,"Executable Path=%s\n",glob_scr_path);
  971.     fprintf(fp,"Logical Path=%s\n",glob_log_path); 
  972.     fprintf(fp,"Physical Path=%s\n",glob_phy_path);
  973.  
  974.     p = getenv("DOCUMENT_ROOT");
  975.     if(p)
  976.         fprintf(fp,"Document Root=%s\n",p);
  977.  
  978.     p = getenv("QUERY_STRING");
  979.     if(p)
  980.         fprintf(fp,"Query String=%s\n",p);
  981.     p = getenv("CONTENT_TYPE");
  982.     if(p)
  983.         fprintf(fp,"Content Type=%s\n",p);
  984.     p = getenv("CONTENT_LENGTH");
  985.     if(p)
  986.         fprintf(fp,"Content Length=%s\n",p);
  987.     if(glob_cfile)
  988.         fprintf(fp,"Content File=%s\n",glob_cfile);
  989.     p = getenv("SERVER_SOFTWARE");
  990.     if(p)
  991.         fprintf(fp,"Server Software=%s\n",p);
  992.     p = getenv("SERVER_NAME");
  993.     if(p)
  994.         fprintf(fp,"Server Name=%s\n",p);
  995.     p = getenv("SERVER_PORT");
  996.     if(p)
  997.         fprintf(fp,"Server Port=%s\n",p);
  998.     p = getenv("SERVER_ADMIN");
  999.     if(p)
  1000.         fprintf(fp,"Server Admin=%s\n",p);
  1001.  
  1002.     /* ignore the GATEWAY_INTERFACE var; write our own hard-coded value */
  1003.     fprintf(fp,"CGI Version=CGI/1.2 (Win)\n",p);
  1004.  
  1005.     /* NB: PWS-32 in FP 1.0 does not pass referer or from */
  1006.     p = getenv("HTTP_REFERER");
  1007.     if(p)
  1008.         fprintf(fp,"Referer=%s\n",p);
  1009.     p = getenv("HTTP_FROM");
  1010.     if(p)
  1011.         fprintf(fp,"From=%s\n",p);
  1012.  
  1013.     p = getenv("REMOTE_HOST");
  1014.     if(p)
  1015.         fprintf(fp,"Remote Host=%s\n",p);
  1016.     p = getenv("REMOTE_ADDR");
  1017.     if(p)
  1018.         fprintf(fp,"Remote Address=%s\n",p);
  1019.     p = getenv("AUTH_TYPE");
  1020.     if(p)
  1021.         fprintf(fp,"Authentication Method=%s\n",p);
  1022.     p = getenv("REMOTE USER");
  1023.     if(p)
  1024.         fprintf(fp,"Authenticated Username=%s\n",p);
  1025.     p = getenv("REMOTE_IDENT");
  1026.     if(p)
  1027.         fprintf(fp,"RFC-931 Identity=%s\n",p);
  1028.  
  1029.     /* [Accept] section */
  1030.     write_accept_section(fp);
  1031.  
  1032.     /* [System] section */
  1033.     fprintf(fp,"\n[System]\n");
  1034.     if(glob_ofile)
  1035.         fprintf(fp,"Output File=%s\n",glob_ofile);
  1036.     if(glob_cfile)
  1037.         fprintf(fp,"Content File=%s\n",glob_cfile);
  1038.     /* NB - not passed by PWS-32 in FP 1.0; read non-standard env var if set */
  1039.     p = getenv("CGIWIN_DEBUG");
  1040.     if(p)
  1041.         fprintf(fp,"Debug Mode=%s\n",p);
  1042.     else
  1043.         fprintf(fp,"Debug Mode=No\n");
  1044.     /* compute local GMT offset */
  1045.     {
  1046.         TIME_ZONE_INFORMATION tzi;
  1047.         tzi.Bias = 0; /* in case it fails */
  1048.         GetTimeZoneInformation(&tzi);
  1049.         fprintf(fp,"GMT Offset=%d\n",-60*tzi.Bias);
  1050.     }
  1051.  
  1052.     /* [Extra Headers] section */
  1053.     fprintf(fp,"\n[Extra Headers]\n");
  1054.  
  1055.     /* REVIEW - should really keep track of which envs have been spat out,
  1056.        then do any unused ones here */
  1057.  
  1058.     p = getenv("HTTP_CONNECTION");
  1059.     if(p)
  1060.         fprintf(fp,"HTTP_CONNECTION=%s\n",p);
  1061.     p = getenv("HTTP_PROXY_CONNECTION");
  1062.     if(p)
  1063.         fprintf(fp,"HTTP_CONNECTION=%s\n",p);
  1064.     p = getenv("HTTP_USER_AGENT");
  1065.     if(p)
  1066.         fprintf(fp,"HTTP_USER_AGENT=%s\n",p);
  1067.     p = getenv("HTTP_HOST");
  1068.     if(p)
  1069.         fprintf(fp,"HTTP_HOST=%s\n",p);
  1070.  
  1071.     /* [Form] sections */
  1072.     if(glob_clen > 0 && glob_content)
  1073.     {
  1074.         p = getenv("CONTENT_TYPE");
  1075.         if(p)
  1076.         {
  1077.             if(!stricmp(p,"application/x-www-form-urlencoded") 
  1078.                 || !stricmp(p,"application/www-form-urlencoded"))
  1079.             {
  1080.                 write_form_fields(fp);
  1081.             }
  1082.         }
  1083.     }
  1084.  
  1085.     fclose(fp);
  1086.  
  1087.     return 0;
  1088. }
  1089.  
  1090. /* ---- launch_cgiwin_program */
  1091.  
  1092. int launch_cgiwin_program()
  1093. {
  1094.     /* change argv[0], keep other args, keep same env, 
  1095.         mangle three args in INI file, exec program */
  1096.  
  1097.     char *cgifn = glob_argv[1];
  1098.     char *exe_path = NULL;
  1099.     char quoted_exe_path[_MAX_PATH];
  1100.     char script[_MAX_PATH];
  1101.     char path[_MAX_PATH];
  1102.     char path_trans[_MAX_PATH];
  1103.     char envstr[_MAX_PATH];
  1104.     DWORD count = 0;
  1105.     DynArrayPtr newargv = NULL;
  1106.     DynArrayPtr newenv = NULL;
  1107.     int i = 0;
  1108.  
  1109.     script[0] = 0;
  1110.     path[0] = 0;
  1111.     path_trans[0] = 0;
  1112.  
  1113.     glob_cgiwin_out[0] = 0;
  1114.     count = GetPrivateProfileString("System","Output File","*",
  1115.                 glob_cgiwin_out,_MAX_PATH-1,cgifn);
  1116.     if(glob_cgiwin_out[0] == 0 || glob_cgiwin_out[0] == '*') /* key not found */
  1117.         return cgi_error("couldn't get 'Output File' from CGI-WIN file in argv[1]",0);
  1118.  
  1119.     count = GetPrivateProfileString("CGI","Executable Path","*",
  1120.                 script,_MAX_PATH-1,cgifn);
  1121.     if(script[0] == 0 || script[0] == '*') /* key not found */
  1122.         return cgi_error("couldn't get 'Executable Path' from CGI-WIN file in argv[1]",0);
  1123.     
  1124.     count = GetPrivateProfileString("CGI","Logical Path","*",
  1125.                 path,_MAX_PATH-1,cgifn);
  1126.     if(path[0] == 0 || path[0] == '*') /* key not found */
  1127.         return cgi_error("couldn't get 'Logical Path' from CGI-WIN file in argv[1]",0);
  1128.  
  1129.     count = GetPrivateProfileString("CGI","Physical Path","*",
  1130.                 path_trans,_MAX_PATH-1,cgifn);
  1131.     if(path_trans[0] == 0 || path_trans[0] == '*') /* key not found */
  1132.         return cgi_error("couldn't get 'Physical Path' from CGI-WIN file in argv[1]",0);
  1133.  
  1134.     /* need to fool get_exe_path, so put these into environment */
  1135.  
  1136.     sprintf(envstr,"SCRIPT_NAME=%s",script);
  1137.     _putenv(envstr);
  1138.     sprintf(envstr,"PATH_INFO=%s",path);
  1139.     _putenv(envstr);
  1140.     sprintf(envstr,"PATH_TRANSLATED=%s",path_trans);
  1141.     _putenv(envstr);
  1142.  
  1143.     /* get absolute path to the *real* script */
  1144.  
  1145.     if(get_exe_path(&exe_path) != 0)
  1146.         return 0;
  1147.  
  1148.     /* quote exe_path in case it has spaces */
  1149.     sprintf(quoted_exe_path,"\"%s\"",exe_path);
  1150.  
  1151.     /* write correct values back to CGI-WIN file */
  1152.     if(!WritePrivateProfileString("CGI","Executable Path",glob_scr_path,cgifn))
  1153.         return cgi_error("couldn't write 'Executable Path' to CGI-WIN file in argv[1]",0);
  1154.     if(!WritePrivateProfileString("CGI","Logical Path",glob_log_path,cgifn))
  1155.         return cgi_error("couldn't write 'Logical Path' to CGI-WIN file in argv[1]",0);
  1156.     if(!WritePrivateProfileString("CGI","Physical Path",glob_phy_path,cgifn))
  1157.         return cgi_error("couldn't write 'Physical Path' to CGI-WIN file in argv[1]",0);
  1158.  
  1159.     /* set up new args and env, and launch program */
  1160.  
  1161.     newargv = da_create(64);
  1162.     newenv = da_create(64);
  1163.  
  1164.     /* do args; only difference is argv[0] */
  1165.     da_add(newargv,quoted_exe_path);
  1166.     for(i=1;i<glob_argc;i++)
  1167.         da_add(newargv,glob_argv[i]);
  1168.     da_add(newargv,NULL);
  1169.     
  1170.     /* do env; use _environ since _putenv may have invalidated old env */
  1171.     for(i=0;_environ != NULL && _environ[i] != NULL;i++)
  1172.         da_add(newenv,_environ[i]);
  1173.     da_add(newenv,NULL);
  1174.  
  1175.     /* exec *real* program */
  1176.     return _spawnve(_P_WAIT,exe_path,newargv->slots,newenv->slots);
  1177. }
  1178.  
  1179.  
  1180. /* ---- main --------- */
  1181.  
  1182. int main(int argc, char *argv[], char *env[])
  1183. {
  1184.     char *exe_path = NULL;
  1185.     char quoted_exe_path[_MAX_PATH];
  1186.     int clen = 0;
  1187.     char *p = NULL;
  1188.     char *query_info = NULL;
  1189.     int tmpnum = 0;
  1190.     FILE *fp = NULL;
  1191.     int i = 0;
  1192.  
  1193.     /* set up globals so cgi_error can report them */
  1194.     glob_argc = argc;
  1195.     glob_argv = &argv[0];
  1196.     glob_env = &env[0];
  1197.  
  1198.     /* set up globals for rewritten env vars */
  1199.     glob_log_path[0] = 0;
  1200.     glob_phy_path[0] = 0;
  1201.     glob_scr_path[0] = 0;
  1202.  
  1203.     /* if we're running under CGI-WIN interface, all args are in file */
  1204.  
  1205.     p = getenv("GATEWAY_INTERFACE");
  1206.     if(!p || strstr(p,"(Win)"))
  1207.     {
  1208.         if(argc > 1)
  1209.         {
  1210.             glob_cgiwin = 1;
  1211.             glob_cgiwin_out[0] = 0;
  1212.             return launch_cgiwin_program();
  1213.         }
  1214.         else
  1215.             return cgi_error("expected GATEWAY_INTERFACE environment variable",0);
  1216.     }
  1217.     
  1218.     /* get absolute path to the *real* script */
  1219.  
  1220.     if(get_exe_path(&exe_path) != 0)
  1221.         return 0;
  1222.  
  1223.     /* quote exe path in case it has spaces */
  1224.     sprintf(quoted_exe_path,"\"%s\"",exe_path);
  1225.  
  1226.     /* read in any content */
  1227.  
  1228.     p = getenv("CONTENT_LENGTH");
  1229.     if(p)
  1230.         clen = atoi(p);
  1231.     if(clen > 0)
  1232.     {
  1233.         extern int _setmode();
  1234.         int fd = -1;
  1235.         /* must reset to binary mode to read in content */
  1236.         p = malloc(clen+1);
  1237.         if(!p)
  1238.             return cgi_error("out of memory",0);
  1239.         fd = _fileno(stdin);
  1240.         _setmode(fd,_O_BINARY);
  1241.         if(fread(p,1,clen,stdin) != (unsigned int)clen)
  1242.         {
  1243.             free(p);
  1244.             return cgi_error("couldn't read content",0);
  1245.         }
  1246.         glob_content = p;
  1247.         glob_clen = clen;
  1248.         glob_content[glob_clen] = 0;
  1249.     }
  1250.  
  1251.     /* create temp files: input, output, and content;
  1252.         must create output and content files first,
  1253.         since input file refers to them; all will 
  1254.         have same numeric identifier, but different
  1255.         prefixes */
  1256.     
  1257.     glob_tnum = get_tempfile(NULL,"cwo",0,&glob_ofile);
  1258.     if(!glob_ofile)
  1259.     {
  1260.         cgi_error("out of memory",-1);
  1261.         goto cleanup;
  1262.     }
  1263.  
  1264.     get_tempfile(NULL,"cwc",glob_tnum,&glob_cfile);
  1265.     if(!glob_cfile)
  1266.     {
  1267.         cgi_error("out of memory",-1);
  1268.         goto cleanup;
  1269.     }
  1270.     if(write_cgi_content(glob_cfile) != 0)
  1271.         goto cleanup;
  1272.  
  1273.     get_tempfile(NULL,"cwi",glob_tnum,&glob_ifile);
  1274.     if(!glob_ifile)
  1275.     {
  1276.         cgi_error("out of memory",-1);
  1277.         goto cleanup;
  1278.     }
  1279.     if(write_cgi_input(glob_ifile) != 0)
  1280.         goto cleanup;
  1281.  
  1282.     /* launch program and wait for it to finish */
  1283.  
  1284.     if(_spawnl(_P_WAIT,exe_path,quoted_exe_path,glob_ifile,NULL) == -1)
  1285.         return cgi_error("failed to spawn program",0);
  1286.  
  1287.     /* send results back to server */
  1288.  
  1289.     if(write_to_server(glob_ofile) != 0)
  1290.         goto cleanup;
  1291.  
  1292. cleanup:;
  1293.  
  1294.     if(glob_content)
  1295.         free(glob_content);
  1296.         
  1297.     if(exe_path)
  1298.         free(exe_path);
  1299.  
  1300.     if(glob_ifile)
  1301.     {
  1302.         unlink(glob_ifile);
  1303.         free(glob_ifile);
  1304.     }
  1305.     if(glob_ofile)
  1306.     {
  1307.         unlink(glob_ofile);
  1308.         free(glob_ofile);
  1309.     }
  1310.     if(glob_cfile)
  1311.     {
  1312.         unlink(glob_cfile);
  1313.         free(glob_cfile);
  1314.     }
  1315.  
  1316.     if(glob_extfiles)
  1317.     {
  1318.         for(i=0;i<glob_extfiles->num;i++)
  1319.             unlink(glob_extfiles->slots[i]);
  1320.         da_destroy(glob_extfiles);
  1321.     }
  1322.  
  1323.     return 0;
  1324. }
  1325.  
  1326.