"Fossies" - the Fresh Open Source Software Archive

Member "delegate9.9.13/src/cgi.c" (17 Aug 2014, 39861 Bytes) of package /linux/misc/old/delegate9.9.13.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cgi.c" see the Fossies "Dox" file reference documentation.

    1 /*////////////////////////////////////////////////////////////////////////
    2 Copyright (c) 1996-2000 Yutaka Sato and ETL,AIST,MITI
    3 Copyright (c) 2001-2006 National Institute of Advanced Industrial Science and Technology (AIST)
    4 AIST-Product-ID: 2000-ETL-198715-01, H14PRO-049, H15PRO-165, H18PRO-443
    5 
    6 Permission to use this material for noncommercial and/or evaluation
    7 purpose, copy this material for your own use, and distribute the copies
    8 via publicly accessible on-line media, without fee, is hereby granted
    9 provided that the above copyright notice and this permission notice
   10 appear in all copies.
   11 AIST MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS
   12 MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS
   13 OR IMPLIED WARRANTIES.
   14 /////////////////////////////////////////////////////////////////////////
   15 Content-Type:   program/C; charset=US-ASCII
   16 Program:    cgi.c
   17 Author:     Yutaka Sato <ysato@etl.go.jp>
   18 Description:
   19 History:
   20     960110  created
   21 //////////////////////////////////////////////////////////////////////#*/
   22 #include <stdlib.h>
   23 #include "ystring.h"
   24 #include "delegate.h"
   25 #include "fpoll.h"
   26 #include "file.h"
   27 #include "http.h"
   28 #include "proc.h"
   29 #include "log.h"
   30 #include <ctype.h>
   31 #include <errno.h>
   32 #define MY_CGIVER   "1.1"
   33 
   34 void closeOnExecServPorts(int set);
   35 int scan_SHTML(Connection *Conn,FILE *tc,FILE *fc,FILE *fp,PCStr(req),PCStr(head),PCStr(vurl),PCStr(ourl),PCStr(path),PCStr(script),PCStr(expath));
   36 FileSize file_copyTimeout(FILE *src,FILE *dst,FILE *cache,FileSize bytes,int *binary,int timeout);
   37 char *fgetsLines(PVStr(line),int lsiz,FILE *in,int timeout);
   38 char *fgetsLinesX(PVStr(line),int lsiz,FILE *in,int timeout,int *rccp,int *binp);
   39 
   40 extern char **environ;
   41 #if defined(__FreeBSD__)
   42 #define safeputenv 1
   43 #else
   44 #define safeputenv (lMTSS_PUTENV()||tMTSS_PUTENV()) /* 9.9.4 MTSS force putenvs() "-Ete" */
   45 #endif
   46 
   47 #define getFieldValue(str,fld,buf,siz) getFieldValue2(str,fld,buf,siz)
   48 #define getFV(str,fld,buf)             getFieldValue2(str,fld,AVStr(buf),sizeof(buf))
   49 
   50 
   51 void addList(PVStr(list),int lsize,PCStr(elem))
   52 {   refQStr(tp,list); /**/
   53     tp = strtail(list);
   54 
   55     if( list < tp ){
   56         linescanX(", ",AVStr(tp),lsize-(tp-list));
   57         tp += strlen(tp);
   58     }
   59     linescanX(elem,AVStr(tp),lsize-(tp-list));
   60 }
   61 
   62 static void cgi_head2env(PCStr(head),StrVec *Evp)
   63 {   CStr(field,0x1000);
   64     CStr(accepts,2048);
   65     CStr(langs,256);
   66     const char *np;
   67     const char *fp;
   68     const char *vp; /* not "const" but fixed */
   69 
   70     accepts[0] = 0;
   71     langs[0] = 0;
   72 
   73     QStrncpy(field,head,sizeof(field)-2);
   74     strcat(field,"\r\n");
   75     fp = field;
   76 
   77     while( np = strpbrk(fp,"\r\n") ){
   78         truncVStr(np); np++;
   79         for( vp = fp; *vp; vp++ ){
   80             if( *vp == ' ' ){
   81                 goto NEXTFIELD;
   82             }
   83             if( *vp == ':' ){
   84                 truncVStr(vp); vp++;
   85                 if( isspace(*vp) )
   86                     vp++;
   87                 break;
   88             }
   89             if( islower(*vp) )
   90                 *(char*)vp = toupper(*vp); /**/
   91             else
   92             if( *vp == '-' )
   93                 *(char*)vp = '_';
   94         }
   95         if( strcmp(fp,"ACCEPT_LANGUAGE") == 0 ){
   96             addList(AVStr(langs),sizeof(langs),vp);
   97         }else
   98         if( strcmp(fp,"ACCEPT") == 0 ){
   99             addList(AVStr(accepts),sizeof(accepts),vp);
  100         }else{
  101             SVaddEnvf(Evp,"HTTP_%s=%s",fp,vp);
  102         }
  103     NEXTFIELD:
  104         fp = np;
  105         while( *fp == '\r' || *fp == '\n' )
  106             fp++;
  107     }
  108 
  109     if( accepts[0] ){
  110         SVaddEnvf(Evp,"HTTP_ACCEPT=%s",accepts);
  111     }
  112     if( langs[0] ){
  113         SVaddEnvf(Evp,"HTTP_ACCEPT_LANGUAGE=%s",langs);
  114     }
  115 }
  116 
  117 static const char *cgienv;
  118 int scan_CGIENV(Connection *Conn,PCStr(envlist))
  119 {
  120     cgienv = StrAlloc(envlist);
  121     return 0;
  122 }
  123 
  124 void cgi_makeEnv(PCStr(conninfo),PCStr(req),PCStr(head),PCStr(vurl),PCStr(vpath),PCStr(datapath),PCStr(scripturl),PCStr(extrapath),int mac,const char *av[],StrVec *Evp)
  125 {   const char *search;
  126     const char *dp;
  127     CStr(tmp,2048);
  128     int ac,ei;
  129     const char *es;
  130     const char *fp;
  131     const char *np;
  132     const char *vp;
  133     CStr(auth,1024);
  134     CStr(atype,128);
  135     CStr(auserpass,256);
  136     CStr(auser,256);
  137     CStr(method,128);
  138     const char *randenv;
  139     CStr(sv,MaxHostNameLen);
  140 
  141     randenv = 0;
  142     for( ei = 0; es = environ[ei]; ei++ ){
  143         if( strncmp(es,"RANDENV=",8) == 0 )
  144             randenv = es;
  145         else
  146         if( cgienv == NULL || strmatch_list(es,cgienv,"^=",NULL,NULL) ){
  147             if( Evp->sv_ecmax-1 <= Evp->sv_ec )
  148                 syslog_ERROR("CGIENV OVERFLOW %d/%d\n",Evp->sv_ec,Evp->sv_ecmax);
  149             else    Evp->sv_ev[Evp->sv_ec++] = (char*)es;
  150         }
  151     }
  152 
  153     atype[0] = auser[0] = 0;
  154     if( getFV(head,"Authorization",auth) ){
  155         HTTP_decompAuth(auth,AVStr(atype),sizeof(atype),AVStr(auserpass),sizeof(auserpass));
  156         Xsscanf(auserpass,"%[^:]",AVStr(auser));
  157     }
  158 
  159     /* AUTH_TYPE */
  160     SVaddEnvf(Evp,"AUTH_TYPE=%s",atype);
  161 
  162     /* CONTENT_LENGTH */
  163     if( getFV(head,"Content-Length",tmp) == 0 )
  164         strcpy(tmp,"0");
  165     SVaddEnvf(Evp,"CONTENT_LENGTH=%d",atoi(tmp));
  166 
  167     /* CONTENT_TYPE */
  168     if( getFV(head,"Content-Type",tmp) == 0 )
  169         strcpy(tmp,"text/html");
  170     SVaddEnvf(Evp,"CONTENT_TYPE=%s",tmp);
  171 
  172     /* GATEWAY_INTERFACE */
  173     SVaddEnvf(Evp,"GATEWAY_INTERFACE=CGI/%s",MY_CGIVER);
  174 
  175     /* HTTP-* */
  176     cgi_head2env(head,Evp);
  177 
  178     /* PATH_INFO */
  179     es =
  180     SVaddEnvf(Evp,"PATH_INFO=%s",extrapath);
  181     if( es )
  182     if( search = strchr(es,'?') )
  183         truncVStr(search);
  184 
  185     /* PATH_TRANSLATED */
  186     SVaddEnvf(Evp,"PATH_TRANSLATED=%s",*extrapath?datapath:"");
  187     Verbose("PATH_TRANSLATED=%s\n",*extrapath?datapath:"");
  188 
  189     /* QUERY STRING */
  190     if( search = strchr(vpath,'?') ){
  191         truncVStr(search);
  192         search++;
  193     }
  194     SVaddEnvf(Evp,"QUERY_STRING=%s",search?search:"");
  195 
  196     /* REMOTE_ADDR */
  197     /* REMOTE_HOST */
  198     /* REMOTE_IDENT */
  199     /* REMOTE_USER */
  200     getFieldValue(conninfo,"Client-Addr",AVStr(tmp),sizeof(tmp));
  201     SVaddEnvf(Evp,"REMOTE_ADDR=%s",tmp);
  202 
  203     getFieldValue(conninfo,"Client-Host",AVStr(tmp),sizeof(tmp));
  204     SVaddEnvf(Evp,"REMOTE_HOST=%s",tmp);
  205 
  206     getFieldValue(conninfo,"Client-User-Ident",AVStr(tmp),sizeof(tmp));
  207     if( strcmp(tmp,"-") == 0 )
  208         tmp[0] = 0;
  209     SVaddEnvf(Evp,"REMOTE_IDENT=%s",tmp);
  210 
  211     SVaddEnvf(Evp,"REMOTE_USER=%s",auser);
  212 
  213     /* REQUEST_METHOD */
  214     wordScan(req,method);
  215     SVaddEnvf(Evp,"REQUEST_METHOD=%s",method);
  216     /* REQUEST_URL (extended for CFI) */
  217     SVaddEnvf(Evp,"REQUEST_URL=%s",vpath);
  218     SVaddEnvf(Evp,"REQUEST_URI=%s",vurl);
  219 
  220     /* SCRIPT_NAME */
  221     SVaddEnvf(Evp,"SCRIPT_NAME=%s",scripturl);
  222 
  223     /* SERVER_NAME */
  224     /* SERVER_PORT */
  225     /* SERVER_PROTOCOL */
  226     /* SERVER_SOFTWARE */
  227     {   CStr(svhp,MaxHostNameLen);
  228         CStr(svhost,MaxHostNameLen);
  229         int svport;
  230 
  231         svhost[0] = 0;
  232         if( getFV(head,"Host",svhp) ){
  233             svport = 80;
  234             Xsscanf(svhp,"%[^:]:%d",AVStr(svhost),&svport);
  235         }
  236         if( svhost[0] == 0 ){
  237             getFieldValue(conninfo,"Client-IF-Host",AVStr(svhp),sizeof(svhp));
  238             Xsscanf(svhp,"%[^:]:%d",AVStr(svhost),&svport);
  239             svport = scan_hostport("http",svhp,AVStr(svhost));
  240         }
  241         if( svhost[0] == 0 ){
  242             GetHostname(AVStr(svhost),sizeof(svhost));
  243             svport = SERVER_PORT();
  244         }
  245 
  246         SVaddEnvf(Evp,"SERVER_NAME=%s",svhost);
  247         SVaddEnvf(Evp,"SERVER_PORT=%d",svport);
  248         SVaddEnvf(Evp,"SERVER_PROTOCOL=HTTP/%s",MY_HTTPVER);
  249         SVaddEnvf(Evp,"SERVER_SOFTWARE=DeleGate/%s",
  250                 DELEGATE_ver());
  251     }
  252 
  253     /* 9.2.4 to be used in SSLway for session cache ...
  254      * a Cookie for SSLsession should be used.
  255      */
  256     if( getFieldValue(conninfo,"Server-Host",AVStr(sv),sizeof(sv)) ){
  257         const char *sva;
  258         if( sva = gethostaddr(sv) )
  259         SVaddEnvf(Evp,"SERVER_ADDR=%s",sva);
  260         SVaddEnvf(Evp,"SERVER_HOST=%s",sv);
  261     }
  262 
  263     if( randenv )
  264         Evp->sv_ev[Evp->sv_ec++] = (char*)randenv;
  265     Evp->sv_ev[Evp->sv_ec] = 0;
  266 
  267     if( av != NULL ){
  268         ac = 0;
  269         if( search ){
  270             fp = SPrintf(&Evp->sv_MemF,"%s+",search);
  271             while( np = strchr(fp,'+') ){
  272                 if( mac-1 <= ac ){
  273                     break;
  274                 }
  275                 av[ac++] = (char*)fp;
  276                 truncVStr(np);
  277                 fp = np + 1;
  278             }
  279         }
  280         av[ac] = 0;
  281     }
  282 /*
  283 for( ei = 0; ei < ac; ei++ ) fprintf(stderr,"#### ARG[%2d] %s\n",ei,av[ei]);
  284 for( ei = 0; ei < ec; ei++ ) fprintf(stderr,"#### ENV[%2d] %s\n",ei,ev[ei]);
  285 */
  286 }
  287 
  288 int getParam(PVStr(params),PCStr(name),PVStr(val),int siz,int del);
  289 int CCX_lockoutcode(CCXP ccx);
  290 int CCX_setindflt(CCXP ccx,PCStr(from));
  291 /*
  292 void HTCCX_setindflt(Connection *Conn,PVStr(ctype)){
  293     IStr(cset,128);
  294     if( CCXactive(CCX_TOCL) ){
  295         getParam(AVStr(ctype),"charset",AVStr(cset),sizeof(cset),0);
  296         if( cset[0] && !strcaseeq(cset,"ISO-2022-JP") ){
  297             CCX_setindflt(CCX_TOCL,cset);
  298         }
  299     }
  300 }
  301 */
  302 static void setcharcode(Connection *Conn,PCStr(field),PVStr(value)){
  303     IStr(ie,128);
  304     IStr(oe,128);
  305 
  306     if( strcaseeq(field,"Content-Type") ){
  307         getParam(AVStr(value),"charset",AVStr(ie),sizeof(ie),0);
  308         strcpy(oe,ie);
  309     }else
  310     if( strcaseeq(field,"CCX-Control") ){
  311         getParam(AVStr(value),"ie",AVStr(ie),sizeof(ie),0);
  312         getParam(AVStr(value),"oe",AVStr(oe),sizeof(oe),0);
  313         if( ie[0] == 0 && oe[0] == 0 ){
  314             strcpy(ie,value);
  315             strcpy(oe,value);
  316         }
  317     }
  318     if( strcaseeq(ie,"iso-2022-jp") )
  319         strcpy(ie,"");
  320     if( CCXactive(CCX_TOCL) ){
  321         if( ie[0] ) CCX_setindflt(CCX_TOCL,ie);
  322         if( oe[0] ) CCX_setoutcode(CCX_TOCL,oe);
  323     }else{
  324         CCXcreate("*",oe,CCX_TOCL);
  325         if( ie[0] ) CCX_setindflt(CCX_TOCL,ie);
  326     }
  327 }
  328 
  329 static int cgi_response(Connection *Conn,PCStr(req),PVStr(ihead),FILE *in,FILE *out,FILE **xout,int *stcodep)
  330 {   CStr(ohead,0x10000);
  331     refQStr(hp,ohead); /**/
  332     CStr(ctype,1024);
  333     CStr(status,1024);
  334     int status_set;
  335     CStr(location,1024);
  336     CStr(mimever,1024);
  337     CStr(field,1024);
  338     CStr(value,1024);
  339     CStr(line,1024);
  340     const char *tp;
  341     int hleng,bleng,cleng,hcc;
  342     CStr(ostat,1024);
  343     const char *xcharset;
  344     int codeconv;
  345     int headonly;
  346     int putConnection;
  347     Connection ConnBuff;
  348     IStr(body,8*1024);
  349     int bodyL = -9;
  350     int bodyB = -9;
  351 
  352     if( Conn == NULL ){
  353         bzero(&ConnBuff,sizeof(Connection));
  354         Conn = &ConnBuff;
  355     }
  356 
  357     xcharset = HTTP_outCharset(Conn);
  358     if( xcharset && *xcharset ){
  359         /* CHARCODE=chset is specified, don't overwrite it */
  360         CCX_lockoutcode(CCX_TOCL);
  361     }
  362 
  363     if( getFieldValue2(ihead,"X-Status",AVStr(status),sizeof(status)) )
  364     {   HttpResponse Stat;
  365         if( 0 < decomp_http_status(status,&Stat) ){
  366             sprintf(status,"%d %s (filtered by CFI)",
  367                 Stat.hr_rcode,Stat.hr_reason);
  368         sv1log("## default status: %s\n",status);
  369         }
  370     }
  371     else
  372     strcpy(status,"200 CGI-OK");
  373     location[0] = 0;
  374     status_set = 0;
  375     if( getFV(ihead,"Content-Type",ctype) )
  376         rmField(AVStr(ihead),"Content-Type");
  377     else    strcpy(ctype,"text/plain");
  378     strcpy(mimever,MY_MIMEVER);
  379 
  380     hcc = 0;
  381     cleng = -1;
  382     headonly = strncasecmp(req,"HEAD ",5) == 0;
  383     putConnection = 0;
  384 
  385     for(;;){
  386         if( fgets(line,sizeof(line),in) == NULL )
  387             break;
  388         hcc += strlen(line);
  389 
  390         if( tp = strpbrk(line,"\r\n") )
  391             truncVStr(tp);
  392 
  393         if( line[0] == 0 )
  394             break;
  395 
  396         if( strchr(line,':') == NULL )
  397         if( line[0] != ' ' && line[0] != '\t' )
  398             /* is folding supported in HTTP ? */
  399         {
  400             sv1log("NON header from CGI program? %s\n",line);
  401             break;
  402         }
  403 
  404         fieldScan(line,field,value);
  405 
  406         if( strcasecmp(field,"MIME-Version") == 0 ){
  407             lineScan(value,mimever);
  408             continue;
  409         }
  410         if( strcasecmp(field,"Status") == 0 ){
  411             lineScan(value,status);
  412             status_set = 1;
  413             continue;
  414         }
  415         if( strcasecmp(field,"Location") == 0 ){
  416             lineScan(value,location);
  417             continue;
  418         }
  419         if( strcasecmp(field,"Content-Type") == 0 ){
  420             lineScan(value,ctype);
  421             setcharcode(Conn,field,AVStr(value));
  422             xcharset = HTTP_outCharset(Conn);
  423             continue;
  424         }
  425         if( strcasecmp(field,"Content-Length") == 0 )
  426             cleng = atoi(value);
  427 
  428         if( strcaseeq(field,"CCX-Control") ){
  429             setcharcode(Conn,field,AVStr(value));
  430             xcharset = HTTP_outCharset(Conn);
  431             continue;
  432         }
  433 
  434         sprintf(hp,"%s\r\n",line);
  435         hp += strlen(hp);
  436     }
  437     if( hcc == 0 )
  438         return 0;
  439 
  440     sprintf(hp,"MIME-Version: %s\r\n",mimever);
  441     hp += strlen(hp);
  442 
  443     if( getFV(ihead,"Server",line) == NULL ){
  444         sprintf(hp,"Server: DeleGate/%s\r\n",DELEGATE_ver());
  445         hp += strlen(hp);
  446     }
  447 
  448     if( location[0] ){
  449         CStr(server,MaxHostNameLen);
  450         if( status_set == 0 )
  451         strcpy(status,"302 Moved (output of CGI)");
  452         if( location[0] == '/' ){
  453             if( Conn )
  454                 ClientIF_HP(Conn,AVStr(server));
  455             else{
  456                 sprintf(server,"%s:%s",
  457                     getenv("SERVER_NAME"),
  458                     getenv("SERVER_PORT"));
  459             }
  460             sprintf(hp,"Location: http://%s%s\r\n",server,location);
  461         }else   sprintf(hp,"Location: %s\r\n",location);
  462         hp += strlen(hp);
  463     }
  464     sprintf(ostat,"HTTP/%s %s\r\n",MY_HTTPVER,status);
  465     sprintf(hp,"Content-Type: %s\r\n",ctype);
  466     codeconv = strncasecmp(ctype,"text/",5) == 0 && xcharset != NULL;
  467 
  468     if( CCXactive(CCX_TOCL) ){
  469         const char *ocs;
  470         ocs = CCXcharset(CCX_TOCL);
  471         sv1log("{C} CGI CHARCODE ie=%s; oe=%s\n",
  472             CCX_getindflt(CCX_TOCL),ocs?ocs:"");
  473     }
  474     if( MountOptions && isinList(MountOptions,"CHARCODE=thru")
  475     ){
  476         sv1log("##NoCCX(%s)%s\n",ctype,MountOptions?MountOptions:"");
  477         codeconv = 0;
  478     }
  479     if( codeconv == 0 && CCXguessing(CCX_TOCL) ){
  480         /* this should not be applied to non-text data... */
  481         const char *ics;
  482         /*
  483         fgetsLines(AVStr(body),sizeof(body)/4,in,3*1000);
  484         CCXexec(CCX_TOCL,body,strlen(body),AVStr(body),sizeof(body));
  485         */
  486         fgetsLinesX(AVStr(body),sizeof(body)/4,in,3*1000,&bodyL,&bodyB);
  487         CCXexec(CCX_TOCL,body,bodyL,AVStr(body),sizeof(body));
  488         ics = CCXident(CCX_TOCL);
  489         if( ics && *ics ){
  490             sv1log("{C} CGI guessed charset=%s [%s]\n",ics,
  491                 CCX_getindflt(CCX_TOCL));
  492             replace_charset(AVStr(hp),ics);
  493         }
  494     }
  495     if( codeconv )
  496         replace_charset(AVStr(hp),xcharset);
  497     hp += strlen(hp);
  498 
  499     if( atoi(status) == 304 )
  500         headonly = 1;
  501 
  502     if( stcodep )
  503         *stcodep = atoi(status);
  504 
  505     if( headonly
  506      || 0 < cleng && file_size(fileno(in)) - ftell(in) == cleng ){
  507         if( Conn ){
  508             if( getKeepAlive(Conn,AVStr(hp)) ){
  509                 putConnection = 1;
  510                 hp += strlen(hp);
  511             }
  512         }
  513     }
  514     if( Conn ){
  515         if( !putConnection ){
  516             sprintf(hp,"Connection: close\r\n");
  517             hp += strlen(hp);
  518         }
  519     }
  520 
  521     strcpy(hp,ihead);
  522     hp += strlen(hp);
  523     strcpy(hp,"\r\n");
  524 
  525     /*
  526     if( strcaseeq(ctype,"text/shtml") ){
  527     */
  528     if( strncasecmp(ctype,"text/shtml",10) == 0 ){
  529         if( xout != 0 ){
  530             *xout = TMPFILE("CGI -> SHTML");
  531             out = *xout;
  532         }
  533     }
  534 
  535     fputs(ostat,out);
  536     fputs(ohead,out);
  537     hleng = strlen(ostat) + strlen(ohead);
  538 
  539     if( headonly ){
  540         bleng = 0;
  541     }else{
  542         if( 0 < bodyL ){
  543             fwrite(body,1,bodyL,out);
  544         }
  545         /*
  546         if( body[0] ){
  547             fputs(body,out);
  548         }
  549         */
  550         if( codeconv )
  551             bleng = CCV_relay_text(Conn,in,out,NULL);
  552         else
  553         if( 0 < cleng )
  554             bleng = copyfile1(in,out);
  555         else    bleng = simple_relayf(in,out);
  556     }
  557 
  558     if( xout && *xout ){ /* v9.9.12 fix-140815a, strange ... */
  559         /* this is a workaround to escape a very strange
  560          * phenomena that the writable TMPFILE with buffered
  561          * input is inherited to children and appended output
  562          * on the automatic fflush() in exit() of the process;
  563          * ...
  564          * It is likely that the stream buffer for "rw" mode
  565          * is not able to be distinguished for read or write.
  566          */
  567         int ofd = dup(fileno(*xout));
  568         fclose(*xout);
  569         *xout = fdopen(ofd,"r");
  570     }
  571 
  572     return hleng+bleng;
  573 }
  574 static void putExecError(int tcd,PCStr(execpath))
  575 {   FILE *tc;
  576     const char *env;
  577     int serrno = errno;
  578 
  579     /*
  580     sv1log("#### FAILED EXEC: %s\n",execpath);
  581     */
  582     sv1log("#### FAILED EXEC: errno=%d %s\n",serrno,execpath);
  583     if( env = getenv("PATH") )
  584         sv1log("#### PATH=%s\n",env);
  585 
  586     tc = fdopen(tcd,"w");
  587     if( tc == NULL ){
  588         return;
  589     }
  590     if( serrno == ENOENT )
  591     {
  592     fprintf(tc,"Status: 404 not found\r\n");
  593     fprintf(tc,"Content-Type: text/plain\r\n");
  594     fprintf(tc,"\r\n");
  595     fprintf(tc,"Couldn't find the file.\r\n");
  596     }
  597     else
  598     {
  599     /*
  600     fprintf(tc,"Status: 500 cannot execute\r\n");
  601     */
  602     fprintf(tc,"Status: 503 Service Unavailable\r\n");
  603     fprintf(tc,"Content-Type: text/plain\r\n");
  604     fprintf(tc,"\r\n");
  605     fprintf(tc,"the service is unavailable temporarily.\r\n");
  606     /*
  607     fprintf(tc,"Couldn't find or execute the CGI script.\r\n");
  608     */
  609     }
  610     fflush(tc);
  611 }
  612 extern const char *BINSHELL;
  613 
  614 int filterDGENV(char *ev[],char *nev[],int nec);
  615 int execvpe(const char *path,char *av[],char *ev[]){
  616     IStr(xpath,1024);
  617     char *xev[1024];
  618     int rcode;
  619 
  620     fullpathCOM(path,"r",AVStr(xpath));
  621     filterDGENV(ev,xev,elnumof(xev));
  622     rcode = execve(xpath,av,xev);
  623     return rcode;
  624 }
  625 int SpawnvpeDirenv(const char *wh,const char *execpath,const char *av[],const char *ev[]){
  626     const char *nev[1024];
  627     int rcode = -1;
  628 
  629     return rcode;
  630 }
  631 
  632 int cgi_process(Connection *Conn,FILE *tc,PCStr(execpath),PCStr(workdir),const char *av[],const char *ev[],FILE *pfp[])
  633 {   int toCGI[2],fromCGI[2];
  634     const char *const *savenv;
  635     CStr(savdir,1024);
  636     CStr(savfds,2);
  637     int pid;
  638     FILE *cfp;
  639     const char *cav[32]; /**/
  640     CStr(cab,1024);
  641     int cai;
  642     int ai;
  643     char *nev[1024];
  644 
  645     IGNRETZ pipe(toCGI);
  646     IGNRETZ pipe(fromCGI);
  647     setCloseOnExec(toCGI[1] /*,1*/);
  648     setCloseOnExec(fromCGI[0] /*,1*/);
  649     savenv = (char const*const*)environ;
  650     /*
  651     environ = (char**)ev;
  652     */
  653     filterDGENV((char**)ev,nev,elnumof(nev));
  654     environ = nev;
  655 
  656     /*
  657     sv1log("chdir(%s)\n",workdir);
  658     */
  659     IGNRETS getcwd(savdir,sizeof(savdir));
  660     sv1log("CGI chdir(%s) <- %s\n",workdir,savdir);
  661     IGNRETZ chdir(workdir);
  662 
  663     savfds[0] = dup(0); dup2(toCGI[0],0); close(toCGI[0]);
  664     savfds[1] = dup(1); dup2(fromCGI[1],1); close(fromCGI[1]);
  665 
  666     if( cfp = fopen(execpath,"r") ){
  667         CStr(line,64);
  668         CStr(shell,32);
  669         bzero(line,sizeof(line));
  670         IGNRETP fread(line,1,sizeof(line),cfp);
  671         wordscanX(line,AVStr(shell),sizeof(shell)-1);
  672         if( strncasecmp(line,"#!CGI-DeleGate",14) == 0 ){
  673 /*
  674  * should pass access control parameters to control the access via CGI...
  675  */
  676             sprintf(cab,"+=%s",execpath);
  677             execpath = EXEC_PATH;
  678             cai = 0;
  679             cav[cai++] = (char*)execpath; 
  680             cav[cai++] = "-Fcgi";
  681             cav[cai++] = cab;
  682             cav[cai] = 0;
  683             av = cav;
  684         }
  685         if( strcmp(shell,"#!/bin/sh") == 0
  686          && strcmp(BINSHELL,"/bin/sh") != 0 ){
  687             cai = 0;
  688             cav[cai++] = (char*)BINSHELL;
  689             cav[cai++] = (char*)execpath;
  690             for( ai = 1; av[ai]; ai++ ){
  691                 if( 32-1 <= cai ){
  692                     break;
  693                 }
  694                 cav[cai++] = av[ai];
  695             }
  696             cav[cai++] = 0;
  697             av = cav;
  698             execpath = BINSHELL;
  699         }
  700         fclose(cfp);
  701     }
  702 
  703     if( INHERENT_fork() ){
  704         if( (pid = Fork("CGI")) == 0 ){
  705         /* don't use Execvp() because it not returns even on error. */
  706             closeOnExecServPorts(1);
  707             execvp(execpath,(char**)av);
  708             /*
  709             execvpe(execpath,(char**)av,(char**)ev);
  710             */
  711             putExecError(1,execpath);
  712             Finish(-1);
  713         }
  714     }else{
  715         extern int MIN_DGSPAWN_WAIT;
  716         int ws;
  717         int setwaitspawn(int ws);
  718         ws = setwaitspawn(MIN_DGSPAWN_WAIT-1);
  719         /* maybe the child is not DeleGate */
  720         closeOnExecServPorts(1);
  721         pid = SpawnvpDirenv("CGI",execpath,av);
  722         /*
  723         pid = SpawnvpeDirenv("CGI",execpath,av,ev);
  724         */
  725         closeOnExecServPorts(0); /* is this necessary ? */
  726         setwaitspawn(ws);
  727     }
  728     if( pid == -1 ){
  729         putExecError(1,execpath); /* write to fromCGI[1] */
  730     } 
  731 
  732     dup2(savfds[0],0); close(savfds[0]);
  733     dup2(savfds[1],1); close(savfds[1]);
  734     environ = (char**)savenv;
  735     IGNRETZ chdir(savdir);
  736     pfp[1] = fdopen(toCGI[1],"w");
  737     pfp[0] = fdopen(fromCGI[0],"r");
  738     return pid;
  739 }
  740 
  741 int file_copyBuffered(FILE *in,FILE *out);
  742 int checkChildAbort1(PCStr(where));
  743 int CCX_reqBody(Connection *Conn,PCStr(qhead),FILE *in,FILE *out,int len,int tout);
  744 
  745 extern double HTTP_TOUT_QBODY;
  746 int exec_cgi(Connection *Conn,PCStr(req),PCStr(reqhead),PCStr(scriptpath),PCStr(datapath),PCStr(vurl),PCStr(vpath),PCStr(scripturl),PCStr(extpath),FILE *fc,FILE *tc,int *stcodep)
  747 {
  748     FILE *pfp[2];
  749     CStr(oreq,2048);
  750     CStr(tmp,128);
  751     int leng;
  752     CStr(workdir,1024);
  753     const char *tp;
  754     const char *av[32]; /**/
  755     const char *ev[128]; /**/
  756     CStr(eb,0x10000);
  757     StrVec Env;
  758     CStr(conninfo,4096);
  759     int pid;
  760     FILE *xout;
  761 
  762     CStr(xvpath,4096);
  763     int HTCCX_reqURL(Connection *Conn,PCStr(url),PVStr(xvpath));
  764 
  765     if( !CCXactive(CCX_TOSV) ){
  766         /* might be no MOUNToption for CCX ... */
  767         scan_CCXTOSV(Conn);
  768     }
  769     if( HTCCX_reqURL(Conn,vpath,AVStr(xvpath)) ){
  770         sv1log("CGI vpath< %s\n",vpath);
  771         sv1log("CGI vpath> %s\n",xvpath);
  772         vpath = xvpath;
  773     }
  774 
  775     make_conninfo(Conn,AVStr(conninfo));
  776     strcpy(workdir,datapath);
  777     if( tp = strrchr(workdir,'/') )
  778         truncVStr(tp);
  779     av[0] = (char*)scriptpath;
  780 
  781     SVinit(&Env,"exec_cgi",ev,elnumof(ev)-1,AVStr(eb),sizeof(eb)); /* -1 for the entry of randenv */
  782     cgi_makeEnv(conninfo,req,reqhead,vurl,vpath,datapath,
  783         scripturl,extpath, 31,&av[1],&Env);
  784     pid = cgi_process(Conn,tc,scriptpath,workdir,av,ev,pfp);
  785 
  786     if( getFV(reqhead,"Content-Length",tmp) ){
  787         FileSize leng,wcc;
  788         int wcc2,isbinary,timeout,ch;
  789 
  790         Xsscanf(tmp,"%lld",&leng);
  791         timeout = (int)HTTP_TOUT_QBODY;
  792         wcc = CCX_reqBody(Conn,reqhead,fc,pfp[1],leng,timeout);
  793         if( wcc <= 0 && leng <= 0 ){
  794             /* 9.9.1 don't wait body when Content-Length:0 */
  795             wcc2 = 0;
  796             isbinary = 0;
  797         }else
  798         if( 0 < wcc ){
  799             wcc2 = 0;
  800             isbinary = 0;
  801         }else{
  802         wcc = file_copyTimeout(fc,pfp[1],NULL,leng,&isbinary,timeout);
  803         wcc2 = file_copyBuffered(fc,pfp[1]);
  804         }
  805         fflush(pfp[1]);
  806         sv1log("## Sent message body data to CGI [%lld+%d/%lld] %s\n",wcc,wcc2,leng,isbinary?"(BINARY)":"");
  807     }
  808     fclose(pfp[1]);
  809 /*
  810     leng = cgi_response(Conn,req,"",pfp[0],tc,stcodep);
  811 */
  812     xout = 0;
  813     leng = cgi_response(Conn,req,CVStr(""),pfp[0],tc,&xout,stcodep);
  814     fclose(pfp[0]);
  815 
  816     if( 0 < pid ){
  817         int xpid,start,ntry;
  818 
  819         start = time(NULL);
  820         {
  821         /* give a chance to the process for finalization...
  822          * this seems very effective.
  823          * and use usleep_bypoll() which does not use SIGALRM
  824          * (to make sleep work with -Tx option)
  825          */
  826         usleep_bypoll(1000);
  827         }
  828         for( ntry = 0; ; ntry++ ){
  829         if( Kill(pid,0) < 0 ){
  830             /* maybe errno == ESRCH (no such process) */
  831             /* (for Windows) should check if it's a my child */
  832             break;
  833         }
  834         xpid = checkChildAbort1("CGI");
  835         if( xpid < 0 && errno == ECHILD ){
  836             break;
  837         }
  838         /*
  839         xpid = NoHangWait();
  840         */
  841         sv1log("Wait*%d CGI-program exit: %d / %d (%d)\n",ntry,xpid,pid,
  842             ll2i(time(NULL)-start));
  843         if( xpid == pid )
  844             break;
  845         if( 10 < time(NULL)-start ){
  846             sv1log("KILL CGI-program to exit[%d]: %d\n",ntry,pid);
  847             Kill(pid,1);
  848             Kill(pid,9);
  849         }
  850         if( xpid <= 0 )
  851             usleep_bypoll(500*1000);
  852         }
  853     }
  854 
  855     if( xout != 0 ){
  856         CStr(head,4096);
  857         refQStr(hp,head); /**/
  858         const char *xp;
  859 
  860         sv1log("## SHTML from CGI\n");
  861         fflush(xout);
  862         fseek(xout,0,0);
  863 
  864         xp = head + (sizeof(head)-1);
  865         for(; hp < xp; hp += strlen(hp) ){
  866             if( xp-hp < 1 )
  867                 break;
  868             if( fgets(hp,xp-hp,xout) == NULL )
  869                 break;
  870             if( *hp == '\r' || *hp == '\n' )
  871                 break;
  872         }
  873         leng = scan_SHTML(Conn,tc,fc,xout,req,head,vurl,vpath,
  874             datapath,scripturl,extpath);
  875         fclose(xout);
  876     }
  877 
  878     return leng;
  879 }
  880 
  881 /* sleep without SIGALRM ... to avoid the effect of -Tx ? */
  882 /*
  883 void msleep_bypoll(int msec)
  884 {   int waits[2];
  885 
  886     Socketpair(waits);
  887     PollIn(waits[0],msec);
  888     close(waits[0]);
  889     close(waits[1]);
  890 }
  891 */
  892 
  893 static void dump(PCStr(request))
  894 {   int ei;
  895     const char *env;
  896 
  897     printf("Content-Type: text/html\r\n\r\n");
  898     printf("<H2>DeleGate as a CGI program</H2>\n");
  899     printf("<PRE>\r\n");
  900     printf("%s\r\n",request);
  901     for( ei = 0; env = environ[ei]; ei++ )
  902         printf("%s\r\n",env);
  903     printf("</PRE>\r\n");
  904     fflush(stdout);
  905 }
  906 
  907 int cgi_delegate(int ac,const char *av[],Connection *Conn)
  908 {   char ei;
  909     CStr(request,4096);
  910     refQStr(rp,request); /**/
  911     const char *env;
  912     const char *method;
  913     const char *url;
  914     const char *ver;
  915     CStr(field,128);
  916     CStr(value,4096);
  917     const char *host;
  918     const char *port;
  919     const char *path;
  920     CStr(server,1024);
  921     CStr(mount,1024);
  922     CStr(delegate,1024);
  923     const char *leng;
  924     const char *type;
  925     const char *query;
  926     CStr(qext,1024);
  927     const char *chost;
  928     const char *caddr;
  929     const char *cuser;
  930     int fromHttpd[2],toHttpd[2];
  931     int wcc;
  932 
  933     method = getenv("REQUEST_METHOD");
  934     url = getenv("PATH_INFO");
  935     ver = getenv("SERVER_PROTOCOL");
  936     host = getenv("SERVER_NAME");
  937     port = getenv("SERVER_PORT");
  938     path = getenv("SCRIPT_NAME");
  939     query = getenv("QUERY_STRING");
  940     chost = getenv("REMOTE_HOST");
  941     caddr = getenv("REMOTE_ADDR");
  942     cuser = getenv("REMOTE_IDENT");
  943 
  944     Verbose("REQUEST_METHOD=%s\n",method?method:"(NULL)");
  945     Verbose("PATH_INFO=%s\n",url?url:"(NULL)");
  946     Verbose("SERVER_PROTOCOL=%s\n",ver?ver:"(NULL)");
  947     Verbose("SERVER_NAME=%s\n",host?host:"(NULL)");
  948     Verbose("SERVER_PORT=%s\n",port?port:"(NULL)");
  949     Verbose("SCRIPT_NAME=%s\n",path?path:"(NULL)");
  950     Verbose("QUERY_STRING=%s\n",query?query:"(NULL)");
  951     Verbose("REMOTE_HOST=%s\n",chost?chost:"(NULL)");
  952     Verbose("REMOTE_ADDR=%s\n",caddr?caddr:"(NULL)");
  953     Verbose("REMOTE_IDENT=%s\n",cuser?cuser:"(NULL)");
  954 
  955     if( method == 0 || host == 0 || port == 0 || path == 0 ){
  956         printf("Status: 500 CGI-DeleGate Error\r\n");
  957         return -1;
  958     }
  959     if( caddr == 0 || ver == 0 ){
  960         printf("Status: 500 CGI-DeleGate Error\r\n");
  961         return -1;
  962     }
  963     if( url == 0 ){
  964         printf("Status: 404 CGI-DeleGate Not Found\r\n");
  965         return -1;
  966     }
  967 
  968     /*
  969      * CGI-DeleGate must be recognized as a directory as "xxx/"
  970      * so that "yyy" in CGI-DeleGate is represented as "xxx/yyy"
  971      */
  972     if( strtailchr(path) != '/' )
  973     if( url[0] == 0 ){
  974         CStr(urlb,1024);
  975         sprintf(urlb,"http://%s",host);
  976         if( atoi(port) != 80 )
  977         Xsprintf(TVStr(urlb),":%s",port);
  978         Xsprintf(TVStr(urlb),"%s/",path);
  979         printf("Status: 302 moved\r\n");
  980         printf("Location: %s\r\n",urlb);
  981         printf("\r\n");
  982         return 0;
  983     }
  984     if( url[0] == 0 )
  985         url = "/";
  986 
  987     sv1log("CGI-DeleGate accepted: %s@%s[%s]\n",
  988         cuser?cuser:"-",chost?chost:"",caddr?caddr:"");
  989 
  990     Conn->from_myself = 1;
  991     ACT_SPECIALIST = 1;
  992     if( chost && chost[0] )
  993         strcpy(CLNT_HOST,chost);
  994     else    strcpy(CLNT_HOST,caddr);
  995 
  996     sprintf(server,"%s://%s:%s%s","http",host,port,path);
  997     scan_SERVER(Conn,server);
  998 
  999 /*
 1000     sprintf(mount,"http://%s:%s%s",host,port,path);
 1001     set_MOUNT(Conn,"/-","=","");
 1002     set_MOUNT(Conn,"/*",mount,"");
 1003 */
 1004 
 1005     sprintf(delegate,"%s:%s",host,port);
 1006     scan_DELEGATE(Conn,delegate);
 1007     /*
 1008      * This cl_baseurl must used as the base URLpath for any absolute
 1009      * URLs in the response message (including URLs of built-in icons)
 1010      * put from this CGI-DeleGate...
 1011      */
 1012     if( Conn->cl_baseurl[0] == 0 )/* can be set manually by BASEURL */
 1013         set_BASEURL(Conn,path);
 1014 
 1015     /*
 1016     if( strtailchr(Conn->cl_baseurl) == '/' )
 1017         Conn->cl_baseurl[strlen(Conn->cl_baseurl)-1] = 0;
 1018      * cl_baseurl is expected to be without trailing '/' but the
 1019      * original SCRIPT_NAME can be ended with "/" when the CGI script
 1020      * is named like ../welcome.cgi (the / should be erased in the
 1021      * parent DeleGate??)
 1022      */
 1023 
 1024     if( query && *query )
 1025         sprintf(qext,"?%s",query);
 1026     else    qext[0] = 0;
 1027 
 1028 /* the response from this CGI-DeleGate must be in the version of the
 1029  * client request, or must be converted to it by the caller HTTP server,
 1030  * but it is not likely...
 1031  */
 1032 if( strcmp(ver,"HTTP/1.1") == 0 ) ver = "HTTP/1.0";
 1033 
 1034 
 1035     sprintf(rp,"%s %s%s %s\r\n",method,url,qext,ver?ver:"");
 1036     rp += strlen(rp);
 1037 
 1038     for( ei = 0; env = environ[ei]; ei++ ){
 1039         if( strncmp(env,"HTTP_",5) == 0 )
 1040         if( Xsscanf(env+5,"%[^=]=%[^\n]",AVStr(field),AVStr(value)) == 2 ){
 1041             const char *fp;
 1042 
 1043             for( fp = &field[1]; *fp; fp++ ){
 1044                 if( isupper(*fp) )
 1045                     *(char*)fp = tolower(*fp); /**/
 1046                 if( *fp == '_' )
 1047                     *(char*)fp = '-';
 1048             }
 1049             if( strcasecmp(field,"Accept") == 0 ){
 1050             }
 1051             sprintf(rp,"%s: %s\r\n",field,value);
 1052             rp += strlen(rp);
 1053         }
 1054     }
 1055 
 1056     /* dump(request); */
 1057 
 1058     /*
 1059      * Generate a HTTP request message header from CGI environment and
 1060      * relay it to the HTTP-DeleGate (acting as CGI-program) as if the
 1061      * requeset was sent from a usual HTTP client. It seems necessary
 1062      * to be a process to relay the body of a request message which may
 1063      * exist (POST method for example)
 1064      */
 1065     Socketpair(fromHttpd);
 1066     if( Fork("CGI-DeleGate-To") == 0 ){
 1067         fclose(stdout);
 1068         fclose(stderr);
 1069         close(fromHttpd[0]);
 1070 
 1071         if( type = getenv("CONTENT_TYPE") ){
 1072             sprintf(rp,"Content-Type: %s\r\n",type);
 1073             rp += strlen(rp);
 1074         }
 1075         if( leng = getenv("CONTENT_LENGTH") ){
 1076             sprintf(rp,"Content-Length: %s\r\n",leng);
 1077             rp += strlen(rp);
 1078         }
 1079         strcpy(rp,"\r\n");
 1080         IGNRETP write(fromHttpd[1],request,strlen(request));
 1081         wcc = simple_relayTimeout(fileno(stdin),fromHttpd[1],1000);
 1082         sv1log("CGI-DeleGate-To: relayed request body %d+%d bytes\n",
 1083             istrlen(request),wcc);
 1084         Finish(0);
 1085     }
 1086     close(fromHttpd[1]);
 1087     fclose(stdin);
 1088 
 1089     /*
 1090      * Relay a output from the HTTP-DeleGate to the server converting
 1091      * into the format of CGI output.
 1092      */
 1093     Socketpair(toHttpd);
 1094     if( Fork("CGI-DeleGate-From") == 0 ){
 1095         CStr(stat,1024);
 1096         const char *rcode;
 1097         CStr(head1,4096);
 1098         FILE *resp;
 1099         int ch,hcc,bcc;
 1100 
 1101         close(toHttpd[1]);
 1102         resp = fdopen(toHttpd[0],"r");
 1103         fgets(stat,sizeof(stat),resp);
 1104         hcc = strlen(stat);
 1105         bcc = 0;
 1106 
 1107         if( strncmp(stat,"HTTP/",5) == 0 ){
 1108            if( rcode = strchr(stat,' ') ){
 1109             fprintf(stdout,"Status: %s",rcode);
 1110             while( fgets(head1,sizeof(head1),resp) != NULL ){
 1111                 hcc += strlen(head1);
 1112 
 1113                 if( strncasecmp(head1,"Location:",9) == 0 ){
 1114 /* should be reverse MOUNTed ...?
 1115  * but it seems to be done in CGI-DeleGate with scan_DELEGATE() + BASEURL...
 1116  */
 1117                     fputs(head1,stdout);
 1118                 }else
 1119                 if( strncasecmp(head1,"Content-Type",12)==0
 1120                  || strncasecmp(head1,"Last-Modified",13)==0
 1121                 ){
 1122                     fputs(head1,stdout);
 1123                 }else
 1124                 if( head1[0] == '\r' || head1[0] == '\n' ){
 1125                     fputs(head1,stdout);
 1126                     break;
 1127                 }
 1128             }
 1129             }
 1130         }else   fputs(stat,stdout);
 1131         while( 0 < ready_cc(resp) ){
 1132             if( (ch = getc(resp)) == EOF )
 1133                 break;
 1134             putc(ch,stdout);
 1135             bcc++;
 1136         }
 1137         fflush(stdout);
 1138 
 1139         bcc += simple_relay(toHttpd[0],fileno(stdout));
 1140         sv1log("CGI-DeleGate-From: relayed response %d+%d bytes\n",hcc,bcc);
 1141         Finish(0);
 1142     }
 1143     close(toHttpd[0]);
 1144     fclose(stderr);
 1145     fclose(stdout);
 1146 
 1147     execGeneralist(Conn,fromHttpd[0],toHttpd[1],-1);
 1148     close(fromHttpd[0]);
 1149     close(toHttpd[1]);
 1150     LOG_flushall();
 1151 
 1152     /*
 1153      * wait() post processes to exit() not to finish before filtered output
 1154      * from post process drained.
 1155      */
 1156     wait(0);
 1157     wait(0);
 1158     return 0;
 1159 }
 1160 
 1161 int form2v(PVStr(form),int maxargc,const char *argv[])
 1162 {   refQStr(sp,form); /**/
 1163     const char *dp;
 1164     int argc;
 1165 
 1166     /*
 1167     for( argc = 0; argc < maxargc; argc++ ){
 1168     */
 1169     for( argc = 0; argc < maxargc; ){
 1170         if( maxargc-1 <= argc ){
 1171             daemonlog("F","## TOO MANY PARAMS IN FORM %d\n",argc);
 1172             break;
 1173         }
 1174         /*
 1175         argv[argc] = sp;
 1176         */
 1177         argv[argc++] = sp;
 1178         if( dp = strchr(sp,'&') ){
 1179             truncVStr(dp); dp++;
 1180         }
 1181         URL_unescape(sp,AVStr(sp),1,0);
 1182         /*
 1183         nonxalpha_unescape(sp,AVStr(sp),1);
 1184         */
 1185         if( dp == 0 )
 1186             break;
 1187         sp = (char*)dp;
 1188     }
 1189     argv[argc] = 0;
 1190     return argc;
 1191 }
 1192 int formdata2v(PCStr(ctype),PVStr(form),int maxargc,const char *argv[]){
 1193     int argc = 0;
 1194     CStr(line,256);
 1195     CStr(boundary,256);
 1196     int blen = 0;
 1197     int where;
 1198     const char *sp;
 1199     const char *np;
 1200     const char *pp;
 1201     int fnlen;
 1202     CStr(type,256);
 1203     CStr(name,256);
 1204     refQStr(fp,form);
 1205     const char *bodyp;
 1206     int enent = 0;
 1207 
 1208     truncVStr(boundary);
 1209     if( pp = strcasestr(ctype,"boundary=") ){
 1210         valuescanX(pp+9,AVStr(boundary),sizeof(boundary));
 1211         blen = strlen(boundary);
 1212     }
 1213 
 1214     sp = form;
 1215     where = 0;
 1216     bodyp = 0;
 1217     for( sp = form; *sp; sp = np ){
 1218         np = lineScan(sp,line);
 1219         /*
 1220         if( enent ){
 1221             encodeEntities(line,AVStr(line),1);
 1222         }
 1223         */
 1224         if( strneq(line,"--",2) && strneq(line+2,boundary,blen) ){
 1225             switch( where ){
 1226                 case 0: where = 1; /* header start */
 1227                     break;
 1228                 case 1: where = 0; /* header only end */
 1229                     setVStrPtrInc(fp,0);
 1230                     break;
 1231                 case 2:
 1232                 case 3: where = 1; /* body end */
 1233                     if( bodyp ){
 1234                         int bleng;
 1235                         const char *ep;
 1236                         ep = sp;
 1237                         if( bodyp+2 <= ep )
 1238                         if( strneq(ep-2,"\r\n",2) )
 1239                             ep -= 2;
 1240                         bleng = ep - bodyp;
 1241                         Bcopy(bodyp,fp,bleng);
 1242                         setVStrEnd(fp,bleng);
 1243                         fp += bleng + 1;
 1244                         bodyp = 0;
 1245                     }
 1246                     break;
 1247             }
 1248             truncVStr(sp);
 1249             enent = 0;
 1250         }
 1251         if( where == 1 ){
 1252             if( *line == 0 ){
 1253                 where = 2; /* body start */
 1254             }
 1255         }
 1256         if( where == 1 ){
 1257             if( fnlen = STRH(line,F_ContDisp) ){
 1258                 truncVStr(type);
 1259                 truncVStr(name);
 1260                 pp = wordScanY(line+fnlen,type,"^;");
 1261                 /*
 1262                 if( strcaseeq(type,"application/form-data") )
 1263                     enent = 1;
 1264                 */
 1265                 if( *pp == ';' ){
 1266                     pp++;
 1267                     if( *pp == ' ' ) pp++;
 1268                     if( strncaseeq(pp,"name=",5) ){
 1269                 valuescanX(pp+5,AVStr(name),sizeof(name));
 1270                     }
 1271                     argv[argc++] = fp;
 1272                     sprintf(fp,"%s=",name);
 1273                     fp += strlen(fp);
 1274                 }
 1275             }
 1276         }
 1277         if( *np == '\r' ) np++;
 1278         if( *np == '\n' ) np++;
 1279         if( where == 2 ){
 1280             bodyp = np;
 1281             where = 3;
 1282         }
 1283     }
 1284     argv[argc] = 0;
 1285     return argc;
 1286 }
 1287 int HTTP_form2v(Connection *Conn,FILE *fc,int maxargc,const char *argv[])
 1288 {   CStr(cLeng,1024);
 1289     defQStr(form);
 1290     int cleng,rcc;
 1291     int argc,argi;
 1292     CStr(ctyp,256);
 1293 
 1294     HTTP_getRequestField(Conn,"Content-Length",AVStr(cLeng),sizeof(cLeng));
 1295     cleng = atoi(cLeng);
 1296     setVStrEnd(ctyp,0);
 1297     HTTP_originalRequestField(Conn,"Content-Type",AVStr(ctyp),sizeof(ctyp));
 1298 
 1299     argc = 0;
 1300     /*
 1301     form[0] = 0;
 1302     */
 1303     if( 0 < cleng ){ /* && if Content-Type: x-form */
 1304         /*
 1305         if( sizeof(form) <= cleng ){
 1306             daemonlog("F","## POST FORM TOO LARGE %d\n",cleng);
 1307             cleng = sizeof(form) -1;
 1308         }
 1309         */
 1310         if( 0x10000 <= cleng ){
 1311             daemonlog("F","## POST FORM TOO LARGE %d\n",cleng);
 1312             cleng = 0x10000;
 1313         }
 1314         setQStr(form,(char*)malloc(cleng+1),cleng+1);
 1315         rcc = fread((char*)form,1,QVSSize(form,cleng),fc);
 1316         setVStrEnd(form,rcc);
 1317 
 1318         if( strncaseeq(ctyp,"multipart/form-data",19) ){
 1319             argc = formdata2v(ctyp,AVStr(form),maxargc,argv);
 1320         }else
 1321         argc = form2v(AVStr(form),maxargc,argv);
 1322     }
 1323     argv[argc] = 0;
 1324     return argc;
 1325 }
 1326 
 1327 int clientHTTP(Connection *Conn)
 1328 {
 1329     if( strcaseeq(CLNT_PROTO,"http") || strcaseeq(CLNT_PROTO,"https") )
 1330         return CurEnv != 0;
 1331     return 0;
 1332 }
 1333 
 1334 typedef struct {
 1335     StrVec   e_Env;
 1336   const char    *e_ev[128]; /**/
 1337     MStr(    e_eb,0x10000);
 1338 } CgiEnv;
 1339 static const char **getCgiEnv(Connection *Conn,CgiEnv *E)
 1340 {   CStr(ci,4096);
 1341     CStr(ourl,1024);
 1342 
 1343     if( !clientHTTP(Conn) )
 1344     if( (ClientFlags & PF_STLS_DO) ){
 1345         /* host-info. is necessary for SSL session cache in FCL */
 1346         make_conninfo(Conn,AVStr(ci));
 1347         SVinit(&E->e_Env,"substCGIENV",E->e_ev,elnumof(E->e_ev)-1,AVStr(E->e_eb),sizeof(E->e_eb));
 1348         cgi_makeEnv(ci,"","","","","","","",0,NULL,&E->e_Env);
 1349         return E->e_ev;
 1350     }else{
 1351         return (const char**)environ;
 1352     }
 1353 
 1354     HTTP_originalURLx(Conn,AVStr(ourl),sizeof(ourl));
 1355     make_conninfo(Conn,AVStr(ci));
 1356     SVinit(&E->e_Env,"substCGIENV",E->e_ev,elnumof(E->e_ev)-1,AVStr(E->e_eb),sizeof(E->e_eb));
 1357     cgi_makeEnv(ci,OREQ,OREQ_MSG,"",ourl,REQ_URL,"","",0,NULL,&E->e_Env);
 1358     return E->e_ev;
 1359 }
 1360 
 1361 const char **CFI_makeEnv(CgiEnv *Ev,PVStr(conninfo),Connection *Conn,PCStr(qhead),PCStr(rstat),PCStr(rhead)){
 1362     CStr(ourl,1024);
 1363     CStr(oupath,1024);
 1364     const char *vu = "";
 1365     const char *vup = "";
 1366     const char *xp = ""; /* to be PATH_INFO */
 1367     const char *dp = ""; /* to be PATH_TRNACLATED */
 1368     const char *scp = ""; /* should be script path of CFI-script ? */
 1369 
 1370     make_conninfo(Conn,AVStr(conninfo));
 1371     SVinit(&Ev->e_Env,"CFIsearch",Ev->e_ev,elnumof(Ev->e_ev)-1,AVStr(Ev->e_eb),sizeof(Ev->e_eb));
 1372 
 1373     /* these should be extracted from qhead */
 1374     HTTP_originalURLx(Conn,AVStr(ourl),sizeof(ourl));
 1375     /*
 1376     HTTP_originalURLPath(Conn,AVStr(oupath));
 1377     */
 1378     urlPath(ourl,AVStr(oupath));
 1379 
 1380     vu = ourl;
 1381     vup = oupath;
 1382     dp = REQ_URL;
 1383     xp = oupath;
 1384 
 1385     cgi_makeEnv(conninfo,OREQ,OREQ_MSG,vu,vup,dp,scp,xp,0,NULL,&Ev->e_Env);
 1386     return Ev->e_ev;
 1387 }
 1388 const char *CFI_searchSpec(PCStr(ci),PCStr(sp),PCStr(st),PCStr(he),int silent);
 1389 const char *CFI_searchSpecEnv(Connection *Conn,PCStr(sp),PCStr(rst),PCStr(rhead)){
 1390     CgiEnv Ev;
 1391     CStr(conninfo,2048);
 1392     char **savenv;
 1393     int silent;
 1394     const char *sp1;
 1395 
 1396     savenv = environ;
 1397     CFI_makeEnv(&Ev,AVStr(conninfo),Conn,OREQ_MSG,rst,rhead);
 1398     environ = (char**)Ev.e_ev;
 1399     silent = LOGLEVEL < 2;
 1400     sp1 = CFI_searchSpec(conninfo,sp,rst,rhead,silent);
 1401     environ = savenv;
 1402     return sp1;
 1403 }
 1404 
 1405 int substCGIENV(Connection *Conn,PCStr(name),PVStr(out),int size)
 1406 {   CgiEnv Ev;
 1407     const char *const*evp;
 1408     const char *e1;
 1409     int ei,nlen,match;
 1410 
 1411     if( getenv("CFI_TYPE") ) /* running as a filter already with CGIENVs */
 1412         evp = (char const*const*)environ;
 1413     else    evp = getCgiEnv(Conn,&Ev);
 1414     nlen = strlen(name);
 1415     match = 0;
 1416     for( ei = 0; e1 = evp[ei]; ei++ ){
 1417         if( strncmp(name,e1,nlen) == 0 )
 1418         if( e1[nlen] == '=' ){
 1419             linescanX(e1+nlen+1,AVStr(out),size);
 1420             match = 1;
 1421         }
 1422     }
 1423     return match;
 1424 }
 1425 int isinv(const char *av[],PCStr(v1));
 1426 /* 9.9.4 MTSS -Ete putting new environ. vector using putenv() and unsetenv()
 1427  * without directly rewriting the global *environ and environ[]
 1428  */
 1429 int putenvs(PCStr(wh),const char **ev){
 1430     int ei,ex,eo,en;
 1431     const char *e1;
 1432     IStr(name,128);
 1433     int log = 0;
 1434 
 1435     for( eo = 0; environ[eo]; eo++ );
 1436     for( ei = eo-1; 0 <= ei; ei-- ){
 1437         if( (e1 = environ[ei]) == 0 ){
 1438             break;
 1439         }
 1440         if( isinv(ev,e1) ){
 1441             /* to be left as is */
 1442         }else{
 1443             paramscanX(e1,"=",AVStr(name),sizeof(name));
 1444             if( getv((const char**)ev,name) ){
 1445                 /* to be overwritten */
 1446                 if( log ){
 1447                     sv1log("envR %s[%d] %s\n",wh,ei,e1);
 1448                 }
 1449             }else{
 1450                 if( log ){
 1451                     sv1log("env- %s[%d] %s\n",wh,ei,e1);
 1452                 }
 1453                 unsetenv(name);
 1454             }
 1455         }
 1456     }
 1457     if( log ){
 1458         for( ei = 0; e1 = ev[ei]; ei++ ){
 1459             if( 1 < log ){
 1460                 if( isinv((const char**)environ,e1) ){
 1461                     sv1log("env= %s[%d] %s\n",wh,ei,e1);
 1462                 }else{
 1463                     sv1log("env+ %s[%d] %s\n",wh,ei,e1);
 1464                 }
 1465             }
 1466         }
 1467     }
 1468     for( ei = 0; e1 = ev[ei]; ei++ ){
 1469         putenv(stralloc(e1));
 1470     }
 1471     en = ei;
 1472     if( log ){
 1473         sv1log("env# eo=%d en=%d\n",eo,en);
 1474         if( 1 < log ){
 1475             for( ei = 0; e1 = environ[ei]; ei++ ){
 1476                 ex = isinv(ev,e1)-1;
 1477                 sv1log("env#[%d][%d] %s\n",ex,ei,e1);
 1478             }
 1479         }
 1480     }
 1481     return 0;
 1482 }
 1483 
 1484 void putCGIENV(Connection *Conn)
 1485 {   CgiEnv Ev;
 1486     const char *const*ev;
 1487     CStr(conninfo,2048);
 1488 
 1489     if( (GatewayFlags & GW_IS_CFI) && CurEnv != NULL ){
 1490         ev = CFI_makeEnv(&Ev,AVStr(conninfo),Conn,OREQ_MSG,"","");
 1491     }else
 1492     ev = getCgiEnv(Conn,&Ev);
 1493     if( ev != (char const*const*)environ )
 1494     {
 1495         if( safeputenv ){
 1496             putenvs("putCGIENV",(const char**)ev);
 1497         }else
 1498         environ = (char**)dupv(ev,0);
 1499     }
 1500 }
 1501 static char **savenv;
 1502 static char **tmpenv;
 1503 static int tmpSUM;
 1504 
 1505 int strCRC32add(int crc,PCStr(str),int len);
 1506 int sumv(char *sv[]){
 1507     int ei;
 1508     int sum = 0;
 1509     char *e1;
 1510 
 1511     for( ei = 0; e1 = sv[ei]; ei++ ){
 1512         sum = strCRC32add(sum,(char*)&e1,sizeof(e1));
 1513     }
 1514     //fprintf(stderr,"--sumv(%X) = %X (%d)\n",sv,sum,ei);
 1515     return sum;
 1516 }
 1517 
 1518 void pushCGIENV(Connection *Conn,void *sevp)
 1519 {   char **ev;
 1520 
 1521     if( safeputenv ){
 1522         ev = (char**)dupv((const char*const*)environ,0);
 1523         *(char***)sevp = ev;
 1524         putCGIENV(Conn);
 1525         return;
 1526     }
 1527     ev = environ;
 1528     putCGIENV(Conn);
 1529     if( ev != environ )
 1530     {
 1531         if( savenv == 0 || ev != savenv ){
 1532             if( savenv != 0 ){
 1533                 /* on CYGWIN+SSHd? */
 1534                 /* free(savenv); */
 1535             }
 1536             /* 9.6.0 the original *environ[] can be applied free()
 1537              * in a putenv() even if the **environ does not point
 1538              * to the area of the original *environ[] (on Linux)
 1539              * Thus saving and restore **environ value pointing to
 1540              * the original *environ[] will cause fatal error as
 1541              * SEGV using broken (overwritten) area as *environ[].
 1542              */
 1543             savenv = (char**)dupv((const char*const*)ev,0);
 1544             *(char***)sevp = savenv;
 1545         }else{
 1546         *(char***)sevp = ev;
 1547         }
 1548         tmpenv = environ;
 1549         tmpSUM = sumv(environ);
 1550     }
 1551     else    *(char***)sevp = 0;
 1552 }
 1553 void popCGIENV(Connection *Conn,void *sevp)
 1554 {
 1555     Verbose("--restore environ:%X %X/%X %X\n",
 1556         p2i(environ),p2i(tmpenv),p2i(*(char***)sevp),p2i(savenv));
 1557 
 1558     if( *(char***)sevp && safeputenv ){
 1559         putenvs("popCGIENV",*(const char***)sevp);
 1560         freev(*(char***)sevp);
 1561     }else
 1562     if( *(char***)sevp && *(char***)sevp != environ ){
 1563         if( *(char***)sevp != savenv ){
 1564             daemonlog("F","--DON'T restore environ: %X != %X\n",
 1565                 p2i(*(char***)sevp),p2i(savenv));
 1566         }else
 1567         if( environ != tmpenv ){
 1568             daemonlog("F","--DON'T free environ: %X != %X\n",
 1569                 p2i(environ),p2i(tmpenv));
 1570         }else
 1571         if( sumv(environ) != tmpSUM ){
 1572             daemonlog("F","--DON'T free environ SUM: %X %X\n",
 1573                 tmpSUM,sumv(environ));
 1574         }else{
 1575         /*
 1576         freev(environ);
 1577             9.9.4 MTSS multi-thread signal safe environ[]
 1578         */
 1579             char **oenviron = environ;
 1580         environ = *(char***)sevp;
 1581             freev(oenviron);
 1582         }
 1583     }
 1584 }
 1585 
 1586 void System(DGC *ctx,PCStr(command),FILE *in,FILE *out);
 1587 void system_CGI(DGC *ctx,PCStr(conninfo),PCStr(oreq),PCStr(req),PVStr(head),PCStr(cgi),FILE *in,FILE *out)
 1588 {   const char *ev[128]; /**/
 1589     CStr(eb,0x10000);
 1590     FILE *tmp;
 1591     const char *xhead;
 1592     const char *dp;
 1593     const char *url;
 1594     const char *ourl;
 1595     HttpRequest reqx,oreqx;
 1596     const char *const *oenv;
 1597     StrVec Env;
 1598 
 1599     xhead = stralloc(head);
 1600     decomp_http_request(oreq,&oreqx);
 1601     ourl = oreqx.hq_url;
 1602     decomp_http_request(req,&reqx);
 1603     url = reqx.hq_url;
 1604     SVinit(&Env,"sysgem_CGI",ev,elnumof(ev)-1,AVStr(eb),sizeof(eb));
 1605     cgi_makeEnv(conninfo,req,xhead,"",ourl,url,"","",0,NULL,&Env);
 1606 
 1607     oenv = (char const*const*)environ;
 1608     environ = (char**)ev;
 1609 
 1610     tmp = TMPFILE("CFI-system_CGI");
 1611     System(ctx,cgi,in,tmp);
 1612     environ = (char**)oenv;
 1613     fseek(tmp,0,0);
 1614     if( dp = strstr(head,"\r\n\r\n") )
 1615         ((char*)dp)[2] = 0;
 1616     if( dp = strstr(head,"\n\n") )
 1617         ((char*)dp)[1] = 0;
 1618     cgi_response(NULL,req,AVStr(head),tmp,out,NULL,NULL);
 1619     fclose(tmp);
 1620 
 1621     free((char*)xhead);
 1622 }