Annotation of sbbs/src/sbbs3/websrvr.c, revision 1.1

1.1     ! root        1: /* websrvr.c */
        !             2: 
        !             3: /* Synchronet Web Server */
        !             4: 
        !             5: /* $Id: websrvr.c,v 1.458 2006/12/29 09:21:35 rswindell Exp $ */
        !             6: 
        !             7: /****************************************************************************
        !             8:  * @format.tab-size 4          (Plain Text/Source Code File Header)                    *
        !             9:  * @format.use-tabs true       (see http://www.synchro.net/ptsc_hdr.html)              *
        !            10:  *                                                                                                                                                     *
        !            11:  * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html         *
        !            12:  *                                                                                                                                                     *
        !            13:  * This program is free software; you can redistribute it and/or                       *
        !            14:  * modify it under the terms of the GNU General Public License                         *
        !            15:  * as published by the Free Software Foundation; either version 2                      *
        !            16:  * of the License, or (at your option) any later version.                                      *
        !            17:  * See the GNU General Public License for more details: gpl.txt or                     *
        !            18:  * http://www.fsf.org/copyleft/gpl.html                                                                                *
        !            19:  *                                                                                                                                                     *
        !            20:  * Anonymous FTP access to the most recent released source is available at     *
        !            21:  * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net     *
        !            22:  *                                                                                                                                                     *
        !            23:  * Anonymous CVS access to the development source and modification history     *
        !            24:  * is available at cvs.synchro.net:/cvsroot/sbbs, example:                                     *
        !            25:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs login                       *
        !            26:  *     (just hit return, no password is necessary)                                                     *
        !            27:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src                *
        !            28:  *                                                                                                                                                     *
        !            29:  * For Synchronet coding style and modification guidelines, see                                *
        !            30:  * http://www.synchro.net/source.html                                                                          *
        !            31:  *                                                                                                                                                     *
        !            32:  * You are encouraged to submit any modifications (preferably in Unix diff     *
        !            33:  * format) via e-mail to [email protected]                                                                      *
        !            34:  *                                                                                                                                                     *
        !            35:  * Note: If this box doesn't appear square, then you need to fix your tabs.    *
        !            36:  ****************************************************************************/
        !            37: 
        !            38: /*
        !            39:  * General notes: (ToDo stuff)
        !            40:  *
        !            41:  * Should support RFC2617 Digest auth.
        !            42:  *
        !            43:  * Support the ident protocol... the standard log format supports it.
        !            44:  *
        !            45:  * Add in support to pass connections through to a different webserver...
        !            46:  *      probobly in access.ars... with like a simplified mod_rewrite.
        !            47:  *      This would allow people to run apache and Synchronet as the same site.
        !            48:  */
        !            49: 
        !            50: /* Headers for CGI stuff */
        !            51: #if defined(__unix__)
        !            52:        #include <sys/wait.h>           /* waitpid() */
        !            53:        #include <sys/types.h>
        !            54:        #include <signal.h>                     /* kill() */
        !            55: #endif
        !            56: 
        !            57: #ifndef JAVASCRIPT
        !            58: #define JAVASCRIPT
        !            59: #endif
        !            60: 
        !            61: #undef SBBS    /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
        !            62: #include "sbbs.h"
        !            63: #include "sockwrap.h"          /* sendfilesocket() */
        !            64: #include "threadwrap.h"
        !            65: #include "semwrap.h"
        !            66: #include "websrvr.h"
        !            67: #include "base64.h"
        !            68: 
        !            69: static const char*     server_name="Synchronet Web Server";
        !            70: static const char*     newline="\r\n";
        !            71: static const char*     http_scheme="http://";
        !            72: static const size_t    http_scheme_len=7;
        !            73: static const char*     error_301="301 Moved Permanently";
        !            74: static const char*     error_302="302 Moved Temporarily";
        !            75: static const char*     error_404="404 Not Found";
        !            76: static const char*     error_416="416 Requested Range Not Satisfiable";
        !            77: static const char*     error_500="500 Internal Server Error";
        !            78: static const char*     unknown="<unknown>";
        !            79: 
        !            80: #define TIMEOUT_THREAD_WAIT            60              /* Seconds */
        !            81: #define MAX_REQUEST_LINE               1024    /* NOT including terminator */
        !            82: #define MAX_HEADERS_SIZE               16384   /* Maximum total size of all headers 
        !            83:                                                                                   (Including terminator )*/
        !            84: #define MAX_REDIR_LOOPS                        20              /* Max. times to follow internal redirects for a single request */
        !            85: #define MAX_POST_LEN                   1048576 /* Max size of body for POSTS */
        !            86: #define        OUTBUF_LEN                              20480   /* Size of output thread ring buffer */
        !            87: 
        !            88: enum {
        !            89:         CLEANUP_SSJS_TMP_FILE
        !            90:        ,CLEANUP_POST_DATA
        !            91:        ,MAX_CLEANUPS
        !            92: };
        !            93: 
        !            94: static scfg_t  scfg;
        !            95: static BOOL            scfg_reloaded=TRUE;
        !            96: static BOOL            http_logging_thread_running=FALSE;
        !            97: static ulong   active_clients=0;
        !            98: static ulong   sockets=0;
        !            99: static BOOL            terminate_server=FALSE;
        !           100: static BOOL            terminate_http_logging_thread=FALSE;
        !           101: static uint            thread_count=0;
        !           102: static SOCKET  server_socket=INVALID_SOCKET;
        !           103: static char            revision[16];
        !           104: static char            root_dir[MAX_PATH+1];
        !           105: static char            error_dir[MAX_PATH+1];
        !           106: static char            temp_dir[MAX_PATH+1];
        !           107: static char            cgi_dir[MAX_PATH+1];
        !           108: static char            cgi_env_ini[MAX_PATH+1];
        !           109: static time_t  uptime=0;
        !           110: static DWORD   served=0;
        !           111: static web_startup_t* startup=NULL;
        !           112: static js_server_props_t js_server_props;
        !           113: static str_list_t recycle_semfiles;
        !           114: static str_list_t shutdown_semfiles;
        !           115: static int session_threads=0;
        !           116: 
        !           117: static named_string_t** mime_types;
        !           118: static named_string_t** cgi_handlers;
        !           119: static named_string_t** xjs_handlers;
        !           120: 
        !           121: /* Logging stuff */
        !           122: link_list_t    log_list;
        !           123: struct log_data {
        !           124:        char    *hostname;
        !           125:        char    *ident;
        !           126:        char    *user;
        !           127:        char    *request;
        !           128:        char    *referrer;
        !           129:        char    *agent;
        !           130:        char    *vhost;
        !           131:        int             status;
        !           132:        unsigned int    size;
        !           133:        struct tm completed;
        !           134: };
        !           135: 
        !           136: typedef struct  {
        !           137:        int                     method;
        !           138:        char            virtual_path[MAX_PATH+1];
        !           139:        char            physical_path[MAX_PATH+1];
        !           140:        BOOL            expect_go_ahead;
        !           141:        time_t          if_modified_since;
        !           142:        BOOL            keep_alive;
        !           143:        char            ars[256];
        !           144:        char            auth[128];                              /* UserID:Password */
        !           145:        char            host[128];                              /* The requested host. (as used for self-referencing URLs) */
        !           146:        char            vhost[128];                             /* The requested host. (virtual host) */
        !           147:        int                     send_location;
        !           148:        const char*     mime_type;
        !           149:        str_list_t      headers;
        !           150:        char            status[MAX_REQUEST_LINE+1];
        !           151:        char *          post_data;
        !           152:        size_t          post_len;
        !           153:        int                     dynamic;
        !           154:        char            xjs_handler[MAX_PATH+1];
        !           155:        struct log_data *ld;
        !           156:        char            request_line[MAX_REQUEST_LINE+1];
        !           157:        BOOL            finished;                               /* Done processing request. */
        !           158:        BOOL            read_chunked;
        !           159:        BOOL            write_chunked;
        !           160:        long            range_start;
        !           161:        long            range_end;
        !           162:        BOOL            accept_ranges;
        !           163:        time_t          if_range;
        !           164:        BOOL            path_info_index;
        !           165: 
        !           166:        /* CGI parameters */
        !           167:        char            query_str[MAX_REQUEST_LINE+1];
        !           168:        char            extra_path_info[MAX_REQUEST_LINE+1];
        !           169:        str_list_t      cgi_env;
        !           170:        str_list_t      dynamic_heads;
        !           171: 
        !           172:        /* Dynamically (sever-side JS) generated HTML parameters */
        !           173:        FILE*   fp;
        !           174:        char            *cleanup_file[MAX_CLEANUPS];
        !           175:        BOOL    sent_headers;
        !           176:        BOOL    prev_write;
        !           177: 
        !           178:        /* webconfig.ini overrides */
        !           179:        char    *error_dir;
        !           180:        char    *cgi_dir;
        !           181:        char    *realm;
        !           182: } http_request_t;
        !           183: 
        !           184: typedef struct  {
        !           185:        SOCKET                  socket;
        !           186:        SOCKADDR_IN             addr;
        !           187:        http_request_t  req;
        !           188:        char                    host_ip[64];
        !           189:        char                    host_name[128]; /* Resolved remote host */
        !           190:        int                             http_ver;       /* HTTP version.  0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
        !           191:        BOOL                    finished;               /* Do not accept any more imput from client */
        !           192:        user_t                  user;
        !           193:        int                             last_user_num;
        !           194:        time_t                  logon_time;
        !           195:        char                    username[LEN_NAME+1];
        !           196:        int                             last_js_user_num;
        !           197: 
        !           198:        /* JavaScript parameters */
        !           199:        JSRuntime*              js_runtime;
        !           200:        JSContext*              js_cx;
        !           201:        JSObject*               js_glob;
        !           202:        JSObject*               js_query;
        !           203:        JSObject*               js_header;
        !           204:        JSObject*               js_cookie;
        !           205:        JSObject*               js_request;
        !           206:        js_branch_t             js_branch;
        !           207:        subscan_t               *subscan;
        !           208: 
        !           209:        /* Ring Buffer Stuff */
        !           210:        RingBuf                 outbuf;
        !           211:        sem_t                   output_thread_terminated;
        !           212:        int                             outbuf_write_initialized;
        !           213:        pthread_mutex_t outbuf_write;
        !           214: 
        !           215:        /* Client info */
        !           216:        client_t                client;
        !           217: 
        !           218:        /* Synchronization stuff */
        !           219:        pthread_mutex_t struct_filled;
        !           220: } http_session_t;
        !           221: 
        !           222: enum { 
        !           223:         HTTP_0_9
        !           224:        ,HTTP_1_0
        !           225:        ,HTTP_1_1
        !           226: };
        !           227: static char* http_vers[] = {
        !           228:         ""
        !           229:        ,"HTTP/1.0"
        !           230:        ,"HTTP/1.1"
        !           231:        ,NULL   /* terminator */
        !           232: };
        !           233: 
        !           234: enum { 
        !           235:         HTTP_HEAD
        !           236:        ,HTTP_GET
        !           237:        ,HTTP_POST
        !           238:        ,HTTP_OPTIONS
        !           239: };
        !           240: 
        !           241: static char* methods[] = {
        !           242:         "HEAD"
        !           243:        ,"GET"
        !           244:        ,"POST"
        !           245:        ,"OPTIONS"
        !           246:        ,NULL   /* terminator */
        !           247: };
        !           248: 
        !           249: enum {
        !           250:         IS_STATIC
        !           251:        ,IS_CGI
        !           252:        ,IS_JS
        !           253:        ,IS_SSJS
        !           254: };
        !           255: 
        !           256: enum { 
        !           257:         HEAD_DATE
        !           258:        ,HEAD_HOST
        !           259:        ,HEAD_IFMODIFIED
        !           260:        ,HEAD_LENGTH
        !           261:        ,HEAD_TYPE
        !           262:        ,HEAD_AUTH
        !           263:        ,HEAD_CONNECTION
        !           264:        ,HEAD_WWWAUTH
        !           265:        ,HEAD_STATUS
        !           266:        ,HEAD_ALLOW
        !           267:        ,HEAD_EXPIRES
        !           268:        ,HEAD_LASTMODIFIED
        !           269:        ,HEAD_LOCATION
        !           270:        ,HEAD_PRAGMA
        !           271:        ,HEAD_SERVER
        !           272:        ,HEAD_REFERER
        !           273:        ,HEAD_AGENT
        !           274:        ,HEAD_TRANSFER_ENCODING
        !           275:        ,HEAD_ACCEPT_RANGES
        !           276:        ,HEAD_CONTENT_RANGE
        !           277:        ,HEAD_RANGE
        !           278:        ,HEAD_IFRANGE
        !           279:        ,HEAD_COOKIE
        !           280: };
        !           281: 
        !           282: static struct {
        !           283:        int             id;
        !           284:        char*   text;
        !           285: } headers[] = {
        !           286:        { HEAD_DATE,                    "Date"                                  },
        !           287:        { HEAD_HOST,                    "Host"                                  },
        !           288:        { HEAD_IFMODIFIED,              "If-Modified-Since"             },
        !           289:        { HEAD_LENGTH,                  "Content-Length"                },
        !           290:        { HEAD_TYPE,                    "Content-Type"                  },
        !           291:        { HEAD_AUTH,                    "Authorization"                 },
        !           292:        { HEAD_CONNECTION,              "Connection"                    },
        !           293:        { HEAD_WWWAUTH,                 "WWW-Authenticate"              },
        !           294:        { HEAD_STATUS,                  "Status"                                },
        !           295:        { HEAD_ALLOW,                   "Allow"                                 },
        !           296:        { HEAD_EXPIRES,                 "Expires"                               },
        !           297:        { HEAD_LASTMODIFIED,    "Last-Modified"                 },
        !           298:        { HEAD_LOCATION,                "Location"                              },
        !           299:        { HEAD_PRAGMA,                  "Pragma"                                },
        !           300:        { HEAD_SERVER,                  "Server"                                },
        !           301:        { HEAD_REFERER,                 "Referer"                               },
        !           302:        { HEAD_AGENT,                   "User-Agent"                    },
        !           303:        { HEAD_TRANSFER_ENCODING,                       "Transfer-Encoding"                     },
        !           304:        { HEAD_ACCEPT_RANGES,   "Accept-Ranges"                 },
        !           305:        { HEAD_CONTENT_RANGE,   "Content-Range"                 },
        !           306:        { HEAD_RANGE,                   "Range"                                 },
        !           307:        { HEAD_IFRANGE,                 "If-Range"                              },
        !           308:        { HEAD_COOKIE,                  "Cookie"                                },
        !           309:        { -1,                                   NULL /* terminator */   },
        !           310: };
        !           311: 
        !           312: /* Everything MOVED_TEMP and everything after is a magical internal redirect */
        !           313: enum  {
        !           314:         NO_LOCATION
        !           315:        ,MOVED_PERM
        !           316:        ,MOVED_TEMP
        !           317:        ,MOVED_STAT
        !           318: };
        !           319: 
        !           320: static char    *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
        !           321: static char    *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
        !           322: 
        !           323: static void respond(http_session_t * session);
        !           324: static BOOL js_setup(http_session_t* session);
        !           325: static char *find_last_slash(char *str);
        !           326: static BOOL check_extra_path(http_session_t * session);
        !           327: static BOOL exec_ssjs(http_session_t* session, char* script);
        !           328: static BOOL ssjs_send_headers(http_session_t* session, int chunked);
        !           329: 
        !           330: static time_t
        !           331: sub_mkgmt(struct tm *tm)
        !           332: {
        !           333:         int y, nleapdays;
        !           334:         time_t t;
        !           335:         /* days before the month */
        !           336:         static const unsigned short moff[12] = {
        !           337:                 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
        !           338:         };
        !           339: 
        !           340:         /*
        !           341:          * XXX: This code assumes the given time to be normalized.
        !           342:          * Normalizing here is impossible in case the given time is a leap
        !           343:          * second but the local time library is ignorant of leap seconds.
        !           344:          */
        !           345: 
        !           346:         /* minimal sanity checking not to access outside of the array */
        !           347:         if ((unsigned) tm->tm_mon >= 12)
        !           348:                 return (time_t) -1;
        !           349:         if (tm->tm_year < 1970 - 1900)
        !           350:                 return (time_t) -1;
        !           351: 
        !           352:         y = tm->tm_year + 1900 - (tm->tm_mon < 2);
        !           353:         nleapdays = y / 4 - y / 100 + y / 400 -
        !           354:             ((1970-1) / 4 - (1970-1) / 100 + (1970-1) / 400);
        !           355:         t = ((((time_t) (tm->tm_year - (1970 - 1900)) * 365 +
        !           356:                         moff[tm->tm_mon] + tm->tm_mday - 1 + nleapdays) * 24 +
        !           357:                 tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
        !           358: 
        !           359:         return (t < 0 ? (time_t) -1 : t);
        !           360: }
        !           361: 
        !           362: time_t
        !           363: time_gm(struct tm *tm)
        !           364: {
        !           365:         time_t t, t2;
        !           366:         struct tm *tm2;
        !           367:         int sec;
        !           368: 
        !           369:         /* Do the first guess. */
        !           370:         if ((t = sub_mkgmt(tm)) == (time_t) -1)
        !           371:                 return (time_t) -1;
        !           372: 
        !           373:         /* save value in case *tm is overwritten by gmtime() */
        !           374:         sec = tm->tm_sec;
        !           375: 
        !           376:         tm2 = gmtime(&t);
        !           377:         if ((t2 = sub_mkgmt(tm2)) == (time_t) -1)
        !           378:                 return (time_t) -1;
        !           379: 
        !           380:         if (t2 < t || tm2->tm_sec != sec) {
        !           381:                 /*
        !           382:                  * Adjust for leap seconds.
        !           383:                  *
        !           384:                  *     real time_t time
        !           385:                  *           |
        !           386:                  *          tm
        !           387:                  *         /        ... (a) first sub_mkgmt() conversion
        !           388:                  *       t
        !           389:                  *       |
        !           390:                  *      tm2
        !           391:                  *     /        ... (b) second sub_mkgmt() conversion
        !           392:                  *   t2
        !           393:                  *                        --->time
        !           394:                  */
        !           395:                 /*
        !           396:                  * Do the second guess, assuming (a) and (b) are almost equal.
        !           397:                  */
        !           398:                 t += t - t2;
        !           399:                 tm2 = gmtime(&t);
        !           400: 
        !           401:                 /*
        !           402:                  * Either (a) or (b), may include one or two extra
        !           403:                  * leap seconds.  Try t, t + 2, t - 2, t + 1, and t - 1.
        !           404:                  */
        !           405:                 if (tm2->tm_sec == sec
        !           406:                     || (t += 2, tm2 = gmtime(&t), tm2->tm_sec == sec)
        !           407:                     || (t -= 4, tm2 = gmtime(&t), tm2->tm_sec == sec)
        !           408:                     || (t += 3, tm2 = gmtime(&t), tm2->tm_sec == sec)
        !           409:                     || (t -= 2, tm2 = gmtime(&t), tm2->tm_sec == sec))
        !           410:                         ;        /* found */
        !           411:                 else {
        !           412:                         /*
        !           413:                          * Not found.
        !           414:                          */
        !           415:                         if (sec >= 60)
        !           416:                                 /*
        !           417:                                  * The given time is a leap second
        !           418:                                  * (sec 60 or 61), but the time library
        !           419:                                  * is ignorant of the leap second.
        !           420:                                  */
        !           421:                                 ;        /* treat sec 60 as 59,
        !           422:                                            sec 61 as 0 of the next minute */
        !           423:                         else
        !           424:                                 /* The given time may not be normalized. */
        !           425:                                 t++;        /* restore t */
        !           426:                 }
        !           427:         }
        !           428: 
        !           429:         return (t < 0 ? (time_t) -1 : t);
        !           430: }
        !           431: 
        !           432: static int lprintf(int level, char *fmt, ...)
        !           433: {
        !           434:        va_list argptr;
        !           435:        char sbuf[1024];
        !           436: 
        !           437:     if(startup==NULL || startup->lputs==NULL)
        !           438:         return(0);
        !           439: 
        !           440:        va_start(argptr,fmt);
        !           441:     vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
        !           442:        sbuf[sizeof(sbuf)-1]=0;
        !           443:     va_end(argptr);
        !           444:     return(startup->lputs(startup->cbdata,level,sbuf));
        !           445: }
        !           446: 
        !           447: static int writebuf(http_session_t     *session, const char *buf, size_t len)
        !           448: {
        !           449:        size_t  sent=0;
        !           450:        size_t  avail;
        !           451: 
        !           452:        while(sent < len) {
        !           453:                avail=RingBufFree(&session->outbuf);
        !           454:                if(!avail) {
        !           455:                        SLEEP(1);
        !           456:                        continue;
        !           457:                }
        !           458:                if(avail > len-sent)
        !           459:                        avail=len-sent;
        !           460:                sent+=RingBufWrite(&(session->outbuf), ((char *)buf)+sent, avail);
        !           461:        }
        !           462:        return(sent);
        !           463: }
        !           464: 
        !           465: static int sock_sendbuf(SOCKET *sock, const char *buf, size_t len, BOOL *failed)
        !           466: {
        !           467:        size_t sent=0;
        !           468:        int result;
        !           469:        int sel;
        !           470:        fd_set  wr_set;
        !           471:        struct timeval tv;
        !           472: 
        !           473:        while(sent<len && *sock!=INVALID_SOCKET) {
        !           474:                FD_ZERO(&wr_set);
        !           475:                FD_SET(*sock,&wr_set);
        !           476:                /* Convert timeout from ms to sec/usec */
        !           477:                tv.tv_sec=startup->max_inactivity;
        !           478:                tv.tv_usec=0;
        !           479:                sel=select(*sock+1,NULL,&wr_set,NULL,&tv);
        !           480:                switch(sel) {
        !           481:                        case 1:
        !           482:                                result=sendsocket(*sock,buf+sent,len-sent);
        !           483:                                if(result==SOCKET_ERROR) {
        !           484:                                        if(ERROR_VALUE==ECONNRESET) 
        !           485:                                                lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",*sock);
        !           486:                                        else if(ERROR_VALUE==ECONNABORTED) 
        !           487:                                                lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",*sock);
        !           488:                                        else
        !           489:                                                lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",*sock,ERROR_VALUE);
        !           490:                                        if(failed)
        !           491:                                                *failed=TRUE;
        !           492:                                        return(sent);
        !           493:                                }
        !           494:                                break;
        !           495:                        case 0:
        !           496:                                lprintf(LOG_WARNING,"%04d Timeout selecting socket for write",*sock);
        !           497:                                if(failed)
        !           498:                                        *failed=TRUE;
        !           499:                                return(sent);
        !           500:                        case -1:
        !           501:                                lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for write",*sock,ERROR_VALUE);
        !           502:                                if(failed)
        !           503:                                        *failed=TRUE;
        !           504:                                return(sent);
        !           505:                }
        !           506:                sent+=result;
        !           507:        }
        !           508:        if(failed && sent<len)
        !           509:                *failed=TRUE;
        !           510:        return(sent);
        !           511: }
        !           512: 
        !           513: #ifdef _WINSOCKAPI_
        !           514: 
        !           515: static WSADATA WSAData;
        !           516: #define SOCKLIB_DESC WSAData.szDescription
        !           517: static BOOL WSAInitialized=FALSE;
        !           518: 
        !           519: static BOOL winsock_startup(void)
        !           520: {
        !           521:        int             status;             /* Status Code */
        !           522: 
        !           523:     if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
        !           524:                lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
        !           525:                WSAInitialized=TRUE;
        !           526:                return (TRUE);
        !           527:        }
        !           528: 
        !           529:     lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
        !           530:        return (FALSE);
        !           531: }
        !           532: 
        !           533: #else /* No WINSOCK */
        !           534: 
        !           535: #define winsock_startup()      (TRUE)
        !           536: #define SOCKLIB_DESC NULL
        !           537: 
        !           538: #endif
        !           539: 
        !           540: static void status(char* str)
        !           541: {
        !           542:        if(startup!=NULL && startup->status!=NULL)
        !           543:            startup->status(startup->cbdata,str);
        !           544: }
        !           545: 
        !           546: static void update_clients(void)
        !           547: {
        !           548:        if(startup!=NULL && startup->clients!=NULL)
        !           549:                startup->clients(startup->cbdata,active_clients);
        !           550: }
        !           551: 
        !           552: static void client_on(SOCKET sock, client_t* client, BOOL update)
        !           553: {
        !           554:        if(startup!=NULL && startup->client_on!=NULL)
        !           555:                startup->client_on(startup->cbdata,TRUE,sock,client,update);
        !           556: }
        !           557: 
        !           558: static void client_off(SOCKET sock)
        !           559: {
        !           560:        if(startup!=NULL && startup->client_on!=NULL)
        !           561:                startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
        !           562: }
        !           563: 
        !           564: static void thread_up(BOOL setuid)
        !           565: {
        !           566:        thread_count++;
        !           567:        if(startup!=NULL && startup->thread_up!=NULL)
        !           568:                startup->thread_up(startup->cbdata,TRUE, setuid);
        !           569: }
        !           570: 
        !           571: static void thread_down(void)
        !           572: {
        !           573:        if(thread_count>0)
        !           574:                thread_count--;
        !           575:        if(startup!=NULL && startup->thread_up!=NULL)
        !           576:                startup->thread_up(startup->cbdata,FALSE, FALSE);
        !           577: }
        !           578: 
        !           579: /*********************************************************************/
        !           580: /* Adds an environment variable to the sessions  cgi_env linked list */
        !           581: /*********************************************************************/
        !           582: static void add_env(http_session_t *session, const char *name,const char *value)  {
        !           583:        char    newname[129];
        !           584:        char    *p;
        !           585: 
        !           586:        if(name==NULL || value==NULL)  {
        !           587:                lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
        !           588:                return;
        !           589:        }
        !           590:        SAFECOPY(newname,name);
        !           591: 
        !           592:        for(p=newname;*p;p++)  {
        !           593:                *p=toupper(*p);
        !           594:                if(*p=='-')
        !           595:                        *p='_';
        !           596:        }
        !           597:        p=(char *)alloca(strlen(name)+strlen(value)+2);
        !           598:        if(p==NULL) {
        !           599:                lprintf(LOG_WARNING,"%04d Cannot allocate memory for string", session->socket);
        !           600:                return;
        !           601:        }
        !           602: #if 0  /* this is way too verbose for every request */
        !           603:        lprintf(LOG_DEBUG,"%04d Adding CGI environment variable %s=%s",session->socket,newname,value);
        !           604: #endif
        !           605:        sprintf(p,"%s=%s",newname,value);
        !           606:        strListPush(&session->req.cgi_env,p);
        !           607: }
        !           608: 
        !           609: /***************************************/
        !           610: /* Initializes default CGI envirnoment */
        !           611: /***************************************/
        !           612: static void init_enviro(http_session_t *session)  {
        !           613:        char    str[128];
        !           614: 
        !           615:        add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
        !           616:        sprintf(str,"%d",startup->port);
        !           617:        add_env(session,"SERVER_PORT",str);
        !           618:        add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
        !           619:        if(!strcmp(session->host_name,session->host_ip))
        !           620:                add_env(session,"REMOTE_HOST",session->host_name);
        !           621:        add_env(session,"REMOTE_ADDR",session->host_ip);
        !           622:        add_env(session,"REQUEST_URI",session->req.request_line);
        !           623: }
        !           624: 
        !           625: /*
        !           626:  * Sends string str to socket sock... returns number of bytes written, or 0 on an error
        !           627:  * Can not close the socket since it can not set it to INVALID_SOCKET
        !           628:  */
        !           629: static int bufprint(http_session_t *session, const char *str)
        !           630: {
        !           631:        int len;
        !           632: 
        !           633:        len=strlen(str);
        !           634:        return(writebuf(session,str,len));
        !           635: }
        !           636: 
        !           637: /**********************************************************/
        !           638: /* Converts a month name/abbr to the 0-based month number */
        !           639: /* ToDo: This probobly exists somewhere else already     */
        !           640: /**********************************************************/
        !           641: static int getmonth(char *mon)
        !           642: {
        !           643:        int     i;
        !           644:        for(i=0;i<12;i++)
        !           645:                if(!stricmp(mon,months[i]))
        !           646:                        return(i);
        !           647: 
        !           648:        return 0;
        !           649: }
        !           650: 
        !           651: /*******************************************************************/
        !           652: /* Converts a date string in any of the common formats to a time_t */
        !           653: /*******************************************************************/
        !           654: static time_t decode_date(char *date)
        !           655: {
        !           656:        struct  tm      ti;
        !           657:        char    *token;
        !           658:        char    *last;
        !           659:        time_t  t;
        !           660: 
        !           661:        ti.tm_sec=0;            /* seconds (0 - 60) */
        !           662:        ti.tm_min=0;            /* minutes (0 - 59) */
        !           663:        ti.tm_hour=0;           /* hours (0 - 23) */
        !           664:        ti.tm_mday=1;           /* day of month (1 - 31) */
        !           665:        ti.tm_mon=0;            /* month of year (0 - 11) */
        !           666:        ti.tm_year=0;           /* year - 1900 */
        !           667:        ti.tm_isdst=0;          /* is summer time in effect? */
        !           668: 
        !           669:        token=strtok_r(date,",",&last);
        !           670:        if(token==NULL)
        !           671:                return(0);
        !           672:        /* This probobly only needs to be 9, but the extra one is for luck. */
        !           673:        if(strlen(date)>15) {
        !           674:                /* asctime() */
        !           675:                /* Toss away week day */
        !           676:                token=strtok_r(date," ",&last);
        !           677:                if(token==NULL)
        !           678:                        return(0);
        !           679:                token=strtok_r(NULL," ",&last);
        !           680:                if(token==NULL)
        !           681:                        return(0);
        !           682:                ti.tm_mon=getmonth(token);
        !           683:                token=strtok_r(NULL," ",&last);
        !           684:                if(token==NULL)
        !           685:                        return(0);
        !           686:                ti.tm_mday=atoi(token);
        !           687:                token=strtok_r(NULL,":",&last);
        !           688:                if(token==NULL)
        !           689:                        return(0);
        !           690:                ti.tm_hour=atoi(token);
        !           691:                token=strtok_r(NULL,":",&last);
        !           692:                if(token==NULL)
        !           693:                        return(0);
        !           694:                ti.tm_min=atoi(token);
        !           695:                token=strtok_r(NULL," ",&last);
        !           696:                if(token==NULL)
        !           697:                        return(0);
        !           698:                ti.tm_sec=atoi(token);
        !           699:                token=strtok_r(NULL,"",&last);
        !           700:                if(token==NULL)
        !           701:                        return(0);
        !           702:                ti.tm_year=atoi(token)-1900;
        !           703:        }
        !           704:        else  {
        !           705:                /* RFC 1123 or RFC 850 */
        !           706:                token=strtok_r(NULL," -",&last);
        !           707:                if(token==NULL)
        !           708:                        return(0);
        !           709:                ti.tm_mday=atoi(token);
        !           710:                token=strtok_r(NULL," -",&last);
        !           711:                if(token==NULL)
        !           712:                        return(0);
        !           713:                ti.tm_mon=getmonth(token);
        !           714:                token=strtok_r(NULL," ",&last);
        !           715:                if(token==NULL)
        !           716:                        return(0);
        !           717:                ti.tm_year=atoi(token);
        !           718:                token=strtok_r(NULL,":",&last);
        !           719:                if(token==NULL)
        !           720:                        return(0);
        !           721:                ti.tm_hour=atoi(token);
        !           722:                token=strtok_r(NULL,":",&last);
        !           723:                if(token==NULL)
        !           724:                        return(0);
        !           725:                ti.tm_min=atoi(token);
        !           726:                token=strtok_r(NULL," ",&last);
        !           727:                if(token==NULL)
        !           728:                        return(0);
        !           729:                ti.tm_sec=atoi(token);
        !           730:                if(ti.tm_year>1900)
        !           731:                        ti.tm_year -= 1900;
        !           732:        }
        !           733: 
        !           734:        t=time_gm(&ti);
        !           735:        return(t);
        !           736: }
        !           737: 
        !           738: static SOCKET open_socket(int type)
        !           739: {
        !           740:        char    error[256];
        !           741:        SOCKET  sock;
        !           742: 
        !           743:        sock=socket(AF_INET, type, IPPROTO_IP);
        !           744:        if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
        !           745:                startup->socket_open(startup->cbdata,TRUE);
        !           746:        if(sock!=INVALID_SOCKET) {
        !           747:                if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error)))
        !           748:                        lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
        !           749: 
        !           750:                sockets++;
        !           751:        }
        !           752:        return(sock);
        !           753: }
        !           754: 
        !           755: static int close_socket(SOCKET *sock)
        !           756: {
        !           757:        int             result;
        !           758: 
        !           759:        if(sock==NULL || *sock==INVALID_SOCKET)
        !           760:                return(-1);
        !           761: 
        !           762:        /* required to ensure all data is send when SO_LINGER is off (Not functional on Win32) */
        !           763:        shutdown(*sock,SHUT_RDWR);
        !           764:        result=closesocket(*sock);
        !           765:        *sock=INVALID_SOCKET;
        !           766:        if(startup!=NULL && startup->socket_open!=NULL) {
        !           767:                startup->socket_open(startup->cbdata,FALSE);
        !           768:        }
        !           769:        sockets--;
        !           770:        if(result!=0) {
        !           771:                if(ERROR_VALUE!=ENOTSOCK)
        !           772:                        lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",*sock, ERROR_VALUE);
        !           773:        }
        !           774: 
        !           775:        return(result);
        !           776: }
        !           777: 
        !           778: /* Waits for the outbuf to drain */
        !           779: static void drain_outbuf(http_session_t * session)
        !           780: {
        !           781:        if(session->socket==INVALID_SOCKET)
        !           782:                return;
        !           783:        /* Force the output thread to go NOW */
        !           784:        sem_post(&(session->outbuf.highwater_sem));
        !           785:        /* ToDo: This should probobly timeout eventually... */
        !           786:        while(RingBufFull(&session->outbuf) && session->socket!=INVALID_SOCKET)
        !           787:                SLEEP(1);
        !           788:        /* Lock the mutex to ensure data has been sent */
        !           789:        while(session->socket!=INVALID_SOCKET && !session->outbuf_write_initialized)
        !           790:                SLEEP(1);
        !           791:        if(session->socket==INVALID_SOCKET)
        !           792:                return;
        !           793:        pthread_mutex_lock(&session->outbuf_write);             /* Win32 Access violation here on Jan-11-2006 - shutting down webserver while in use */
        !           794:        pthread_mutex_unlock(&session->outbuf_write);
        !           795: }
        !           796: 
        !           797: /**************************************************/
        !           798: /* End of a single request...                                    */
        !           799: /* This is called at the end of EVERY request    */
        !           800: /*  Log the request                                                      */
        !           801: /*  Free request-specific data ie: dynamic stuff  */
        !           802: /*  Close socket unless it's being kept alive     */
        !           803: /*   If the socket is closed, the session is done */
        !           804: /**************************************************/
        !           805: static void close_request(http_session_t * session)
        !           806: {
        !           807:        time_t          now;
        !           808:        int                     i;
        !           809: 
        !           810:        if(session->req.write_chunked) {
        !           811:                drain_outbuf(session);
        !           812:                session->req.write_chunked=0;
        !           813:                writebuf(session,"0\r\n",3);
        !           814:                if(session->req.dynamic==IS_SSJS)
        !           815:                        ssjs_send_headers(session,FALSE);
        !           816:                else
        !           817:                        /* Non-ssjs isn't capable of generating headers during execution */
        !           818:                        writebuf(session, newline, 2);
        !           819:        }
        !           820: 
        !           821:        /* Force the output thread to go NOW */
        !           822:        sem_post(&(session->outbuf.highwater_sem));
        !           823: 
        !           824:        if(session->req.ld!=NULL) {
        !           825:                now=time(NULL);
        !           826:                localtime_r(&now,&session->req.ld->completed);
        !           827:                listPushNode(&log_list,session->req.ld);
        !           828:                session->req.ld=NULL;
        !           829:        }
        !           830: 
        !           831:        strListFree(&session->req.headers);
        !           832:        strListFree(&session->req.dynamic_heads);
        !           833:        strListFree(&session->req.cgi_env);
        !           834:        FREE_AND_NULL(session->req.post_data);
        !           835:        FREE_AND_NULL(session->req.error_dir);
        !           836:        FREE_AND_NULL(session->req.cgi_dir);
        !           837:        FREE_AND_NULL(session->req.realm);
        !           838:        /*
        !           839:         * This causes all active http_session_threads to terminate.
        !           840:         */
        !           841:        if((!session->req.keep_alive) || terminate_server) {
        !           842:                drain_outbuf(session);
        !           843:                close_socket(&session->socket);
        !           844:        }
        !           845:        if(session->socket==INVALID_SOCKET)
        !           846:                session->finished=TRUE;
        !           847: 
        !           848:        if(session->js_cx!=NULL && (session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)) {
        !           849:                JS_GC(session->js_cx);
        !           850:        }
        !           851:        if(session->subscan!=NULL)
        !           852:                putmsgptrs(&scfg, session->user.number, session->subscan);
        !           853: 
        !           854:        if(session->req.fp!=NULL)
        !           855:                fclose(session->req.fp);
        !           856: 
        !           857:        for(i=0;i<MAX_CLEANUPS;i++) {
        !           858:                if(session->req.cleanup_file[i]!=NULL) {
        !           859:                        if(!(startup->options&WEB_OPT_DEBUG_SSJS))
        !           860:                                remove(session->req.cleanup_file[i]);
        !           861:                        free(session->req.cleanup_file[i]);
        !           862:                }
        !           863:        }
        !           864: 
        !           865:        memset(&session->req,0,sizeof(session->req));
        !           866: }
        !           867: 
        !           868: static int get_header_type(char *header)
        !           869: {
        !           870:        int i;
        !           871:        for(i=0; headers[i].text!=NULL; i++) {
        !           872:                if(!stricmp(header,headers[i].text)) {
        !           873:                        return(headers[i].id);
        !           874:                }
        !           875:        }
        !           876:        return(-1);
        !           877: }
        !           878: 
        !           879: /* Opposite of get_header_type() */
        !           880: static char *get_header(int id) 
        !           881: {
        !           882:        int i;
        !           883:        if(headers[id].id==id)
        !           884:                return(headers[id].text);
        !           885: 
        !           886:        for(i=0;headers[i].text!=NULL;i++) {
        !           887:                if(headers[i].id==id) {
        !           888:                        return(headers[i].text);
        !           889:                }
        !           890:        }
        !           891:        return(NULL);
        !           892: }
        !           893: 
        !           894: static const char* unknown_mime_type="application/octet-stream";
        !           895: 
        !           896: static const char* get_mime_type(char *ext)
        !           897: {
        !           898:        uint i;
        !           899: 
        !           900:        if(ext==NULL || mime_types==NULL)
        !           901:                return(unknown_mime_type);
        !           902: 
        !           903:        for(i=0;mime_types[i]!=NULL;i++)
        !           904:                if(stricmp(ext+1,mime_types[i]->name)==0)
        !           905:                        return(mime_types[i]->value);
        !           906: 
        !           907:        return(unknown_mime_type);
        !           908: }
        !           909: 
        !           910: static BOOL get_cgi_handler(char* cmdline, size_t maxlen)
        !           911: {
        !           912:        char    fname[MAX_PATH+1];
        !           913:        char*   ext;
        !           914:        size_t  i;
        !           915: 
        !           916:        if(cgi_handlers==NULL || (ext=getfext(cmdline))==NULL)
        !           917:                return(FALSE);
        !           918: 
        !           919:        for(i=0;cgi_handlers[i]!=NULL;i++) {
        !           920:                if(stricmp(cgi_handlers[i]->name, ext+1)==0) {
        !           921:                        SAFECOPY(fname,cmdline);
        !           922:                        safe_snprintf(cmdline,maxlen,"%s %s",cgi_handlers[i]->value,fname);
        !           923:                        return(TRUE);
        !           924:                }
        !           925:        }
        !           926:        return(FALSE);
        !           927: }
        !           928: 
        !           929: static BOOL get_xjs_handler(char* ext, http_session_t* session)
        !           930: {
        !           931:        size_t  i;
        !           932: 
        !           933:        if(ext==NULL || xjs_handlers==NULL || ext[0]==0)
        !           934:                return(FALSE);
        !           935: 
        !           936:        for(i=0;xjs_handlers[i]!=NULL;i++) {
        !           937:                if(stricmp(xjs_handlers[i]->name, ext+1)==0) {
        !           938:                        if(getfname(xjs_handlers[i]->value)==xjs_handlers[i]->value)    /* no path specified */
        !           939:                                SAFEPRINTF2(session->req.xjs_handler,"%s%s",scfg.exec_dir,xjs_handlers[i]->value);
        !           940:                        else
        !           941:                                SAFECOPY(session->req.xjs_handler,xjs_handlers[i]->value);
        !           942:                        return(TRUE);
        !           943:                }
        !           944:        }
        !           945:        return(FALSE);
        !           946: }
        !           947: 
        !           948: /* This function appends append plus a newline IF the final dst string would have a length less than maxlen */
        !           949: static void safecat(char *dst, const char *append, size_t maxlen) {
        !           950:        size_t dstlen,appendlen;
        !           951:        dstlen=strlen(dst);
        !           952:        appendlen=strlen(append);
        !           953:        if(dstlen+appendlen+2 < maxlen) {
        !           954:                strcat(dst,append);
        !           955:                strcat(dst,newline);
        !           956:        }
        !           957: }
        !           958: 
        !           959: /*************************************************/
        !           960: /* Sends headers for the reply.                                         */
        !           961: /* HTTP/0.9 doesn't use headers, so just returns */
        !           962: /*************************************************/
        !           963: static BOOL send_headers(http_session_t *session, const char *status, int chunked)
        !           964: {
        !           965:        int             ret;
        !           966:        BOOL    send_file=TRUE;
        !           967:        time_t  ti;
        !           968:        size_t  idx;
        !           969:        const char      *status_line;
        !           970:        struct stat     stats;
        !           971:        struct tm       tm;
        !           972:        char    *headers;
        !           973:        char    header[MAX_REQUEST_LINE+1];
        !           974: 
        !           975:        if(session->socket==INVALID_SOCKET) {
        !           976:                session->req.sent_headers=TRUE;
        !           977:                return(FALSE);
        !           978:        }
        !           979:        lprintf(LOG_DEBUG,"%04d Request resolved to: %s"
        !           980:                ,session->socket,session->req.physical_path);
        !           981:        if(session->http_ver <= HTTP_0_9) {
        !           982:                session->req.sent_headers=TRUE;
        !           983:                if(session->req.ld != NULL)
        !           984:                        session->req.ld->status=atoi(status);
        !           985:                return(TRUE);
        !           986:        }
        !           987:        headers=alloca(MAX_HEADERS_SIZE);
        !           988:        if(headers==NULL)  {
        !           989:                lprintf(LOG_CRIT,"Could not allocate memory for response headers.");
        !           990:                return(FALSE);
        !           991:        }
        !           992:        *headers=0;
        !           993:        if(!session->req.sent_headers) {
        !           994:                session->req.sent_headers=TRUE;
        !           995:                status_line=status;
        !           996:                ret=stat(session->req.physical_path,&stats);
        !           997:                if(session->req.method==HTTP_OPTIONS)
        !           998:                        ret=-1;
        !           999:                if(!ret && session->req.if_modified_since && (stats.st_mtime <= session->req.if_modified_since) && !session->req.dynamic) {
        !          1000:                        status_line="304 Not Modified";
        !          1001:                        ret=-1;
        !          1002:                        send_file=FALSE;
        !          1003:                }
        !          1004:                if(!ret && session->req.if_range && (stats.st_mtime > session->req.if_range || session->req.dynamic)) {
        !          1005:                        status_line="200 OK";
        !          1006:                        session->req.range_start=0;
        !          1007:                        session->req.range_end=0;
        !          1008:                }
        !          1009:                if(session->req.send_location==MOVED_PERM)  {
        !          1010:                        status_line=error_301;
        !          1011:                        ret=-1;
        !          1012:                        send_file=FALSE;
        !          1013:                }
        !          1014:                if(session->req.send_location==MOVED_TEMP)  {
        !          1015:                        status_line=error_302;
        !          1016:                        ret=-1;
        !          1017:                        send_file=FALSE;
        !          1018:                }
        !          1019: 
        !          1020:                if(session->req.ld!=NULL)
        !          1021:                        session->req.ld->status=atoi(status_line);
        !          1022: 
        !          1023:                /* Status-Line */
        !          1024:                safe_snprintf(header,sizeof(header),"%s %s",http_vers[session->http_ver],status_line);
        !          1025: 
        !          1026:                lprintf(LOG_DEBUG,"%04d Result: %s",session->socket,header);
        !          1027: 
        !          1028:                safecat(headers,header,MAX_HEADERS_SIZE);
        !          1029: 
        !          1030:                /* General Headers */
        !          1031:                ti=time(NULL);
        !          1032:                if(gmtime_r(&ti,&tm)==NULL)
        !          1033:                        memset(&tm,0,sizeof(tm));
        !          1034:                safe_snprintf(header,sizeof(header),"%s: %s, %02d %s %04d %02d:%02d:%02d GMT"
        !          1035:                        ,get_header(HEAD_DATE)
        !          1036:                        ,days[tm.tm_wday],tm.tm_mday,months[tm.tm_mon]
        !          1037:                        ,tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
        !          1038:                safecat(headers,header,MAX_HEADERS_SIZE);
        !          1039:                if(session->req.keep_alive) {
        !          1040:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_CONNECTION),"Keep-Alive");
        !          1041:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1042:                }
        !          1043:                else {
        !          1044:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_CONNECTION),"Close");
        !          1045:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1046:                }
        !          1047: 
        !          1048:                /* Response Headers */
        !          1049:                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_SERVER),VERSION_NOTICE);
        !          1050:                safecat(headers,header,MAX_HEADERS_SIZE);
        !          1051: 
        !          1052:                /* Entity Headers */
        !          1053:                if(session->req.dynamic) {
        !          1054:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD, POST, OPTIONS");
        !          1055:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1056:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ACCEPT_RANGES),"none");
        !          1057:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1058:                }
        !          1059:                else {
        !          1060:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD, OPTIONS");
        !          1061:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1062:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ACCEPT_RANGES),"bytes");
        !          1063:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1064:                }
        !          1065: 
        !          1066:                if(session->req.send_location) {
        !          1067:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_LOCATION),(session->req.virtual_path));
        !          1068:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1069:                }
        !          1070: 
        !          1071:                if(chunked) {
        !          1072:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_TRANSFER_ENCODING),"Chunked");
        !          1073:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1074:                }
        !          1075: 
        !          1076:                /* DO NOT send a content-length for chunked */
        !          1077:                if(session->req.keep_alive && session->req.dynamic!=IS_CGI && (!chunked)) {
        !          1078:                        if(ret)  {
        !          1079:                                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_LENGTH),"0");
        !          1080:                                safecat(headers,header,MAX_HEADERS_SIZE);
        !          1081:                        }
        !          1082:                        else  {
        !          1083:                                if((session->req.range_start || session->req.range_end) && atoi(status_line)==206) {
        !          1084:                                        safe_snprintf(header,sizeof(header),"%s: %d",get_header(HEAD_LENGTH),session->req.range_end-session->req.range_start+1);
        !          1085:                                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1086:                                }
        !          1087:                                else {
        !          1088:                                        safe_snprintf(header,sizeof(header),"%s: %d",get_header(HEAD_LENGTH),(int)stats.st_size);
        !          1089:                                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1090:                                }
        !          1091:                        }
        !          1092:                }
        !          1093: 
        !          1094:                if(!ret && !session->req.dynamic)  {
        !          1095:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_TYPE),session->req.mime_type);
        !          1096:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1097:                        gmtime_r(&stats.st_mtime,&tm);
        !          1098:                        safe_snprintf(header,sizeof(header),"%s: %s, %02d %s %04d %02d:%02d:%02d GMT"
        !          1099:                                ,get_header(HEAD_LASTMODIFIED)
        !          1100:                                ,days[tm.tm_wday],tm.tm_mday,months[tm.tm_mon]
        !          1101:                                ,tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
        !          1102:                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1103:                }
        !          1104: 
        !          1105:                if(session->req.range_start || session->req.range_end) {
        !          1106:                        switch(atoi(status_line)) {
        !          1107:                                case 206:       /* Partial reply */
        !          1108:                                        safe_snprintf(header,sizeof(header),"%s: bytes %d-%d/%d",get_header(HEAD_CONTENT_RANGE),session->req.range_start,session->req.range_end,stats.st_size);
        !          1109:                                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1110:                                        break;
        !          1111:                                default:
        !          1112:                                        safe_snprintf(header,sizeof(header),"%s: *",get_header(HEAD_CONTENT_RANGE));
        !          1113:                                        safecat(headers,header,MAX_HEADERS_SIZE);
        !          1114:                                        break;
        !          1115:                        }
        !          1116:                }
        !          1117:        }
        !          1118: 
        !          1119:        if(session->req.dynamic)  {
        !          1120:                /* Dynamic headers */
        !          1121:                /* Set up environment */
        !          1122:                for(idx=0;session->req.dynamic_heads[idx]!=NULL;idx++)
        !          1123:                        safecat(headers,session->req.dynamic_heads[idx],MAX_HEADERS_SIZE);
        !          1124:                /* free() the headers so they don't get sent again if more are sent at the end of the request (chunked) */
        !          1125:                strListFreeStrings(session->req.dynamic_heads);
        !          1126:        }
        !          1127: 
        !          1128:        safecat(headers,"",MAX_HEADERS_SIZE);
        !          1129:        send_file = (bufprint(session,headers) && send_file);
        !          1130:        drain_outbuf(session);
        !          1131:        session->req.write_chunked=chunked;
        !          1132:        return(send_file);
        !          1133: }
        !          1134: 
        !          1135: static int sock_sendfile(http_session_t *session,char *path,unsigned long start, unsigned long end)
        !          1136: {
        !          1137:        int             file;
        !          1138:        int             ret=0;
        !          1139:        int             i;
        !          1140:        char    buf[2048];              /* Input buffer */
        !          1141:        unsigned long           remain;
        !          1142: 
        !          1143:        if(startup->options&WEB_OPT_DEBUG_TX)
        !          1144:                lprintf(LOG_DEBUG,"%04d Sending %s",session->socket,path);
        !          1145:        if((file=open(path,O_RDONLY|O_BINARY))==-1)
        !          1146:                lprintf(LOG_WARNING,"%04d !ERROR %d opening %s",session->socket,errno,path);
        !          1147:        else {
        !          1148:                if(start || end) {
        !          1149:                        if(lseek(file, start, SEEK_SET)==-1) {
        !          1150:                                lprintf(LOG_WARNING,"%04d !ERROR %d seeking to position %lu in %s",session->socket,ERROR_VALUE,start,path);
        !          1151:                                return(0);
        !          1152:                        }
        !          1153:                        remain=end-start+1;
        !          1154:                }
        !          1155:                else {
        !          1156:                        remain=-1L;
        !          1157:                }
        !          1158:                while((i=read(file, buf, remain>sizeof(buf)?sizeof(buf):remain))>0) {
        !          1159:                        if(writebuf(session,buf,i)!=i) {
        !          1160:                                lprintf(LOG_WARNING,"%04d !ERROR sending %s",session->socket,path);
        !          1161:                                return(0);
        !          1162:                        }
        !          1163:                        ret+=i;
        !          1164:                        remain-=i;
        !          1165:                }
        !          1166:                close(file);
        !          1167:        }
        !          1168:        return(ret);
        !          1169: }
        !          1170: 
        !          1171: /********************************************************/
        !          1172: /* Sends a specified error message, closes the request, */
        !          1173: /* and marks the session to be closed                                  */
        !          1174: /********************************************************/
        !          1175: static void send_error(http_session_t * session, const char* message)
        !          1176: {
        !          1177:        char    error_code[4];
        !          1178:        struct stat     sb;
        !          1179:        char    sbuf[MAX_PATH+1];
        !          1180:        char    sbuf2[MAX_PATH+1];
        !          1181:        BOOL    sent_ssjs=FALSE;
        !          1182: 
        !          1183:        if(session->socket==INVALID_SOCKET)
        !          1184:                return;
        !          1185:        session->req.if_modified_since=0;
        !          1186:        lprintf(LOG_INFO,"%04d !ERROR: %s",session->socket,message);
        !          1187:        session->req.keep_alive=FALSE;
        !          1188:        session->req.send_location=NO_LOCATION;
        !          1189:        SAFECOPY(error_code,message);
        !          1190:        SAFECOPY(session->req.status,message);
        !          1191:        if(atoi(error_code)<500) {
        !          1192:                /*
        !          1193:                 * Attempt to run SSJS error pages
        !          1194:                 * If this fails, do the standard error page instead,
        !          1195:                 * ie: Don't "upgrade" to a 500 error
        !          1196:                 */
        !          1197: 
        !          1198:                if(session->req.error_dir) {
        !          1199:                        /* We have a custom error directory from webctrl.ini look there first */
        !          1200:                        sprintf(sbuf,"%s%s%s",session->req.error_dir,error_code,startup->ssjs_ext);
        !          1201:                        if(stat(sbuf,&sb)) {
        !          1202:                                /* No custom .ssjs error message... check for custom .html */
        !          1203:                                sprintf(sbuf2,"%s%s.html",session->req.error_dir,error_code);
        !          1204:                                if(stat(sbuf2,&sb)) {
        !          1205:                                        /* Nope, no custom .html error either, check for global ssjs one */
        !          1206:                                        sprintf(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
        !          1207:                                }
        !          1208:                        }
        !          1209:                }
        !          1210:                else
        !          1211:                        sprintf(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
        !          1212:                if(!stat(sbuf,&sb)) {
        !          1213:                        lprintf(LOG_INFO,"%04d Using SSJS error page",session->socket);
        !          1214:                        session->req.dynamic=IS_SSJS;
        !          1215:                        if(js_setup(session)) {
        !          1216:                                sent_ssjs=exec_ssjs(session,sbuf);
        !          1217:                                if(sent_ssjs) {
        !          1218:                                        int     snt=0;
        !          1219: 
        !          1220:                                        lprintf(LOG_INFO,"%04d Sending generated error page",session->socket);
        !          1221:                                        snt=sock_sendfile(session,session->req.physical_path,0,0);
        !          1222:                                        if(snt<0)
        !          1223:                                                snt=0;
        !          1224:                                        if(session->req.ld!=NULL)
        !          1225:                                                session->req.ld->size=snt;
        !          1226:                                }
        !          1227:                                else
        !          1228:                                         session->req.dynamic=IS_STATIC;
        !          1229:                        }
        !          1230:                        else
        !          1231:                                session->req.dynamic=IS_STATIC;
        !          1232:                }
        !          1233:        }
        !          1234:        if(!sent_ssjs) {
        !          1235:                if(session->req.error_dir) {
        !          1236:                        sprintf(session->req.physical_path,"%s%s.html",session->req.error_dir,error_code);
        !          1237:                        if(stat(session->req.physical_path,&sb))
        !          1238:                                sprintf(session->req.physical_path,"%s%s.html",error_dir,error_code);
        !          1239:                }
        !          1240:                else
        !          1241:                        sprintf(session->req.physical_path,"%s%s.html",error_dir,error_code,startup->ssjs_ext);
        !          1242:                session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
        !          1243:                send_headers(session,message,FALSE);
        !          1244:                if(!stat(session->req.physical_path,&sb)) {
        !          1245:                        int     snt=0;
        !          1246:                        snt=sock_sendfile(session,session->req.physical_path,0,0);
        !          1247:                        if(snt<0)
        !          1248:                                snt=0;
        !          1249:                        if(session->req.ld!=NULL)
        !          1250:                                session->req.ld->size=snt;
        !          1251:                }
        !          1252:                else {
        !          1253:                        lprintf(LOG_NOTICE,"%04d Error message file %s doesn't exist"
        !          1254:                                ,session->socket,session->req.physical_path);
        !          1255:                        safe_snprintf(sbuf,sizeof(sbuf)
        !          1256:                                ,"<HTML><HEAD><TITLE>%s Error</TITLE></HEAD>"
        !          1257:                                "<BODY><H1>%s Error</H1><BR><H3>In addition, "
        !          1258:                                "I can't seem to find the %s error file</H3><br>"
        !          1259:                                "please notify <a href=\"mailto:sysop@%s\">"
        !          1260:                                "%s</a></BODY></HTML>"
        !          1261:                                ,error_code,error_code,error_code,scfg.sys_inetaddr,scfg.sys_op);
        !          1262:                        bufprint(session,sbuf);
        !          1263:                        if(session->req.ld!=NULL)
        !          1264:                                session->req.ld->size=strlen(sbuf);
        !          1265:                }
        !          1266:        }
        !          1267:        drain_outbuf(session);
        !          1268:        session->req.finished=TRUE;
        !          1269: }
        !          1270: 
        !          1271: void http_logon(http_session_t * session, user_t *usr)
        !          1272: {
        !          1273:        char    str[128];
        !          1274: 
        !          1275:        if(usr==NULL)
        !          1276:                getuserdat(&scfg, &session->user);
        !          1277:        else
        !          1278:                session->user=*usr;
        !          1279: 
        !          1280:        if(session->user.number==session->last_user_num)
        !          1281:                return;
        !          1282: 
        !          1283:        lprintf(LOG_DEBUG,"%04d HTTP Logon (user #%d)",session->socket,session->user.number);
        !          1284: 
        !          1285:        if(session->subscan!=NULL)
        !          1286:                getmsgptrs(&scfg,session->user.number,session->subscan);
        !          1287: 
        !          1288:        session->logon_time=time(NULL);
        !          1289:        if(session->user.number==0)
        !          1290:                SAFECOPY(session->username,unknown);
        !          1291:        else {
        !          1292:                SAFECOPY(session->username,session->user.alias);
        !          1293:                /* Adjust Connect and host */
        !          1294:                putuserrec(&scfg,session->user.number,U_MODEM,LEN_MODEM,"HTTP");
        !          1295:                putuserrec(&scfg,session->user.number,U_COMP,LEN_COMP,session->host_name);
        !          1296:                putuserrec(&scfg,session->user.number,U_NOTE,LEN_NOTE,session->host_ip);
        !          1297:                putuserrec(&scfg,session->user.number,U_LOGONTIME,0,ultoa(session->logon_time,str,16));
        !          1298:        }
        !          1299:        session->client.user=session->username;
        !          1300:        client_on(session->socket, &session->client, /* update existing client record? */TRUE);
        !          1301: 
        !          1302:        session->last_user_num=session->user.number;
        !          1303: }
        !          1304: 
        !          1305: void http_logoff(http_session_t* session, SOCKET socket, int line)
        !          1306: {
        !          1307:        if(session->last_user_num<=0)
        !          1308:                return;
        !          1309: 
        !          1310:        lprintf(LOG_DEBUG,"%04d HTTP Logoff (user #%d) from line %d"
        !          1311:                ,socket,session->user.number, line);
        !          1312: 
        !          1313:        SAFECOPY(session->username,unknown);
        !          1314:        logoutuserdat(&scfg, &session->user, time(NULL), session->logon_time);
        !          1315:        memset(&session->user,0,sizeof(session->user));
        !          1316:        session->last_user_num=session->user.number;
        !          1317: }
        !          1318: 
        !          1319: BOOL http_checkuser(http_session_t * session)
        !          1320: {
        !          1321:        if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
        !          1322:                if(session->last_js_user_num==session->user.number)
        !          1323:                        return(TRUE);
        !          1324:                lprintf(LOG_INFO,"%04d JavaScript: Initializing User Objects",session->socket);
        !          1325:                if(session->user.number>0) {
        !          1326:                        if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user
        !          1327:                                ,NULL /* ftp index file */, session->subscan /* subscan */)) {
        !          1328:                                lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user objects",session->socket);
        !          1329:                                send_error(session,"500 Error initializing JavaScript User Objects");
        !          1330:                                return(FALSE);
        !          1331:                        }
        !          1332:                }
        !          1333:                else {
        !          1334:                        if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, NULL
        !          1335:                                ,NULL /* ftp index file */, session->subscan /* subscan */)) {
        !          1336:                                lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript User Objects",session->socket);
        !          1337:                                send_error(session,"500 Error initializing JavaScript User Objects");
        !          1338:                                return(FALSE);
        !          1339:                        }
        !          1340:                }
        !          1341:                session->last_js_user_num=session->user.number;
        !          1342:        }
        !          1343:        return(TRUE);
        !          1344: }
        !          1345: 
        !          1346: static BOOL check_ars(http_session_t * session)
        !          1347: {
        !          1348:        char    *username;
        !          1349:        char    *password;
        !          1350:        char    *last;
        !          1351:        uchar   *ar;
        !          1352:        BOOL    authorized;
        !          1353:        char    auth_req[MAX_REQUEST_LINE+1];
        !          1354:        int             i;
        !          1355:        user_t  thisuser;
        !          1356: 
        !          1357:        if(session->req.auth[0]==0) {
        !          1358:                /* No authentication information... */
        !          1359:                if(session->last_user_num!=0) {
        !          1360:                        if(session->last_user_num>0)
        !          1361:                                http_logoff(session,session->socket,__LINE__);
        !          1362:                        session->user.number=0;
        !          1363:                        http_logon(session,NULL);
        !          1364:                }
        !          1365:                if(!http_checkuser(session))
        !          1366:                        return(FALSE);
        !          1367:                if(session->req.ars[0]) {
        !          1368:                        /* There *IS* an ARS string  ie: Auth is required */
        !          1369:                        if(startup->options&WEB_OPT_DEBUG_RX)
        !          1370:                                lprintf(LOG_NOTICE,"%04d !No authentication information",session->socket);
        !          1371:                        return(FALSE);
        !          1372:                }
        !          1373:                /* No auth required, allow */
        !          1374:                return(TRUE);
        !          1375:        }
        !          1376:        SAFECOPY(auth_req,session->req.auth);
        !          1377: 
        !          1378:        username=strtok_r(auth_req,":",&last);
        !          1379:        if(username)
        !          1380:                password=strtok_r(NULL,":",&last);
        !          1381:        else {
        !          1382:                username="";
        !          1383:                password="";
        !          1384:        }
        !          1385:        /* Require a password */
        !          1386:        if(password==NULL)
        !          1387:                password="";
        !          1388:        i=matchuser(&scfg, username, FALSE);
        !          1389:        if(i==0) {
        !          1390:                if(session->last_user_num!=0) {
        !          1391:                        if(session->last_user_num>0)
        !          1392:                                http_logoff(session,session->socket,__LINE__);
        !          1393:                        session->user.number=0;
        !          1394:                        http_logon(session,NULL);
        !          1395:                }
        !          1396:                if(!http_checkuser(session))
        !          1397:                        return(FALSE);
        !          1398:                if(scfg.sys_misc&SM_ECHO_PW)
        !          1399:                        lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s, Password: %s"
        !          1400:                                ,session->socket,username,password);
        !          1401:                else
        !          1402:                        lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s"
        !          1403:                                ,session->socket,username);
        !          1404:                return(FALSE);
        !          1405:        }
        !          1406:        thisuser.number=i;
        !          1407:        getuserdat(&scfg, &thisuser);
        !          1408:        if(thisuser.pass[0] && stricmp(thisuser.pass,password)) {
        !          1409:                if(session->last_user_num!=0) {
        !          1410:                        if(session->last_user_num>0)
        !          1411:                                http_logoff(session,session->socket,__LINE__);
        !          1412:                        session->user.number=0;
        !          1413:                        http_logon(session,NULL);
        !          1414:                }
        !          1415:                if(!http_checkuser(session))
        !          1416:                        return(FALSE);
        !          1417:                /* Should go to the hack log? */
        !          1418:                if(scfg.sys_misc&SM_ECHO_PW)
        !          1419:                        lprintf(LOG_WARNING,"%04d !PASSWORD FAILURE for user %s: '%s' expected '%s'"
        !          1420:                                ,session->socket,username,password,thisuser.pass);
        !          1421:                else
        !          1422:                        lprintf(LOG_WARNING,"%04d !PASSWORD FAILURE for user %s"
        !          1423:                                ,session->socket,username);
        !          1424: #ifdef _WIN32
        !          1425:                if(startup->hack_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
        !          1426:                        PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
        !          1427: #endif
        !          1428:                return(FALSE);
        !          1429:        }
        !          1430: 
        !          1431:        if(i != session->last_user_num) {
        !          1432:                http_logoff(session,session->socket,__LINE__);
        !          1433:                session->user.number=i;
        !          1434:                http_logon(session,&thisuser);
        !          1435:        }
        !          1436:        if(!http_checkuser(session))
        !          1437:                return(FALSE);
        !          1438: 
        !          1439:        if(session->req.ld!=NULL) {
        !          1440:                FREE_AND_NULL(session->req.ld->user);
        !          1441:                /* FREE()d in http_logging_thread */
        !          1442:                session->req.ld->user=strdup(username);
        !          1443:        }
        !          1444: 
        !          1445:        ar = arstr(NULL,session->req.ars,&scfg);
        !          1446:        authorized=chk_ar(&scfg,ar,&session->user);
        !          1447:        if(ar!=NULL && ar!=nular)
        !          1448:                FREE_AND_NULL(ar);
        !          1449: 
        !          1450:        if(authorized)  {
        !          1451:                add_env(session,"AUTH_TYPE","Basic");
        !          1452:                /* Should use real name if set to do so somewhere ToDo */
        !          1453:                add_env(session,"REMOTE_USER",session->user.alias);
        !          1454: 
        !          1455:                return(TRUE);
        !          1456:        }
        !          1457: 
        !          1458:        /* Should go to the hack log? */
        !          1459:        lprintf(LOG_WARNING,"%04d !AUTHORIZATION FAILURE for user %s, ARS: %s"
        !          1460:                ,session->socket,username,session->req.ars);
        !          1461: 
        !          1462: #ifdef _WIN32
        !          1463:        if(startup->hack_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
        !          1464:                PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
        !          1465: #endif
        !          1466: 
        !          1467:        return(FALSE);
        !          1468: }
        !          1469: 
        !          1470: static named_string_t** read_ini_list(char* fname, char* section, char* desc
        !          1471:                                                                          ,named_string_t** list)
        !          1472: {
        !          1473:        char    path[MAX_PATH+1];
        !          1474:        size_t  i;
        !          1475:        FILE*   fp;
        !          1476: 
        !          1477:        list=iniFreeNamedStringList(list);
        !          1478: 
        !          1479:        iniFileName(path,sizeof(path),scfg.ctrl_dir,fname);
        !          1480: 
        !          1481:        if((fp=iniOpenFile(path, /* create? */FALSE))!=NULL) {
        !          1482:                list=iniReadNamedStringList(fp,section);
        !          1483:                iniCloseFile(fp);
        !          1484:                COUNT_LIST_ITEMS(list,i);
        !          1485:                if(i)
        !          1486:                        lprintf(LOG_DEBUG,"Read %u %s from %s",i,desc,path);
        !          1487:        }
        !          1488: 
        !          1489:        return(list);
        !          1490: }
        !          1491: 
        !          1492: static int sockreadline(http_session_t * session, char *buf, size_t length)
        !          1493: {
        !          1494:        char    ch;
        !          1495:        int             sel;
        !          1496:        DWORD   i;
        !          1497:        DWORD   chucked=0;
        !          1498:        fd_set  rd_set;
        !          1499:        struct  timeval tv;
        !          1500: 
        !          1501:        for(i=0;TRUE;) {
        !          1502:                if(session->socket==INVALID_SOCKET)
        !          1503:                        return(-1);
        !          1504:                FD_ZERO(&rd_set);
        !          1505:                FD_SET(session->socket,&rd_set);
        !          1506:                /* Convert timeout from ms to sec/usec */
        !          1507:                tv.tv_sec=startup->max_inactivity;
        !          1508:                tv.tv_usec=0;
        !          1509:                sel=select(session->socket+1,&rd_set,NULL,NULL,&tv);
        !          1510:                switch(sel) {
        !          1511:                        case 1:
        !          1512:                                break;
        !          1513:                        case -1:
        !          1514:                                close_socket(&session->socket);
        !          1515:                                lprintf(LOG_DEBUG,"%04d !ERROR %d selecting socket for read",session->socket,ERROR_VALUE);
        !          1516:                                return(-1);
        !          1517:                        default:
        !          1518:                                /* Timeout */
        !          1519:                                lprintf(LOG_WARNING,"%04d Session timeout due to inactivity (%d seconds)",session->socket,startup->max_inactivity);
        !          1520:                                return(-1);
        !          1521:                }
        !          1522: 
        !          1523:                switch(recv(session->socket, &ch, 1, 0)) {
        !          1524:                        case -1:
        !          1525:                                if(ERROR_VALUE!=EAGAIN) {
        !          1526:                                        if(startup->options&WEB_OPT_DEBUG_RX)
        !          1527:                                                lprintf(LOG_DEBUG,"%04d !ERROR %d receiving on socket",session->socket,ERROR_VALUE);
        !          1528:                                        close_socket(&session->socket);
        !          1529:                                        return(-1);
        !          1530:                                }
        !          1531:                                break;
        !          1532:                        case 0:
        !          1533:                                /* Socket has been closed */
        !          1534:                                close_socket(&session->socket);
        !          1535:                                return(-1);
        !          1536:                }
        !          1537: 
        !          1538:                if(ch=='\n')
        !          1539:                        break;
        !          1540: 
        !          1541:                if(i<length)
        !          1542:                        buf[i++]=ch;
        !          1543:                else
        !          1544:                        chucked++;
        !          1545:        }
        !          1546: 
        !          1547:        /* Terminate at length if longer */
        !          1548:        if(i>length)
        !          1549:                i=length;
        !          1550: 
        !          1551:        if(i>0 && buf[i-1]=='\r')
        !          1552:                buf[--i]=0;
        !          1553:        else
        !          1554:                buf[i]=0;
        !          1555: 
        !          1556:        if(startup->options&WEB_OPT_DEBUG_RX) {
        !          1557:                lprintf(LOG_DEBUG,"%04d RX: %s",session->socket,buf);
        !          1558:                if(chucked)
        !          1559:                        lprintf(LOG_DEBUG,"%04d Long header, chucked %d bytes",session->socket,chucked);
        !          1560:        }
        !          1561:        return(i);
        !          1562: }
        !          1563: 
        !          1564: #if defined(_WIN32)
        !          1565: static int pipereadline(HANDLE pipe, char *buf, size_t length, char *fullbuf, size_t fullbuf_len)
        !          1566: #else
        !          1567: static int pipereadline(int pipe, char *buf, size_t length, char *fullbuf, size_t fullbuf_len)
        !          1568: #endif
        !          1569: {
        !          1570:        char    ch;
        !          1571:        DWORD   i;
        !          1572:        int             ret=0;
        !          1573: #ifndef _WIN32
        !          1574:        struct timeval tv={0,0};
        !          1575:        fd_set  read_set;
        !          1576: #endif
        !          1577: 
        !          1578:        /* Terminate buffers */
        !          1579:        if(buf != NULL)
        !          1580:                buf[0]=0;
        !          1581:        if(fullbuf != NULL)
        !          1582:                fullbuf[0]=0;
        !          1583:        for(i=0;TRUE;) {
        !          1584: #if defined(_WIN32)
        !          1585:                ret=0;
        !          1586:                ReadFile(pipe, &ch, 1, (DWORD*)&ret, NULL);
        !          1587: #else
        !          1588:                tv.tv_sec=startup->max_cgi_inactivity;
        !          1589:                tv.tv_usec=0;
        !          1590:                FD_ZERO(&read_set);
        !          1591:                FD_SET(pipe, &read_set);
        !          1592:                if(select(pipe+1, &read_set, NULL, NULL, &tv)<1)
        !          1593:                        return(-1);
        !          1594:                ret=read(pipe, &ch, 1);
        !          1595: #endif
        !          1596:                if(ret==1)  {
        !          1597:                        if(fullbuf != NULL && i < (fullbuf_len-1)) {
        !          1598:                                fullbuf[i]=ch;
        !          1599:                                fullbuf[i+1]=0;
        !          1600:                        }
        !          1601: 
        !          1602:                        if(ch=='\n')
        !          1603:                                break;
        !          1604: 
        !          1605:                        if(buf != NULL && i<length)
        !          1606:                                buf[i]=ch;
        !          1607: 
        !          1608:                        i++;
        !          1609:                }
        !          1610:                else
        !          1611:                        return(-1);
        !          1612:        }
        !          1613: 
        !          1614:        /* Terminate at length if longer */
        !          1615:        if(i>length)
        !          1616:                i=length;
        !          1617: 
        !          1618:        if(i>0 && buf != NULL && buf[i-1]=='\r')
        !          1619:                buf[--i]=0;
        !          1620:        else {
        !          1621:                if(buf != NULL)
        !          1622:                        buf[i]=0;
        !          1623:        }
        !          1624: 
        !          1625:        return(i);
        !          1626: }
        !          1627: 
        !          1628: int recvbufsocket(SOCKET *sock, char *buf, long count)
        !          1629: {
        !          1630:        int             rd=0;
        !          1631:        int             i;
        !          1632:        time_t  start;
        !          1633: 
        !          1634:        if(count<1) {
        !          1635:                errno=ERANGE;
        !          1636:                return(0);
        !          1637:        }
        !          1638: 
        !          1639:        while(rd<count && socket_check(*sock,NULL,NULL,startup->max_inactivity*1000))  {
        !          1640:                i=recv(*sock,buf+rd,count-rd,0);
        !          1641:                switch(i) {
        !          1642:                        case -1:
        !          1643:                                if(ERROR_VALUE!=EAGAIN)
        !          1644:                                        close_socket(sock);
        !          1645:                        case 0:
        !          1646:                                close_socket(sock);
        !          1647:                                *buf=0;
        !          1648:                                return(0);
        !          1649:                }
        !          1650: 
        !          1651:                rd+=i;
        !          1652:                start=time(NULL);
        !          1653:        }
        !          1654: 
        !          1655:        if(rd==count)  {
        !          1656:                return(rd);
        !          1657:        }
        !          1658: 
        !          1659:        *buf=0;
        !          1660:        return(0);
        !          1661: }
        !          1662: 
        !          1663: static void unescape(char *p)
        !          1664: {
        !          1665:        char *  dst;
        !          1666:        char    code[3];
        !          1667:        
        !          1668:        dst=p;
        !          1669:        for(;*p;p++) {
        !          1670:                if(*p=='%' && isxdigit(*(p+1)) && isxdigit(*(p+2))) {
        !          1671:                        sprintf(code,"%.2s",p+1);
        !          1672:                        *(dst++)=(char)strtol(code,NULL,16);
        !          1673:                        p+=2;
        !          1674:                }
        !          1675:                else  {
        !          1676:                        if(*p=='+')  {
        !          1677:                                *(dst++)=' ';
        !          1678:                        }
        !          1679:                        else  {
        !          1680:                                *(dst++)=*p;
        !          1681:                        }
        !          1682:                }
        !          1683:        }
        !          1684:        *(dst)=0;
        !          1685: }
        !          1686: 
        !          1687: static void js_add_queryval(http_session_t * session, char *key, char *value)
        !          1688: {
        !          1689:        JSObject*       keyarray;
        !          1690:        jsval           val;
        !          1691:        jsuint          len;
        !          1692:        int                     alen;
        !          1693: 
        !          1694:        /* Return existing object if it's already been created */
        !          1695:        if(JS_GetProperty(session->js_cx,session->js_query,key,&val) && val!=JSVAL_VOID)  {
        !          1696:                keyarray = JSVAL_TO_OBJECT(val);
        !          1697:                alen=-1;
        !          1698:        }
        !          1699:        else {
        !          1700:                keyarray = JS_NewArrayObject(session->js_cx, 0, NULL);
        !          1701:                if(!JS_DefineProperty(session->js_cx, session->js_query, key, OBJECT_TO_JSVAL(keyarray)
        !          1702:                        , NULL, NULL, JSPROP_ENUMERATE))
        !          1703:                        return;
        !          1704:                alen=0;
        !          1705:        }
        !          1706: 
        !          1707:        if(alen==-1) {
        !          1708:                if(JS_GetArrayLength(session->js_cx, keyarray, &len)==JS_FALSE)
        !          1709:                        return;
        !          1710:                alen=len;
        !          1711:        }
        !          1712: 
        !          1713:        lprintf(LOG_DEBUG,"%04d Adding query value %s=%s at pos %d",session->socket,key,value,alen);
        !          1714:        val=STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx,value));
        !          1715:        JS_SetElement(session->js_cx, keyarray, alen, &val);
        !          1716: }
        !          1717: 
        !          1718: static void js_add_cookieval(http_session_t * session, char *key, char *value)
        !          1719: {
        !          1720:        JSObject*       keyarray;
        !          1721:        jsval           val;
        !          1722:        jsuint          len;
        !          1723:        int                     alen;
        !          1724: 
        !          1725:        /* Return existing object if it's already been created */
        !          1726:        if(JS_GetProperty(session->js_cx,session->js_cookie,key,&val) && val!=JSVAL_VOID)  {
        !          1727:                keyarray = JSVAL_TO_OBJECT(val);
        !          1728:                alen=-1;
        !          1729:        }
        !          1730:        else {
        !          1731:                keyarray = JS_NewArrayObject(session->js_cx, 0, NULL);
        !          1732:                if(!JS_DefineProperty(session->js_cx, session->js_cookie, key, OBJECT_TO_JSVAL(keyarray)
        !          1733:                        , NULL, NULL, JSPROP_ENUMERATE))
        !          1734:                        return;
        !          1735:                alen=0;
        !          1736:        }
        !          1737: 
        !          1738:        if(alen==-1) {
        !          1739:                if(JS_GetArrayLength(session->js_cx, keyarray, &len)==JS_FALSE)
        !          1740:                        return;
        !          1741:                alen=len;
        !          1742:        }
        !          1743: 
        !          1744:        lprintf(LOG_DEBUG,"%04d Adding cookie value %s=%s at pos %d",session->socket,key,value,alen);
        !          1745:        val=STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx,value));
        !          1746:        JS_SetElement(session->js_cx, keyarray, alen, &val);
        !          1747: }
        !          1748: 
        !          1749: static void js_add_request_prop(http_session_t * session, char *key, char *value)  
        !          1750: {
        !          1751:        JSString*       js_str;
        !          1752: 
        !          1753:        if(session->js_cx==NULL || session->js_request==NULL)
        !          1754:                return;
        !          1755:        if(key==NULL || value==NULL)
        !          1756:                return;
        !          1757:        if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL)
        !          1758:                return;
        !          1759:        JS_DefineProperty(session->js_cx, session->js_request, key, STRING_TO_JSVAL(js_str)
        !          1760:                ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
        !          1761: }
        !          1762: 
        !          1763: static void js_add_header(http_session_t * session, char *key, char *value)  
        !          1764: {
        !          1765:        JSString*       js_str;
        !          1766:        char            *lckey;
        !          1767: 
        !          1768:        if((lckey=(char *)alloca(strlen(key)+1))==NULL)
        !          1769:                return;
        !          1770:        strcpy(lckey,key);
        !          1771:        strlwr(lckey);
        !          1772:        if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL) {
        !          1773:                return;
        !          1774:        }
        !          1775:        JS_DefineProperty(session->js_cx, session->js_header, lckey, STRING_TO_JSVAL(js_str)
        !          1776:                ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
        !          1777: }
        !          1778: 
        !          1779: #if 0
        !          1780: static void js_parse_multipart(http_session_t * session, char *p)  {
        !          1781:        size_t          key_len;
        !          1782:        size_t          value_len;
        !          1783:        char            *lp;
        !          1784:        char            *key;
        !          1785:        char            *value;
        !          1786: 
        !          1787:        if(p == NULL)
        !          1788:                return;
        !          1789: 
        !          1790:        lp=p;
        !          1791: 
        !          1792:        while((key_len=strcspn(lp,"="))!=0)  {
        !          1793:                key=lp;
        !          1794:                lp+=key_len;
        !          1795:                if(*lp) {
        !          1796:                        *lp=0;
        !          1797:                        lp++;
        !          1798:                }
        !          1799:                value_len=strcspn(lp,"&");
        !          1800:                value=lp;
        !          1801:                lp+=value_len;
        !          1802:                if(*lp) {
        !          1803:                        *lp=0;
        !          1804:                        lp++;
        !          1805:                }
        !          1806:                unescape(value);
        !          1807:                unescape(key);
        !          1808:                js_add_queryval(session, key, value);
        !          1809:        }
        !          1810: }
        !          1811: #endif
        !          1812: 
        !          1813: static void js_parse_query(http_session_t * session, char *p)  {
        !          1814:        size_t          key_len;
        !          1815:        size_t          value_len;
        !          1816:        char            *lp;
        !          1817:        char            *key;
        !          1818:        char            *value;
        !          1819: 
        !          1820:        if(p == NULL)
        !          1821:                return;
        !          1822: 
        !          1823:        lp=p;
        !          1824: 
        !          1825:        while((key_len=strcspn(lp,"="))!=0)  {
        !          1826:                key=lp;
        !          1827:                lp+=key_len;
        !          1828:                if(*lp) {
        !          1829:                        *lp=0;
        !          1830:                        lp++;
        !          1831:                }
        !          1832:                value_len=strcspn(lp,"&");
        !          1833:                value=lp;
        !          1834:                lp+=value_len;
        !          1835:                if(*lp) {
        !          1836:                        *lp=0;
        !          1837:                        lp++;
        !          1838:                }
        !          1839:                unescape(value);
        !          1840:                unescape(key);
        !          1841:                js_add_queryval(session, key, value);
        !          1842:        }
        !          1843: }
        !          1844: 
        !          1845: static BOOL parse_headers(http_session_t * session)
        !          1846: {
        !          1847:        char    *head_line;
        !          1848:        char    *value;
        !          1849:        char    *last;
        !          1850:        char    *p;
        !          1851:        int             i;
        !          1852:        size_t  idx;
        !          1853:        size_t  content_len=0;
        !          1854:        char    env_name[128];
        !          1855: 
        !          1856:        for(idx=0;session->req.headers[idx]!=NULL;idx++) {
        !          1857:                head_line=session->req.headers[idx];
        !          1858:                if((strtok_r(head_line,":",&last))!=NULL && (value=strtok_r(NULL,"",&last))!=NULL) {
        !          1859:                        i=get_header_type(head_line);
        !          1860:                        while(*value && *value<=' ') value++;
        !          1861:                        if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)
        !          1862:                                js_add_header(session,head_line,value);
        !          1863:                        switch(i) {
        !          1864:                                case HEAD_AUTH:
        !          1865:                                        if(strtok_r(value," ",&last)) {
        !          1866:                                                p=strtok_r(NULL," ",&last);
        !          1867:                                                if(p==NULL)
        !          1868:                                                        break;
        !          1869:                                                while(*p && *p<' ') p++;
        !          1870:                                                b64_decode(session->req.auth,sizeof(session->req.auth),p,strlen(p));
        !          1871:                                        }
        !          1872:                                        break;
        !          1873:                                case HEAD_LENGTH:
        !          1874:                                        add_env(session,"CONTENT_LENGTH",value);
        !          1875:                                        content_len=strtol(value,NULL,10);
        !          1876:                                        break;
        !          1877:                                case HEAD_TYPE:
        !          1878:                                        add_env(session,"CONTENT_TYPE",value);
        !          1879:                                        if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
        !          1880:                                                /*
        !          1881:                                                 * We need to parse out the files based on RFC1867
        !          1882:                                                 *
        !          1883:                                                 * And example reponse looks like this:
        !          1884:                                                 * Content-type: multipart/form-data, boundary=AaB03x
        !          1885:                                                 * 
        !          1886:                                                 * --AaB03x
        !          1887:                                                 * content-disposition: form-data; name="field1"
        !          1888:                                                 * 
        !          1889:                                                 * Joe Blow
        !          1890:                                                 * --AaB03x
        !          1891:                                                 * content-disposition: form-data; name="pics"
        !          1892:                                                 * Content-type: multipart/mixed, boundary=BbC04y
        !          1893:                                                 * 
        !          1894:                                                 * --BbC04y
        !          1895:                                                 * Content-disposition: attachment; filename="file1.txt"
        !          1896:                                                 * 
        !          1897:                                                 * Content-Type: text/plain
        !          1898:                                                 * 
        !          1899:                                                 * ... contents of file1.txt ...
        !          1900:                                                 * --BbC04y
        !          1901:                                                 * Content-disposition: attachment; filename="file2.gif"
        !          1902:                                                 * Content-type: image/gif
        !          1903:                                                 * Content-Transfer-Encoding: binary
        !          1904:                                                 * 
        !          1905:                                                 * ...contents of file2.gif...
        !          1906:                                                 * --BbC04y--
        !          1907:                                                 * --AaB03x--                                            
        !          1908:                                                 */
        !          1909:                                        }
        !          1910:                                        break;
        !          1911:                                case HEAD_IFMODIFIED:
        !          1912:                                        session->req.if_modified_since=decode_date(value);
        !          1913:                                        break;
        !          1914:                                case HEAD_CONNECTION:
        !          1915:                                        if(!stricmp(value,"Keep-Alive")) {
        !          1916:                                                session->req.keep_alive=TRUE;
        !          1917:                                        }
        !          1918:                                        if(!stricmp(value,"Close")) {
        !          1919:                                                session->req.keep_alive=FALSE;
        !          1920:                                        }
        !          1921:                                        break;
        !          1922:                                case HEAD_REFERER:
        !          1923:                                        if(session->req.ld!=NULL) {
        !          1924:                                                FREE_AND_NULL(session->req.ld->referrer);
        !          1925:                                                /* FREE()d in http_logging_thread() */
        !          1926:                                                session->req.ld->referrer=strdup(value);
        !          1927:                                        }
        !          1928:                                        break;
        !          1929:                                case HEAD_AGENT:
        !          1930:                                        if(session->req.ld!=NULL) {
        !          1931:                                                FREE_AND_NULL(session->req.ld->agent);
        !          1932:                                                /* FREE()d in http_logging_thread() */
        !          1933:                                                session->req.ld->agent=strdup(value);
        !          1934:                                        }
        !          1935:                                        break;
        !          1936:                                case HEAD_TRANSFER_ENCODING:
        !          1937:                                        if(!stricmp(value,"chunked"))
        !          1938:                                                session->req.read_chunked=TRUE;
        !          1939:                                        else
        !          1940:                                                send_error(session,"501 Not Implemented");
        !          1941:                                        break;
        !          1942:                                case HEAD_RANGE:
        !          1943:                                        if(!stricmp(value,"bytes=")) {
        !          1944:                                                send_error(session,error_416);
        !          1945:                                                break;
        !          1946:                                        }
        !          1947:                                        value+=6;
        !          1948:                                        if(strchr(value,',')!=NULL) {   /* We don't do multiple ranges yet - TODO */
        !          1949:                                                send_error(session,error_416);
        !          1950:                                                break;
        !          1951:                                        }
        !          1952:                                        /* Check for offset from end. */
        !          1953:                                        if(*value=='-') {
        !          1954:                                                session->req.range_start=strtol(value,NULL,10);
        !          1955:                                                session->req.range_end=-1;
        !          1956:                                                break;
        !          1957:                                        }
        !          1958:                                        if((p=strtok_r(value,"-",&last))!=NULL) {
        !          1959:                                                session->req.range_start=strtol(p,NULL,10);
        !          1960:                                                if((p=strtok_r(NULL,"-",&last))!=NULL)
        !          1961:                                                        session->req.range_end=strtol(p,NULL,10);
        !          1962:                                                else
        !          1963:                                                        session->req.range_end=-1;
        !          1964:                                        }
        !          1965:                                        else {
        !          1966:                                                send_error(session,error_416);
        !          1967:                                                break;
        !          1968:                                        }
        !          1969:                                        break;
        !          1970:                                case HEAD_IFRANGE:
        !          1971:                                        session->req.if_range=decode_date(value);
        !          1972:                                        break;
        !          1973:                                case HEAD_COOKIE:
        !          1974:                                        if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
        !          1975:                                                char    *key;
        !          1976:                                                char    *val;
        !          1977: 
        !          1978:                                                p=value;
        !          1979:                                                while((key=strtok_r(p,"=",&last))!=NULL) {
        !          1980:                                                        p=NULL;
        !          1981:                                                        if((val=strtok_r(p,";\t\n\v\f\r ",&last))!=NULL) {      /* Whitespace */
        !          1982:                                                                js_add_cookieval(session,key,val);
        !          1983:                                                        }
        !          1984:                                                }
        !          1985:                                        }
        !          1986:                                        break;
        !          1987:                                default:
        !          1988:                                        break;
        !          1989:                        }
        !          1990:                        sprintf(env_name,"HTTP_%s",head_line);
        !          1991:                        add_env(session,env_name,value);
        !          1992:                }
        !          1993:        }
        !          1994:        if(content_len)
        !          1995:                session->req.post_len = content_len;
        !          1996:        add_env(session,"SERVER_NAME",session->req.host[0] ? session->req.host : startup->host_name );
        !          1997:        return TRUE;
        !          1998: }
        !          1999: 
        !          2000: static int get_version(char *p)
        !          2001: {
        !          2002:        int             i;
        !          2003:        if(p==NULL)
        !          2004:                return(0);
        !          2005:        while(*p && *p<' ') p++;
        !          2006:        if(*p==0)
        !          2007:                return(0);
        !          2008:        for(i=1;http_vers[i]!=NULL;i++) {
        !          2009:                if(!stricmp(p,http_vers[i])) {
        !          2010:                        return(i);
        !          2011:                }
        !          2012:        }
        !          2013:        return(i-1);
        !          2014: }
        !          2015: 
        !          2016: static int is_dynamic_req(http_session_t* session)
        !          2017: {
        !          2018:        int             i=0;
        !          2019:        char    drive[4];
        !          2020:        char    cgidrive[4];
        !          2021:        char    dir[MAX_PATH+1];
        !          2022:        char    cgidir[MAX_PATH+1];
        !          2023:        char    fname[MAX_PATH+1];
        !          2024:        char    ext[MAX_PATH+1];
        !          2025: 
        !          2026:        check_extra_path(session);
        !          2027:        _splitpath(session->req.physical_path, drive, dir, fname, ext);
        !          2028: 
        !          2029:        if(stricmp(ext,startup->ssjs_ext)==0)
        !          2030:                i=IS_SSJS;
        !          2031:        else if(get_xjs_handler(ext,session))
        !          2032:                i=IS_SSJS;
        !          2033:        else if(stricmp(ext,startup->js_ext)==0)
        !          2034:                i=IS_JS;
        !          2035:        if(!(startup->options&BBS_OPT_NO_JAVASCRIPT) && i)  {
        !          2036:                lprintf(LOG_INFO,"%04d Setting up JavaScript support", session->socket);
        !          2037:                if(!js_setup(session)) {
        !          2038:                        lprintf(LOG_ERR,"%04d !ERROR setting up JavaScript support", session->socket);
        !          2039:                        send_error(session,error_500);
        !          2040:                        return(IS_STATIC);
        !          2041:                }
        !          2042: 
        !          2043:                return(i);
        !          2044:        }
        !          2045: 
        !          2046:        if(!(startup->options&WEB_OPT_NO_CGI)) {
        !          2047:                for(i=0; startup->cgi_ext!=NULL && startup->cgi_ext[i]!=NULL; i++)  {
        !          2048:                        if(stricmp(ext,startup->cgi_ext[i])==0)  {
        !          2049:                                init_enviro(session);
        !          2050:                                return(IS_CGI);
        !          2051:                        }
        !          2052:                }
        !          2053:                _splitpath(session->req.cgi_dir?session->req.cgi_dir:cgi_dir, cgidrive, cgidir, fname, ext);
        !          2054:                if(stricmp(dir,cgidir)==0 && stricmp(drive,cgidrive)==0)  {
        !          2055:                        init_enviro(session);
        !          2056:                        return(IS_CGI);
        !          2057:                }
        !          2058:        }
        !          2059: 
        !          2060:        return(IS_STATIC);
        !          2061: }
        !          2062: 
        !          2063: static char *get_request(http_session_t * session, char *req_line)
        !          2064: {
        !          2065:        char*   p;
        !          2066:        char*   query;
        !          2067:        char*   retval;
        !          2068:        char*   last;
        !          2069:        int             offset;
        !          2070: 
        !          2071:        SKIP_WHITESPACE(req_line);
        !          2072:        SAFECOPY(session->req.virtual_path,req_line);
        !          2073:        if(strtok_r(session->req.virtual_path," \t",&last))
        !          2074:                retval=strtok_r(NULL," \t",&last);
        !          2075:        else
        !          2076:                retval=NULL;
        !          2077:        SAFECOPY(session->req.request_line,session->req.virtual_path);
        !          2078:        if(strtok_r(session->req.virtual_path,"?",&last))
        !          2079:                query=strtok_r(NULL,"",&last);
        !          2080:        else
        !          2081:                query=NULL;
        !          2082: 
        !          2083:        /* Must initialize physical_path before calling is_dynamic_req() */
        !          2084:        SAFECOPY(session->req.physical_path,session->req.virtual_path);
        !          2085:        unescape(session->req.physical_path);
        !          2086:        if(!strnicmp(session->req.physical_path,http_scheme,http_scheme_len)) {
        !          2087:                /* Set HOST value... ignore HOST header */
        !          2088:                SAFECOPY(session->req.host,session->req.physical_path+http_scheme_len);
        !          2089:                SAFECOPY(session->req.vhost,session->req.host);
        !          2090:                /* Remove port specification */
        !          2091:                strtok_r(session->req.vhost,":",&last);
        !          2092:                if(strtok_r(session->req.physical_path,"/",&last))
        !          2093:                        p=strtok_r(NULL,"/",&last);
        !          2094:                else
        !          2095:                        p=NULL;
        !          2096:                if(p==NULL) {
        !          2097:                        /* Do not allow host values larger than 128 bytes */
        !          2098:                        session->req.host[0]=0;
        !          2099:                        p=session->req.physical_path+http_scheme_len;
        !          2100:                }
        !          2101:                offset=p-session->req.physical_path;
        !          2102:                memmove(session->req.physical_path
        !          2103:                        ,session->req.physical_path+offset
        !          2104:                        ,strlen(session->req.physical_path+offset)+1    /* move '\0' terminator too */
        !          2105:                        );
        !          2106:        }
        !          2107:        if(query!=NULL)
        !          2108:                SAFECOPY(session->req.query_str,query);
        !          2109: 
        !          2110:        return(retval);
        !          2111: }
        !          2112: 
        !          2113: static char *get_method(http_session_t * session, char *req_line)
        !          2114: {
        !          2115:        int i;
        !          2116: 
        !          2117:        for(i=0;methods[i]!=NULL;i++) {
        !          2118:                if(!strnicmp(req_line,methods[i],strlen(methods[i]))) {
        !          2119:                        session->req.method=i;
        !          2120:                        if(strlen(req_line)<strlen(methods[i])+2) {
        !          2121:                                send_error(session,"400 Bad Request");
        !          2122:                                return(NULL);
        !          2123:                        }
        !          2124:                        return(req_line+strlen(methods[i])+1);
        !          2125:                }
        !          2126:        }
        !          2127:        if(req_line!=NULL && *req_line>=' ')
        !          2128:                send_error(session,"501 Not Implemented");
        !          2129:        return(NULL);
        !          2130: }
        !          2131: 
        !          2132: static BOOL get_request_headers(http_session_t * session)
        !          2133: {
        !          2134:        char    head_line[MAX_REQUEST_LINE+1];
        !          2135:        char    next_char;
        !          2136:        char    *value;
        !          2137:        char    *last;
        !          2138:        int             i;
        !          2139: 
        !          2140:        while(sockreadline(session,head_line,sizeof(head_line)-1)>0) {
        !          2141:                /* Multi-line headers */
        !          2142:                while((i=recv(session->socket,&next_char,1,MSG_PEEK))>0
        !          2143:                        && (next_char=='\t' || next_char==' ')) {
        !          2144:                        if(i==-1 && ERROR_VALUE != EAGAIN)
        !          2145:                                close_socket(&session->socket);
        !          2146:                        i=strlen(head_line);
        !          2147:                        if(i>sizeof(head_line)-1) {
        !          2148:                                lprintf(LOG_ERR,"%04d !ERROR long multi-line header. The web server is broken!", session->socket);
        !          2149:                                i=sizeof(head_line)/2;
        !          2150:                                break;
        !          2151:                        }
        !          2152:                        sockreadline(session,head_line+i,sizeof(head_line)-i-1);
        !          2153:                }
        !          2154:                strListPush(&session->req.headers,head_line);
        !          2155: 
        !          2156:                if((strtok_r(head_line,":",&last))!=NULL && (value=strtok_r(NULL,"",&last))!=NULL) {
        !          2157:                        i=get_header_type(head_line);
        !          2158:                        while(*value && *value<=' ') value++;
        !          2159:                        switch(i) {
        !          2160:                                case HEAD_HOST:
        !          2161:                                        if(session->req.host[0]==0) {
        !          2162:                                                SAFECOPY(session->req.host,value);
        !          2163:                                                SAFECOPY(session->req.vhost,value);
        !          2164:                                                /* Remove port part of host (Win32 doesn't allow : in dir names) */
        !          2165:                                                /* Either an existing : will be replaced with a null, or nothing */
        !          2166:                                                /* Will happen... the return value is not relevent here */
        !          2167:                                                strtok_r(session->req.vhost,":",&last);
        !          2168:                                        }
        !          2169:                                        break;
        !          2170:                                default:
        !          2171:                                        break;
        !          2172:                        }
        !          2173:                }
        !          2174:        }
        !          2175: 
        !          2176:        if(!(session->req.vhost[0]))
        !          2177:                SAFECOPY(session->req.vhost, startup->host_name);
        !          2178:        if(!(session->req.host[0]))
        !          2179:                SAFECOPY(session->req.host, startup->host_name);
        !          2180:        return TRUE;
        !          2181: }
        !          2182: 
        !          2183: static BOOL get_fullpath(http_session_t * session)
        !          2184: {
        !          2185:        char    str[MAX_PATH+1];
        !          2186: 
        !          2187:        if(session->req.vhost[0] && startup->options&WEB_OPT_VIRTUAL_HOSTS) {
        !          2188:                safe_snprintf(str,sizeof(str),"%s/%s",root_dir,session->req.vhost);
        !          2189:                if(isdir(str))
        !          2190:                        safe_snprintf(str,sizeof(str),"%s/%s%s",root_dir,session->req.vhost,session->req.physical_path);
        !          2191:                else
        !          2192:                        safe_snprintf(str,sizeof(str),"%s%s",root_dir,session->req.physical_path);
        !          2193:        } else
        !          2194:                safe_snprintf(str,sizeof(str),"%s%s",root_dir,session->req.physical_path);
        !          2195: 
        !          2196:        if(FULLPATH(session->req.physical_path,str,sizeof(session->req.physical_path))==NULL) {
        !          2197:                send_error(session,error_500);
        !          2198:                return(FALSE);
        !          2199:        }
        !          2200: 
        !          2201:        return(TRUE);
        !          2202: }
        !          2203: 
        !          2204: static BOOL get_req(http_session_t * session, char *request_line)
        !          2205: {
        !          2206:        char    req_line[MAX_REQUEST_LINE+1];
        !          2207:        char *  p;
        !          2208:        int             is_redir=0;
        !          2209:        int             len;
        !          2210: 
        !          2211:        req_line[0]=0;
        !          2212:        if(request_line == NULL) {
        !          2213:                /* Eat leaing blank lines... as apache does...
        !          2214:                 * "This is a legacy issue. The CERN webserver required POST data to have an extra
        !          2215:                 * CRLF following it. Thus many clients send an extra CRLF that is not included in the
        !          2216:                 * Content-Length of the request. Apache works around this problem by eating any empty
        !          2217:                 * lines which appear before a request."
        !          2218:                 * http://httpd.apache.org/docs/misc/known_client_problems.html
        !          2219:                 */
        !          2220:                while((len=sockreadline(session,req_line,sizeof(req_line)-1))==0);
        !          2221:                if(len<0)
        !          2222:                        return(FALSE);
        !          2223:                if(req_line[0])
        !          2224:                        lprintf(LOG_DEBUG,"%04d Request: %s",session->socket,req_line);
        !          2225:                if(session->req.ld!=NULL && session->req.ld->request==NULL)
        !          2226:                        /* FREE()d in http_logging_thread() */
        !          2227:                        session->req.ld->request=strdup(req_line);
        !          2228:        }
        !          2229:        else {
        !          2230:                lprintf(LOG_DEBUG,"%04d Handling Internal Redirect to: %s",session->socket,request_line);
        !          2231:                SAFECOPY(req_line,request_line);
        !          2232:                is_redir=1;
        !          2233:        }
        !          2234:        if(req_line[0]) {
        !          2235:                p=NULL;
        !          2236:                p=get_method(session,req_line);
        !          2237:                if(p!=NULL) {
        !          2238:                        p=get_request(session,p);
        !          2239:                        session->http_ver=get_version(p);
        !          2240:                        if(session->http_ver>=HTTP_1_1)
        !          2241:                                session->req.keep_alive=TRUE;
        !          2242:                        if(!is_redir)
        !          2243:                                get_request_headers(session);
        !          2244:                        if(!get_fullpath(session)) {
        !          2245:                                send_error(session,error_500);
        !          2246:                                return(FALSE);
        !          2247:                        }
        !          2248:                        if(session->req.ld!=NULL && session->req.ld->vhost==NULL)
        !          2249:                                /* FREE()d in http_logging_thread() */
        !          2250:                                session->req.ld->vhost=strdup(session->req.vhost);
        !          2251:                        session->req.dynamic=is_dynamic_req(session);
        !          2252:                        if(session->req.query_str[0])
        !          2253:                                add_env(session,"QUERY_STRING",session->req.query_str);
        !          2254: 
        !          2255:                        add_env(session,"REQUEST_METHOD",methods[session->req.method]);
        !          2256:                        add_env(session,"SERVER_PROTOCOL",session->http_ver ? 
        !          2257:                                http_vers[session->http_ver] : "HTTP/0.9");
        !          2258:                        return(TRUE);
        !          2259:                }
        !          2260:        }
        !          2261:        session->req.keep_alive=FALSE;
        !          2262:        send_error(session,"400 Bad Request");
        !          2263:        return FALSE;
        !          2264: }
        !          2265: 
        !          2266: /* This may exist somewhere else - ToDo */
        !          2267: static char *find_last_slash(char *str)
        !          2268: {
        !          2269: #ifdef _WIN32
        !          2270:        char * LastFSlash;
        !          2271:        char * LastBSlash;
        !          2272: 
        !          2273:        LastFSlash=strrchr(str,'/');
        !          2274:        LastBSlash=strrchr(str,'\\');
        !          2275:        if(LastFSlash==NULL)
        !          2276:                return(LastBSlash);
        !          2277:        if(LastBSlash==NULL)
        !          2278:                return(LastFSlash);
        !          2279:        if(LastBSlash < LastFSlash)
        !          2280:                return(LastFSlash);
        !          2281:        return(LastBSlash);
        !          2282: #else
        !          2283:        return(strrchr(str,'/'));
        !          2284: #endif
        !          2285: }
        !          2286: 
        !          2287: /* This may exist somewhere else - ToDo */
        !          2288: static char *find_first_slash(char *str)
        !          2289: {
        !          2290: #ifdef _WIN32
        !          2291:        char * FirstFSlash;
        !          2292:        char * FirstBSlash;
        !          2293: 
        !          2294:        FirstFSlash=strchr(str,'/');
        !          2295:        FirstBSlash=strchr(str,'\\');
        !          2296:        if(FirstFSlash==NULL)
        !          2297:                return(FirstBSlash);
        !          2298:        if(FirstBSlash==NULL)
        !          2299:                return(FirstFSlash);
        !          2300:        if(FirstBSlash > FirstFSlash)
        !          2301:                return(FirstFSlash);
        !          2302:        return(FirstBSlash);
        !          2303: #else
        !          2304:        return(strchr(str,'/'));
        !          2305: #endif
        !          2306: }
        !          2307: 
        !          2308: static BOOL check_extra_path(http_session_t * session)
        !          2309: {
        !          2310:        char    *rp_slash;
        !          2311:        char    *vp_slash;
        !          2312:        char    rpath[MAX_PATH+1];
        !          2313:        char    vpath[MAX_PATH+1];
        !          2314:        char    epath[MAX_PATH+1];
        !          2315:        char    str[MAX_PATH+1];
        !          2316:        struct  stat sb;
        !          2317:        int             i;
        !          2318:        char    *end;
        !          2319:        int     use_epath=0;
        !          2320: 
        !          2321:        epath[0]=0;
        !          2322:        epath[1]=0;
        !          2323:        if(IS_PATH_DELIM(*lastchar(session->req.physical_path)) || stat(session->req.physical_path,&sb)==-1 /* && errno==ENOTDIR */)
        !          2324:        {
        !          2325:                SAFECOPY(vpath,session->req.virtual_path);
        !          2326:                SAFECOPY(rpath,session->req.physical_path);
        !          2327:                while((vp_slash=find_last_slash(vpath))!=NULL)
        !          2328:                {
        !          2329:                        *vp_slash=0;
        !          2330:                        if((rp_slash=find_last_slash(rpath))==NULL)
        !          2331:                                return(FALSE);
        !          2332:                        SAFECOPY(str,epath);
        !          2333:                        if(*rp_slash)
        !          2334:                                sprintf(epath,"%s%s",rp_slash,str);
        !          2335:                        *(rp_slash+1)=0;
        !          2336: 
        !          2337:                        /* Check if this contains an index */
        !          2338:                        end=strchr(rpath,0);
        !          2339:                        if(use_epath || session->req.path_info_index || strchr(epath+1,'/')!=NULL) {
        !          2340:                                use_epath=1;
        !          2341:                                if(isdir(rpath) && !isdir(session->req.physical_path)) {
        !          2342:                                        for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++)  {
        !          2343:                                                *end=0;
        !          2344:                                                strcat(rpath,startup->index_file_name[i]);
        !          2345:                                                if(!stat(rpath,&sb)) {
        !          2346:                                                        /* *end=0; /* Removed Wed, Aug 09, 2006 to allow is_dynamic_req to detect correctly */
        !          2347:                                                        SAFECOPY(session->req.extra_path_info,epath);
        !          2348:                                                        SAFECOPY(session->req.virtual_path,vpath);
        !          2349:                                                        strcat(session->req.virtual_path,"/");
        !          2350:                                                        SAFECOPY(session->req.physical_path,rpath);
        !          2351:                                                        return(TRUE);
        !          2352:                                                }
        !          2353:                                        }
        !          2354:                                        /* rpath was an existing path and DID NOT contain an index. */
        !          2355:                                        /* We do not allow scripts to mask existing dirs/files */
        !          2356:                                        return(FALSE);
        !          2357:                                }
        !          2358:                        }
        !          2359:                        else {
        !          2360:                                if(isdir(rpath))
        !          2361:                                        return(FALSE);
        !          2362:                        }
        !          2363: 
        !          2364:                        if(vp_slash==vpath)
        !          2365:                                return(FALSE);
        !          2366: 
        !          2367:                        /* Check if this is a script */
        !          2368:                        *rp_slash=0;
        !          2369:                        if(vp_slash!=vpath) {
        !          2370:                                if(stat(rpath,&sb)!=-1 && (!(sb.st_mode&S_IFDIR)))
        !          2371:                                {
        !          2372:                                        SAFECOPY(session->req.extra_path_info,epath);
        !          2373:                                        SAFECOPY(session->req.virtual_path,vpath);
        !          2374:                                        SAFECOPY(session->req.physical_path,rpath);
        !          2375:                                        return(TRUE);
        !          2376:                                }
        !          2377:                        }
        !          2378:                }
        !          2379:        }
        !          2380:        return(FALSE);
        !          2381: }
        !          2382: 
        !          2383: static BOOL check_request(http_session_t * session)
        !          2384: {
        !          2385:        char    path[MAX_PATH+1];
        !          2386:        char    curdir[MAX_PATH+1];
        !          2387:        char    str[MAX_PATH+1];
        !          2388:        char    last_ch;
        !          2389:        char*   last_slash;
        !          2390:        char*   p;
        !          2391:        FILE*   file;
        !          2392:        int             i;
        !          2393:        struct stat sb;
        !          2394:        int             send404=0;
        !          2395:        char    filename[MAX_PATH+1];
        !          2396:        char    *spec;
        !          2397:        str_list_t      specs;
        !          2398:        BOOL    recheck_dynamic=FALSE;
        !          2399: 
        !          2400:        if(session->req.finished)
        !          2401:                return(FALSE);
        !          2402: 
        !          2403:        SAFECOPY(path,session->req.physical_path);
        !          2404:        if(startup->options&WEB_OPT_DEBUG_TX)
        !          2405:                lprintf(LOG_DEBUG,"%04d Path is: %s",session->socket,path);
        !          2406: 
        !          2407:        if(isdir(path)) {
        !          2408:                last_ch=*lastchar(path);
        !          2409:                if(!IS_PATH_DELIM(last_ch))  {
        !          2410:                        session->req.send_location=MOVED_PERM;
        !          2411:                        strcat(path,"/");
        !          2412:                        strcat(session->req.physical_path,"/");
        !          2413:                }
        !          2414:                last_ch=*lastchar(session->req.virtual_path);
        !          2415:                if(!IS_PATH_DELIM(last_ch))  {
        !          2416:                        session->req.send_location=MOVED_PERM;
        !          2417:                        strcat(session->req.virtual_path,"/");
        !          2418:                }
        !          2419:                last_slash=find_last_slash(path);
        !          2420:                if(last_slash==NULL) {
        !          2421:                        send_error(session,error_500);
        !          2422:                        return(FALSE);
        !          2423:                }
        !          2424:                last_slash++;
        !          2425:                for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++)  {
        !          2426:                        *last_slash=0;
        !          2427:                        strcat(path,startup->index_file_name[i]);
        !          2428:                        if(startup->options&WEB_OPT_DEBUG_TX)
        !          2429:                                lprintf(LOG_DEBUG,"%04d Checking for %s",session->socket,path);
        !          2430:                        if(!stat(path,&sb))
        !          2431:                                break;
        !          2432:                        SAFECOPY(path,session->req.physical_path);
        !          2433:                }
        !          2434: 
        !          2435:                /* Don't send 404 unless authourized... prevent info leak */
        !          2436:                if(startup->index_file_name==NULL || startup->index_file_name[i] == NULL)
        !          2437:                        send404=1;
        !          2438:                else {
        !          2439:                        strcat(session->req.virtual_path,startup->index_file_name[i]);
        !          2440:                        if(session->req.send_location != MOVED_PERM)
        !          2441:                                session->req.send_location=MOVED_STAT;
        !          2442:                }
        !          2443:                filename[0]=0;
        !          2444:        }
        !          2445:        else {
        !          2446:                last_slash=find_last_slash(path);
        !          2447:                if(last_slash==NULL)
        !          2448:                        last_slash=path;
        !          2449:                else
        !          2450:                        last_slash++;
        !          2451:                strcpy(filename,last_slash);
        !          2452:        }
        !          2453:        if(strnicmp(path,root_dir,strlen(root_dir))) {
        !          2454:                session->req.keep_alive=FALSE;
        !          2455:                send_error(session,"400 Bad Request");
        !          2456:                lprintf(LOG_NOTICE,"%04d !ERROR Request for %s is outside of web root %s"
        !          2457:                        ,session->socket,path,root_dir);
        !          2458:                return(FALSE);
        !          2459:        }
        !          2460: 
        !          2461:        /* Set default ARS to a 0-length string */
        !          2462:        session->req.ars[0]=0;
        !          2463:        /* Walk up from root_dir checking for access.ars and webconfig.ini */
        !          2464:        SAFECOPY(curdir,path);
        !          2465:        last_slash=curdir+strlen(root_dir)-1;
        !          2466:        /* Loop while there's more /s in path*/
        !          2467:        p=last_slash;
        !          2468: 
        !          2469:        while((last_slash=find_first_slash(p+1))!=NULL) {
        !          2470:                p=last_slash;
        !          2471:                /* Terminate the path after the slash */
        !          2472:                *(last_slash+1)=0;
        !          2473:                sprintf(str,"%saccess.ars",curdir);
        !          2474:                if(!stat(str,&sb)) {
        !          2475:                        /* NEVER serve up an access.ars file */
        !          2476:                        lprintf(LOG_WARNING,"%04d !WARNING! access.ars support is depreciated and will be REMOVED very soon.",session->socket);
        !          2477:                        lprintf(LOG_WARNING,"%04d !WARNING! access.ars found at %s.",session->socket,str);
        !          2478:                        if(!strcmp(path,str)) {
        !          2479:                                send_error(session,"403 Forbidden");
        !          2480:                                return(FALSE);
        !          2481:                        }
        !          2482:                        /* Read access.ars file */
        !          2483:                        if((file=fopen(str,"r"))!=NULL) {
        !          2484:                                fgets(session->req.ars,sizeof(session->req.ars),file);
        !          2485:                                fclose(file);
        !          2486:                        }
        !          2487:                        else  {
        !          2488:                                /* If cannot open access.ars, only allow sysop access */
        !          2489:                                SAFECOPY(session->req.ars,"LEVEL 90");
        !          2490:                                break;
        !          2491:                        }
        !          2492:                        /* Truncate at \r or \n - can use last_slash since I'm done with it.*/
        !          2493:                        truncsp(session->req.ars);
        !          2494:                }
        !          2495:                sprintf(str,"%swebctrl.ini",curdir);
        !          2496:                if(!stat(str,&sb)) {
        !          2497:                        /* NEVER serve up a webctrl.ini file */
        !          2498:                        if(!strcmp(path,str)) {
        !          2499:                                send_error(session,"403 Forbidden");
        !          2500:                                return(FALSE);
        !          2501:                        }
        !          2502:                        /* Read webctrl.ars file */
        !          2503:                        if((file=fopen(str,"r"))!=NULL) {
        !          2504:                                /* FREE()d in this block */
        !          2505:                                specs=iniReadSectionList(file,NULL);
        !          2506:                                /* Read in globals */
        !          2507:                                if(iniReadString(file, NULL, "AccessRequirements", session->req.ars,str)==str)
        !          2508:                                        SAFECOPY(session->req.ars,str);
        !          2509:                                if(iniReadString(file, NULL, "Realm", scfg.sys_name,str)==str) {
        !          2510:                                        FREE_AND_NULL(session->req.realm);
        !          2511:                                        /* FREE()d in close_request() */
        !          2512:                                        session->req.realm=strdup(str);
        !          2513:                                }
        !          2514:                                if(iniReadString(file, NULL, "ErrorDirectory", error_dir,str)==str) {
        !          2515:                                        prep_dir(root_dir, str, sizeof(str));
        !          2516:                                        FREE_AND_NULL(session->req.error_dir);
        !          2517:                                        /* FREE()d in close_request() */
        !          2518:                                        session->req.error_dir=strdup(str);
        !          2519:                                }
        !          2520:                                if(iniReadString(file, NULL, "CGIDirectory", cgi_dir,str)==str) {
        !          2521:                                        prep_dir(root_dir, str, sizeof(str));
        !          2522:                                        FREE_AND_NULL(session->req.cgi_dir);
        !          2523:                                        /* FREE()d in close_request() */
        !          2524:                                        session->req.cgi_dir=strdup(str);
        !          2525:                                        recheck_dynamic=TRUE;
        !          2526:                                }
        !          2527:                                session->req.path_info_index=iniReadBool(file, NULL, "PathInfoIndex", FALSE);
        !          2528:                                /* Read in per-filespec */
        !          2529:                                while((spec=strListPop(&specs))!=NULL) {
        !          2530:                                        if(wildmatch(filename,spec,TRUE)) {
        !          2531:                                                if(iniReadString(file, spec, "AccessRequirements", session->req.ars,str)==str)
        !          2532:                                                        SAFECOPY(session->req.ars,str);
        !          2533:                                                if(iniReadString(file, spec, "Realm", scfg.sys_name,str)==str) {
        !          2534:                                                        FREE_AND_NULL(session->req.realm);
        !          2535:                                                        /* FREE()d in close_request() */
        !          2536:                                                        session->req.realm=strdup(str);
        !          2537:                                                }
        !          2538:                                                if(iniReadString(file, spec, "ErrorDirectory", error_dir,str)==str) {
        !          2539:                                                        FREE_AND_NULL(session->req.error_dir);
        !          2540:                                                        prep_dir(root_dir, str, sizeof(str));
        !          2541:                                                        /* FREE()d in close_request() */
        !          2542:                                                        session->req.error_dir=strdup(str);
        !          2543:                                                }
        !          2544:                                                if(iniReadString(file, spec, "CGIDirectory", cgi_dir,str)==str) {
        !          2545:                                                        FREE_AND_NULL(session->req.cgi_dir);
        !          2546:                                                        prep_dir(root_dir, str, sizeof(str));
        !          2547:                                                        /* FREE()d in close_request() */
        !          2548:                                                        session->req.cgi_dir=strdup(str);
        !          2549:                                                        recheck_dynamic=TRUE;
        !          2550:                                                }
        !          2551:                                                session->req.path_info_index=iniReadBool(file, spec, "PathInfoIndex", FALSE);
        !          2552:                                        }
        !          2553:                                        free(spec);
        !          2554:                                }
        !          2555:                                iniFreeStringList(specs);
        !          2556:                                fclose(file);
        !          2557:                                if(session->req.path_info_index)
        !          2558:                                        recheck_dynamic=TRUE;
        !          2559:                        }
        !          2560:                        else  {
        !          2561:                                /* If cannot open webctrl.ars, only allow sysop access */
        !          2562:                                SAFECOPY(session->req.ars,"LEVEL 90");
        !          2563:                                break;
        !          2564:                        }
        !          2565:                        /* Truncate at \r or \n - can use last_slash since I'm done with it.*/
        !          2566:                        truncsp(session->req.ars);
        !          2567:                }
        !          2568:                SAFECOPY(curdir,path);
        !          2569:        }
        !          2570: 
        !          2571:        if(recheck_dynamic) {
        !          2572:                session->req.dynamic=is_dynamic_req(session);
        !          2573:                if(session->req.dynamic)        /* Need to re-copy path here in case of re-checked PathInfoIndex change */
        !          2574:                        SAFECOPY(path,session->req.physical_path);
        !          2575:        }
        !          2576: 
        !          2577:        if(!session->req.dynamic && session->req.extra_path_info[0])
        !          2578:                send404=TRUE;
        !          2579: 
        !          2580:        if(!check_ars(session)) {
        !          2581:                /* No authentication provided */
        !          2582:                sprintf(str,"401 Unauthorized%s%s: Basic realm=\"%s\""
        !          2583:                        ,newline,get_header(HEAD_WWWAUTH),session->req.realm?session->req.realm:scfg.sys_name);
        !          2584:                send_error(session,str);
        !          2585:                return(FALSE);
        !          2586:        }
        !          2587: 
        !          2588:        if(stat(path,&sb) || IS_PATH_DELIM(*(lastchar(path))) || send404) {
        !          2589:                /* OPTIONS requests never return 404 errors (ala Apache) */
        !          2590:                if(session->req.method!=HTTP_OPTIONS) {
        !          2591:                        if(startup->options&WEB_OPT_DEBUG_TX)
        !          2592:                                lprintf(LOG_DEBUG,"%04d 404 - %s does not exist",session->socket,path);
        !          2593:                        strcat(session->req.physical_path,session->req.extra_path_info);
        !          2594:                        strcat(session->req.virtual_path,session->req.extra_path_info);
        !          2595:                        send_error(session,error_404);
        !          2596:                        return(FALSE);
        !          2597:                }
        !          2598:        }
        !          2599:        if(session->req.range_start || session->req.range_end) {
        !          2600:                if(session->req.range_start < 0)
        !          2601:                        session->req.range_start=sb.st_size-session->req.range_start;
        !          2602:                if(session->req.range_end < 0)
        !          2603:                        session->req.range_end=sb.st_size-session->req.range_end;
        !          2604:                if(session->req.range_end >= sb.st_size)
        !          2605:                        session->req.range_end=sb.st_size-1;
        !          2606:                if(session->req.range_end < session->req.range_start || session->req.dynamic) {
        !          2607:                        send_error(session,error_416);
        !          2608:                        return(FALSE);
        !          2609:                }
        !          2610:                if(session->req.range_start < 0 || session->req.range_end < 0) {
        !          2611:                        send_error(session,error_416);
        !          2612:                        return(FALSE);
        !          2613:                }
        !          2614:                if(session->req.range_start >= sb.st_size) {
        !          2615:                        send_error(session,error_416);
        !          2616:                        return(FALSE);
        !          2617:                }
        !          2618:                SAFECOPY(session->req.status,"206 Partial Content");
        !          2619:        }
        !          2620:        SAFECOPY(session->req.physical_path,path);
        !          2621:        add_env(session,"SCRIPT_NAME",session->req.virtual_path);
        !          2622:        add_env(session,"SCRIPT_FILENAME",session->req.physical_path);
        !          2623:        SAFECOPY(str,session->req.virtual_path);
        !          2624:        last_slash=find_last_slash(str);
        !          2625:        if(last_slash!=NULL)
        !          2626:                *(last_slash+1)=0;
        !          2627:        if(*(session->req.extra_path_info))
        !          2628:        {
        !          2629:                sprintf(str,"%s%s",startup->root_dir,session->req.extra_path_info);
        !          2630:                add_env(session,"PATH_TRANSLATED",str);
        !          2631:                add_env(session,"PATH_INFO",session->req.extra_path_info);
        !          2632:        }
        !          2633: 
        !          2634:        return(TRUE);
        !          2635: }
        !          2636: 
        !          2637: static str_list_t get_cgi_env(http_session_t *session)
        !          2638: {
        !          2639:        char            value[INI_MAX_VALUE_LEN+1];
        !          2640:        char*           deflt;
        !          2641:        char            defltbuf[INI_MAX_VALUE_LEN+1];
        !          2642:        char            append[INI_MAX_VALUE_LEN+1];
        !          2643:        char            prepend[INI_MAX_VALUE_LEN+1];
        !          2644:        char            env_str[(INI_MAX_VALUE_LEN*4)+2];
        !          2645:        FILE*           fp;
        !          2646:        size_t          i;
        !          2647:        str_list_t      env_list;
        !          2648:        str_list_t      add_list;
        !          2649: 
        !          2650:        /* Return value */
        !          2651:        if((env_list=strListInit())==NULL)
        !          2652:                return(NULL);
        !          2653: 
        !          2654:        strListAppendList(&env_list, session->req.cgi_env);
        !          2655: 
        !          2656:        strListPush(&env_list,"REDIRECT_STATUS=200");   /* Kludge for php-cgi */
        !          2657: 
        !          2658:        if((fp=iniOpenFile(cgi_env_ini,/* create? */FALSE))==NULL)
        !          2659:                return(env_list);
        !          2660: 
        !          2661:        /* FREE()d in this block */
        !          2662:        if((add_list=iniReadSectionList(fp,NULL))!=NULL) {
        !          2663: 
        !          2664:                for(i=0; add_list[i]!=NULL; i++) {
        !          2665:                        if((deflt=getenv(add_list[i]))==NULL)
        !          2666:                                deflt=iniReadString(fp,add_list[i],"default",NULL,defltbuf);
        !          2667:                        if(iniReadString(fp,add_list[i],"value",deflt,value)==NULL)
        !          2668:                                continue;
        !          2669:                        iniReadString(fp,add_list[i],"append","",append);
        !          2670:                        iniReadString(fp,add_list[i],"prepend","",prepend);
        !          2671:                        safe_snprintf(env_str,sizeof(env_str),"%s=%s%s%s"
        !          2672:                                ,add_list[i], prepend, value, append);
        !          2673:                        strListPush(&env_list,env_str);
        !          2674:                }
        !          2675:                iniFreeStringList(add_list);
        !          2676:        }
        !          2677: 
        !          2678:        fclose(fp);
        !          2679: 
        !          2680:        return(env_list);
        !          2681: }
        !          2682: 
        !          2683: 
        !          2684: static BOOL exec_cgi(http_session_t *session)
        !          2685: {
        !          2686: #ifdef __unix__
        !          2687:        char    cmdline[MAX_PATH+256];
        !          2688:        /* ToDo: Damn, that's WAY too many variables */
        !          2689:        int             i=0;
        !          2690:        int             j;
        !          2691:        int             status=0;
        !          2692:        pid_t   child=0;
        !          2693:        int             out_pipe[2];
        !          2694:        int             err_pipe[2];
        !          2695:        struct timeval tv={0,0};
        !          2696:        fd_set  read_set;
        !          2697:        fd_set  write_set;
        !          2698:        int             high_fd=0;
        !          2699:        char    buf[1024];
        !          2700:        char    fbuf[1026];
        !          2701:        BOOL    done_parsing_headers=FALSE;
        !          2702:        BOOL    done_reading=FALSE;
        !          2703:        char    cgi_status[MAX_REQUEST_LINE+1];
        !          2704:        char    header[MAX_REQUEST_LINE+1];
        !          2705:        char    *directive=NULL;
        !          2706:        char    *value=NULL;
        !          2707:        char    *last;
        !          2708:        BOOL    done_wait=FALSE;
        !          2709:        BOOL    got_valid_headers=FALSE;
        !          2710:        time_t  start;
        !          2711:        char    cgipath[MAX_PATH+1];
        !          2712:        char    *p;
        !          2713:        char    ch;
        !          2714:        BOOL    orig_keep=FALSE;
        !          2715:        size_t  idx;
        !          2716:        str_list_t      tmpbuf;
        !          2717:        size_t  tmpbuflen=0;
        !          2718:        BOOL    no_chunked=FALSE;
        !          2719:        BOOL    set_chunked=FALSE;
        !          2720: 
        !          2721:        SAFECOPY(cmdline,session->req.physical_path);
        !          2722: 
        !          2723:        lprintf(LOG_INFO,"%04d Executing CGI: %s",session->socket,cmdline);
        !          2724: 
        !          2725:        orig_keep=session->req.keep_alive;
        !          2726:        session->req.keep_alive=FALSE;
        !          2727: 
        !          2728:        /* Set up I/O pipes */
        !          2729: 
        !          2730:        if(pipe(out_pipe)!=0) {
        !          2731:                lprintf(LOG_ERR,"%04d Can't create out_pipe",session->socket);
        !          2732:                return(FALSE);
        !          2733:        }
        !          2734: 
        !          2735:        if(pipe(err_pipe)!=0) {
        !          2736:                lprintf(LOG_ERR,"%04d Can't create err_pipe",session->socket);
        !          2737:                return(FALSE);
        !          2738:        }
        !          2739: 
        !          2740:        if((child=fork())==0)  {
        !          2741:                str_list_t  env_list;
        !          2742: 
        !          2743:                /* Do a full suid thing. */
        !          2744:                if(startup->setuid!=NULL)
        !          2745:                        startup->setuid(TRUE);
        !          2746: 
        !          2747:                env_list=get_cgi_env(session);
        !          2748: 
        !          2749:                /* Set up STDIO */
        !          2750:                dup2(session->socket,0);                /* redirect stdin */
        !          2751:                close(out_pipe[0]);             /* close read-end of pipe */
        !          2752:                dup2(out_pipe[1],1);    /* stdout */
        !          2753:                close(out_pipe[1]);             /* close excess file descriptor */
        !          2754:                close(err_pipe[0]);             /* close read-end of pipe */
        !          2755:                dup2(err_pipe[1],2);    /* stderr */
        !          2756:                close(err_pipe[1]);             /* close excess file descriptor */
        !          2757: 
        !          2758:                SAFECOPY(cgipath,cmdline);
        !          2759:                if((p=strrchr(cgipath,'/'))!=NULL)
        !          2760:                {
        !          2761:                        *p=0;
        !          2762:                        chdir(cgipath);
        !          2763:                }
        !          2764: 
        !          2765:                /* Execute command */
        !          2766:                if(get_cgi_handler(cgipath, sizeof(cgipath))) {
        !          2767:                        char* shell=os_cmdshell();
        !          2768:                        lprintf(LOG_INFO,"%04d Using handler %s to execute %s",session->socket,cgipath,cmdline);
        !          2769:                        execle(shell,shell,"-c",cgipath,NULL,env_list);
        !          2770:                }
        !          2771:                else {
        !          2772:                        execle(cmdline,cmdline,NULL,env_list);
        !          2773:                }
        !          2774: 
        !          2775:                lprintf(LOG_ERR,"%04d !FAILED! execle() (%d)",session->socket,errno);
        !          2776:                exit(EXIT_FAILURE); /* Should never happen */
        !          2777:        }
        !          2778: 
        !          2779:        if(child==-1)  {
        !          2780:                lprintf(LOG_ERR,"%04d !FAILED! fork() errno=%d",session->socket,errno);
        !          2781:                close(out_pipe[0]);             /* close read-end of pipe */
        !          2782:                close(err_pipe[0]);             /* close read-end of pipe */
        !          2783:        }
        !          2784: 
        !          2785:        close(out_pipe[1]);             /* close excess file descriptor */
        !          2786:        close(err_pipe[1]);             /* close excess file descriptor */
        !          2787: 
        !          2788:        if(child==-1)
        !          2789:                return(FALSE);
        !          2790: 
        !          2791:        start=time(NULL);
        !          2792: 
        !          2793:        high_fd=out_pipe[0];
        !          2794:        if(err_pipe[0]>high_fd)
        !          2795:                high_fd=err_pipe[0];
        !          2796: 
        !          2797:        /* ToDo: Magically set done_parsing_headers for nph-* scripts */
        !          2798:        cgi_status[0]=0;
        !          2799:        /* FREE()d following this block */
        !          2800:        tmpbuf=strListInit();
        !          2801:        while(!done_reading)  {
        !          2802:                tv.tv_sec=startup->max_cgi_inactivity;
        !          2803:                tv.tv_usec=0;
        !          2804: 
        !          2805:                FD_ZERO(&read_set);
        !          2806:                FD_SET(out_pipe[0],&read_set);
        !          2807:                FD_SET(err_pipe[0],&read_set);
        !          2808:                FD_ZERO(&write_set);
        !          2809: 
        !          2810:                if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0)  {
        !          2811:                        if(FD_ISSET(out_pipe[0],&read_set))  {
        !          2812:                                if(done_parsing_headers && got_valid_headers)  {
        !          2813:                                        i=read(out_pipe[0],buf,sizeof(buf));
        !          2814:                                        if(i!=-1 && i!=0)  {
        !          2815:                                                int snt=0;
        !          2816:                                                start=time(NULL);
        !          2817:                                                if(session->req.method!=HTTP_HEAD) {
        !          2818:                                                        snt=writebuf(session,buf,i);
        !          2819:                                                        if(session->req.ld!=NULL) {
        !          2820:                                                                session->req.ld->size+=snt;
        !          2821:                                                        }
        !          2822:                                                }
        !          2823:                                        }
        !          2824:                                        else
        !          2825:                                                done_reading=TRUE;
        !          2826:                                }
        !          2827:                                else  {
        !          2828:                                        /* This is the tricky part */
        !          2829:                                        i=pipereadline(out_pipe[0],buf,sizeof(buf), fbuf, sizeof(fbuf));
        !          2830:                                        if(i==-1)  {
        !          2831:                                                done_reading=TRUE;
        !          2832:                                                got_valid_headers=FALSE;
        !          2833:                                        }
        !          2834:                                        else
        !          2835:                                                start=time(NULL);
        !          2836: 
        !          2837:                                        if(!done_parsing_headers && *buf)  {
        !          2838:                                                if(tmpbuf != NULL)
        !          2839:                                                        strListPush(&tmpbuf, fbuf);
        !          2840:                                                SAFECOPY(header,buf);
        !          2841:                                                directive=strtok_r(header,":",&last);
        !          2842:                                                if(directive != NULL)  {
        !          2843:                                                        value=strtok_r(NULL,"",&last);
        !          2844:                                                        i=get_header_type(directive);
        !          2845:                                                        switch (i)  {
        !          2846:                                                                case HEAD_LOCATION:
        !          2847:                                                                        got_valid_headers=TRUE;
        !          2848:                                                                        if(*value=='/')  {
        !          2849:                                                                                unescape(value);
        !          2850:                                                                                SAFECOPY(session->req.virtual_path,value);
        !          2851:                                                                                session->req.send_location=MOVED_STAT;
        !          2852:                                                                                if(cgi_status[0]==0)
        !          2853:                                                                                        SAFECOPY(cgi_status,error_302);
        !          2854:                                                                        } else  {
        !          2855:                                                                                SAFECOPY(session->req.virtual_path,value);
        !          2856:                                                                                session->req.send_location=MOVED_TEMP;
        !          2857:                                                                                if(cgi_status[0]==0)
        !          2858:                                                                                        SAFECOPY(cgi_status,error_302);
        !          2859:                                                                        }
        !          2860:                                                                        break;
        !          2861:                                                                case HEAD_STATUS:
        !          2862:                                                                        SAFECOPY(cgi_status,value);
        !          2863:                                                                        break;
        !          2864:                                                                case HEAD_LENGTH:
        !          2865:                                                                        session->req.keep_alive=orig_keep;
        !          2866:                                                                        strListPush(&session->req.dynamic_heads,buf);
        !          2867:                                                                        no_chunked=TRUE;
        !          2868:                                                                        break;
        !          2869:                                                                case HEAD_TYPE:
        !          2870:                                                                        got_valid_headers=TRUE;
        !          2871:                                                                        strListPush(&session->req.dynamic_heads,buf);
        !          2872:                                                                        break;
        !          2873:                                                                case HEAD_TRANSFER_ENCODING:
        !          2874:                                                                        no_chunked=TRUE;
        !          2875:                                                                        break;
        !          2876:                                                                default:
        !          2877:                                                                        strListPush(&session->req.dynamic_heads,buf);
        !          2878:                                                        }
        !          2879:                                                }
        !          2880:                                                if(directive == NULL || value == NULL) {
        !          2881:                                                        /* Invalid header line */
        !          2882:                                                        done_parsing_headers=TRUE;
        !          2883:                                                }
        !          2884:                                        }
        !          2885:                                        else  {
        !          2886:                                                if(!no_chunked && session->http_ver>=HTTP_1_1) {
        !          2887:                                                        session->req.keep_alive=orig_keep;
        !          2888:                                                        set_chunked=TRUE;
        !          2889:                                                }
        !          2890:                                                if(got_valid_headers)  {
        !          2891:                                                        session->req.dynamic=IS_CGI;
        !          2892:                                                        if(cgi_status[0]==0)
        !          2893:                                                                SAFECOPY(cgi_status,session->req.status);
        !          2894:                                                        send_headers(session,cgi_status,set_chunked);
        !          2895:                                                }
        !          2896:                                                else {
        !          2897:                                                        /* Invalid headers... send 'er all as plain-text */
        !          2898:                                                        char    content_type[MAX_REQUEST_LINE+1];
        !          2899:                                                        int snt;
        !          2900: 
        !          2901:                                                        lprintf(LOG_DEBUG,"%04d Recieved invalid CGI headers, sending result as plain-text",session->socket);
        !          2902: 
        !          2903:                                                        /* free() the non-headers so they don't get sent, then recreate the list */
        !          2904:                                                        strListFreeStrings(session->req.dynamic_heads);
        !          2905: 
        !          2906:                                                        /* Copy current status */
        !          2907:                                                        SAFECOPY(cgi_status,session->req.status);
        !          2908: 
        !          2909:                                                        /* Add the content-type header (REQUIRED) */
        !          2910:                                                        SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content);
        !          2911:                                                        strListPush(&session->req.dynamic_heads,content_type);
        !          2912:                                                        send_headers(session,cgi_status,FALSE);
        !          2913: 
        !          2914:                                                        /* Now send the tmpbuf */
        !          2915:                                                        for(i=0; tmpbuf != NULL && tmpbuf[i] != NULL; i++) {
        !          2916:                                                                if(strlen(tmpbuf[i])>0) {
        !          2917:                                                                        snt=writebuf(session,tmpbuf[i],strlen(tmpbuf[i]));
        !          2918:                                                                        if(session->req.ld!=NULL) {
        !          2919:                                                                                session->req.ld->size+=snt;
        !          2920:                                                                        }
        !          2921:                                                                }
        !          2922:                                                        }
        !          2923:                                                        if(strlen(fbuf)>0) {
        !          2924:                                                                snt=writebuf(session,fbuf,strlen(fbuf));
        !          2925:                                                                if(session->req.ld!=NULL && snt>0) {
        !          2926:                                                                        session->req.ld->size+=snt;
        !          2927:                                                                }
        !          2928:                                                        }
        !          2929:                                                        got_valid_headers=TRUE;
        !          2930:                                                }
        !          2931:                                                done_parsing_headers=TRUE;
        !          2932:                                        }
        !          2933:                                }
        !          2934:                        }
        !          2935:                        if(FD_ISSET(err_pipe[0],&read_set))  {
        !          2936:                                i=read(err_pipe[0],buf,sizeof(buf)-1);
        !          2937:                                if(i>0) {
        !          2938:                                        buf[i]=0;
        !          2939:                                        lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
        !          2940:                                        start=time(NULL);
        !          2941:                                }
        !          2942:                        }
        !          2943:                        if(!done_wait)
        !          2944:                                done_wait = (waitpid(child,&status,WNOHANG)==child);
        !          2945:                        if(!FD_ISSET(err_pipe[0],&read_set) && !FD_ISSET(out_pipe[0],&read_set) && done_wait)
        !          2946:                                done_reading=TRUE;
        !          2947:                }
        !          2948:                else  {
        !          2949:                        if((time(NULL)-start) >= startup->max_cgi_inactivity)  {
        !          2950:                                lprintf(LOG_ERR,"%04d CGI Process %s Timed out",session->socket,getfname(cmdline));
        !          2951:                                done_reading=TRUE;
        !          2952:                                start=0;
        !          2953:                        }
        !          2954:                }
        !          2955:        }
        !          2956: 
        !          2957:        if(tmpbuf != NULL)
        !          2958:                strListFree(&tmpbuf);
        !          2959: 
        !          2960:        if(!done_wait)
        !          2961:                done_wait = (waitpid(child,&status,WNOHANG)==child);
        !          2962:        if(!done_wait)  {
        !          2963:                if(start)
        !          2964:                        lprintf(LOG_NOTICE,"%04d CGI Process %s still alive on client exit"
        !          2965:                                ,session->socket,getfname(cmdline));
        !          2966:                kill(child,SIGTERM);
        !          2967:                mswait(1000);
        !          2968:                done_wait = (waitpid(child,&status,WNOHANG)==child);
        !          2969:                if(!done_wait)  {
        !          2970:                        kill(child,SIGKILL);
        !          2971:                        done_wait = (waitpid(child,&status,0)==child);
        !          2972:                }
        !          2973:        }
        !          2974: 
        !          2975:        /* Drain STDERR & STDOUT */     
        !          2976:        tv.tv_sec=1;
        !          2977:        tv.tv_usec=0;
        !          2978:        FD_ZERO(&read_set);
        !          2979:        FD_SET(err_pipe[0],&read_set);
        !          2980:        FD_SET(out_pipe[0],&read_set);
        !          2981:        while(select(high_fd+1,&read_set,NULL,NULL,&tv)>0) {
        !          2982:                if(FD_ISSET(err_pipe[0],&read_set)) {
        !          2983:                        i=read(err_pipe[0],buf,sizeof(buf)-1);
        !          2984:                        if(i!=-1 && i!=0) {
        !          2985:                                buf[i]=0;
        !          2986:                                lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
        !          2987:                                start=time(NULL);
        !          2988:                        }
        !          2989:                }
        !          2990: 
        !          2991:                if(FD_ISSET(out_pipe[0],&read_set))  {
        !          2992:                        i=read(out_pipe[0],buf,sizeof(buf));
        !          2993:                        if(i!=-1 && i!=0)  {
        !          2994:                                int snt=0;
        !          2995:                                start=time(NULL);
        !          2996:                                if(session->req.method!=HTTP_HEAD) {
        !          2997:                                        snt=writebuf(session,buf,i);
        !          2998:                                        if(session->req.ld!=NULL) {
        !          2999:                                                session->req.ld->size+=snt;
        !          3000:                                        }
        !          3001:                                }
        !          3002:                        }
        !          3003:                }
        !          3004: 
        !          3005:                if(i==0 || i==-1)
        !          3006:                        break;
        !          3007: 
        !          3008:                tv.tv_sec=1;
        !          3009:                tv.tv_usec=0;
        !          3010:                FD_ZERO(&read_set);
        !          3011:                FD_SET(err_pipe[0],&read_set);
        !          3012:                FD_SET(out_pipe[0],&read_set);
        !          3013:        }
        !          3014: 
        !          3015:        close(out_pipe[0]);             /* close read-end of pipe */
        !          3016:        close(err_pipe[0]);             /* close read-end of pipe */
        !          3017:        if(!got_valid_headers) {
        !          3018:                lprintf(LOG_ERR,"%04d CGI Process %s did not generate valid headers"
        !          3019:                        ,session->socket,getfname(cmdline));
        !          3020:                return(FALSE);
        !          3021:        }
        !          3022: 
        !          3023:        if(!done_parsing_headers) {
        !          3024:                lprintf(LOG_ERR,"%04d CGI Process %s did not send data header termination"
        !          3025:                        ,session->socket,getfname(cmdline));
        !          3026:                return(FALSE);
        !          3027:        }
        !          3028: 
        !          3029:        return(TRUE);
        !          3030: #else
        !          3031:        /* Win32 exec_cgi() */
        !          3032: 
        !          3033:        /* These are (more or less) copied from the Unix version */
        !          3034:        char*   p;
        !          3035:        char    *last;
        !          3036:        char    cmdline[MAX_PATH+256];
        !          3037:        char    buf[4096];
        !          3038:        int             i;
        !          3039:        BOOL    orig_keep;
        !          3040:        BOOL    done_parsing_headers=FALSE;
        !          3041:        BOOL    got_valid_headers=FALSE;
        !          3042:        char    cgi_status[MAX_REQUEST_LINE+1];
        !          3043:        char    content_type[MAX_REQUEST_LINE+1];
        !          3044:        char    header[MAX_REQUEST_LINE+1];
        !          3045:        char    *directive=NULL;
        !          3046:        char    *value=NULL;
        !          3047:        time_t  start;
        !          3048:        BOOL    no_chunked=FALSE;
        !          3049:        int             set_chunked=FALSE;
        !          3050: 
        !          3051:        /* Win32-specific */
        !          3052:        char*   env_block;
        !          3053:        char    startup_dir[MAX_PATH+1];
        !          3054:        int             wr;
        !          3055:        HANDLE  rdpipe=INVALID_HANDLE_VALUE;
        !          3056:        HANDLE  wrpipe=INVALID_HANDLE_VALUE;
        !          3057:        HANDLE  rdoutpipe;
        !          3058:        HANDLE  wrinpipe;
        !          3059:        DWORD   waiting;
        !          3060:        DWORD   msglen;
        !          3061:        DWORD   retval;
        !          3062:        BOOL    success;
        !          3063:        BOOL    process_terminated=FALSE;
        !          3064:     PROCESS_INFORMATION process_info;
        !          3065:        SECURITY_ATTRIBUTES sa;
        !          3066:     STARTUPINFO startup_info={0};
        !          3067:        str_list_t      env_list;
        !          3068: 
        !          3069:     startup_info.cb=sizeof(startup_info);
        !          3070:        startup_info.dwFlags|=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
        !          3071:     startup_info.wShowWindow=SW_HIDE;
        !          3072: 
        !          3073:        SAFECOPY(cmdline,session->req.physical_path);
        !          3074: 
        !          3075:        SAFECOPY(startup_dir,cmdline);
        !          3076:        if((p=strrchr(startup_dir,'/'))!=NULL || (p=strrchr(startup_dir,'\\'))!=NULL)
        !          3077:                *p=0;
        !          3078:        else
        !          3079:                SAFECOPY(startup_dir,session->req.cgi_dir?session->req.cgi_dir:cgi_dir);
        !          3080: 
        !          3081:        lprintf(LOG_DEBUG,"%04d CGI startup dir: %s", session->socket, startup_dir);
        !          3082: 
        !          3083:        get_cgi_handler(cmdline, sizeof(cmdline));
        !          3084: 
        !          3085:        lprintf(LOG_INFO,"%04d Executing CGI: %s",session->socket,cmdline);
        !          3086: 
        !          3087:        orig_keep=session->req.keep_alive;
        !          3088:        session->req.keep_alive=FALSE;
        !          3089: 
        !          3090:        memset(&sa,0,sizeof(sa));
        !          3091:        sa.nLength= sizeof(SECURITY_ATTRIBUTES);
        !          3092:        sa.lpSecurityDescriptor = NULL;
        !          3093:        sa.bInheritHandle = TRUE;
        !          3094: 
        !          3095:        /* Create the child output pipe (override default 4K buffer size) */
        !          3096:        if(!CreatePipe(&rdoutpipe,&startup_info.hStdOutput,&sa,sizeof(buf))) {
        !          3097:                lprintf(LOG_ERR,"%04d !ERROR %d creating stdout pipe",session->socket,GetLastError());
        !          3098:                return(FALSE);
        !          3099:        }
        !          3100:        startup_info.hStdError=startup_info.hStdOutput;
        !          3101: 
        !          3102:        /* Create the child input pipe. */
        !          3103:        if(!CreatePipe(&startup_info.hStdInput,&wrinpipe,&sa,0 /* default buffer size */)) {
        !          3104:                lprintf(LOG_ERR,"%04d !ERROR %d creating stdin pipe",session->socket,GetLastError());
        !          3105:                return(FALSE);
        !          3106:        }
        !          3107: 
        !          3108:        DuplicateHandle(
        !          3109:                GetCurrentProcess(), rdoutpipe,
        !          3110:                GetCurrentProcess(), &rdpipe, 0, FALSE, DUPLICATE_SAME_ACCESS);
        !          3111: 
        !          3112:        DuplicateHandle(
        !          3113:                GetCurrentProcess(), wrinpipe,
        !          3114:                GetCurrentProcess(), &wrpipe, 0, FALSE, DUPLICATE_SAME_ACCESS);
        !          3115: 
        !          3116:        CloseHandle(rdoutpipe);
        !          3117:        CloseHandle(wrinpipe);
        !          3118: 
        !          3119:        env_list=get_cgi_env(session);
        !          3120:        env_block = strListCreateBlock(env_list);
        !          3121:        strListFree(&env_list);
        !          3122: 
        !          3123:     success=CreateProcess(
        !          3124:                NULL,                   /* pointer to name of executable module */
        !          3125:                cmdline,                /* pointer to command line string */
        !          3126:                NULL,                   /* process security attributes */
        !          3127:                NULL,                   /* thread security attributes */
        !          3128:                TRUE,                   /* handle inheritance flag */
        !          3129:                CREATE_NEW_CONSOLE, /* creation flags */
        !          3130:         env_block,     /* pointer to new environment block */
        !          3131:                startup_dir,    /* pointer to current directory name */
        !          3132:                &startup_info,  /* pointer to STARTUPINFO */
        !          3133:                &process_info   /* pointer to PROCESS_INFORMATION */
        !          3134:                );
        !          3135: 
        !          3136:        strListFreeBlock(env_block);
        !          3137:        
        !          3138:        if(!success) {
        !          3139:                lprintf(LOG_ERR,"%04d !ERROR %d running %s",session->socket,GetLastError(),cmdline);
        !          3140:                return(FALSE);
        !          3141:     }
        !          3142: 
        !          3143:        start=time(NULL);
        !          3144: 
        !          3145:        SAFECOPY(cgi_status,session->req.status);
        !          3146:        SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content);
        !          3147:        while(server_socket!=INVALID_SOCKET) {
        !          3148: 
        !          3149:                if(WaitForSingleObject(process_info.hProcess,0)==WAIT_OBJECT_0)
        !          3150:                        process_terminated=TRUE;        /* handle remaining data in pipe before breaking */
        !          3151: 
        !          3152:                if((time(NULL)-start) >= startup->max_cgi_inactivity)  {
        !          3153:                        lprintf(LOG_WARNING,"%04d CGI Process %s timed out after %u seconds of inactivity"
        !          3154:                                ,session->socket,getfname(cmdline),startup->max_cgi_inactivity);
        !          3155:                        break;
        !          3156:                }
        !          3157: 
        !          3158:                waiting = 0;
        !          3159:                PeekNamedPipe(
        !          3160:                        rdpipe,             /* handle to pipe to copy from */
        !          3161:                        NULL,               /* pointer to data buffer */
        !          3162:                        0,                                      /* size, in bytes, of data buffer */
        !          3163:                        NULL,                           /* pointer to number of bytes read */
        !          3164:                        &waiting,                       /* pointer to total number of bytes available */
        !          3165:                        NULL                            /* pointer to unread bytes in this message */
        !          3166:                        );
        !          3167:                if(!waiting) {
        !          3168:                        if(process_terminated)
        !          3169:                                break;
        !          3170:                        Sleep(1);
        !          3171:                        continue;
        !          3172:                }
        !          3173:                /* reset inactivity timer */
        !          3174:                start=time(NULL);       
        !          3175: 
        !          3176:                msglen=0;
        !          3177:                if(done_parsing_headers) {
        !          3178:                        if(ReadFile(rdpipe,buf,sizeof(buf),&msglen,NULL)==FALSE) {
        !          3179:                                lprintf(LOG_ERR,"%04d !ERROR %d reading from pipe"
        !          3180:                                        ,session->socket,GetLastError());
        !          3181:                                break;
        !          3182:                        }
        !          3183:                }
        !          3184:                else  {
        !          3185:                        /* This is the tricky part */
        !          3186:                        buf[0]=0;
        !          3187:                        i=pipereadline(rdpipe,buf,sizeof(buf),NULL,0);
        !          3188:                        if(i<0)  {
        !          3189:                                lprintf(LOG_WARNING,"%04d CGI pipereadline returned %d",session->socket,i);
        !          3190:                                got_valid_headers=FALSE;
        !          3191:                                break;
        !          3192:                        }
        !          3193:                        lprintf(LOG_DEBUG,"%04d CGI header line: %s"
        !          3194:                                ,session->socket, buf);
        !          3195:                        SAFECOPY(header,buf);
        !          3196:                        if(strchr(header,':')!=NULL) {
        !          3197:                                if((directive=strtok_r(header,":",&last))!=NULL)
        !          3198:                                        value=strtok_r(NULL,"",&last);
        !          3199:                                else
        !          3200:                                        value="";
        !          3201:                                i=get_header_type(directive);
        !          3202:                                switch (i)  {
        !          3203:                                        case HEAD_LOCATION:
        !          3204:                                                got_valid_headers=TRUE;
        !          3205:                                                if(*value=='/')  {
        !          3206:                                                        unescape(value);
        !          3207:                                                        SAFECOPY(session->req.virtual_path,value);
        !          3208:                                                        session->req.send_location=MOVED_STAT;
        !          3209:                                                        if(cgi_status[0]==0)
        !          3210:                                                                SAFECOPY(cgi_status,error_302);
        !          3211:                                                } else  {
        !          3212:                                                        SAFECOPY(session->req.virtual_path,value);
        !          3213:                                                        session->req.send_location=MOVED_TEMP;
        !          3214:                                                        if(cgi_status[0]==0)
        !          3215:                                                                SAFECOPY(cgi_status,error_302);
        !          3216:                                                }
        !          3217:                                                break;
        !          3218:                                        case HEAD_STATUS:
        !          3219:                                                SAFECOPY(cgi_status,value);
        !          3220:                                                break;
        !          3221:                                        case HEAD_LENGTH:
        !          3222:                                                session->req.keep_alive=orig_keep;
        !          3223:                                                strListPush(&session->req.dynamic_heads,buf);
        !          3224:                                                no_chunked=TRUE;
        !          3225:                                                break;
        !          3226:                                        case HEAD_TYPE:
        !          3227:                                                got_valid_headers=TRUE;
        !          3228:                                                SAFECOPY(content_type,buf);
        !          3229:                                                break;
        !          3230:                                        case HEAD_TRANSFER_ENCODING:
        !          3231:                                                no_chunked=TRUE;
        !          3232:                                                break;
        !          3233:                                        default:
        !          3234:                                                strListPush(&session->req.dynamic_heads,buf);
        !          3235:                                }
        !          3236:                                continue;
        !          3237:                        }
        !          3238:                        if(i) {
        !          3239:                                strcat(buf,"\r\n");     /* Add back the missing line terminator */
        !          3240:                                msglen=strlen(buf);     /* we will send this text later */
        !          3241:                        }
        !          3242:                        done_parsing_headers = TRUE;    /* invalid header */
        !          3243:                        session->req.dynamic=IS_CGI;
        !          3244:                        if(!no_chunked && session->http_ver>=HTTP_1_1) {
        !          3245:                                session->req.keep_alive=orig_keep;
        !          3246:                                set_chunked=TRUE;
        !          3247:                        }
        !          3248:                        strListPush(&session->req.dynamic_heads,content_type);
        !          3249:                        send_headers(session,cgi_status,set_chunked);
        !          3250:                }
        !          3251:                if(msglen) {
        !          3252:                        lprintf(LOG_DEBUG,"%04d Sending %d bytes: %.*s"
        !          3253:                                ,session->socket,msglen,msglen,buf);
        !          3254:                        wr=writebuf(session,buf,msglen);
        !          3255:                        /* log actual bytes sent */
        !          3256:                        if(session->req.ld!=NULL && wr>0)
        !          3257:                                session->req.ld->size+=wr;      
        !          3258:                }
        !          3259:        }
        !          3260: 
        !          3261:     if(GetExitCodeProcess(process_info.hProcess, &retval)==FALSE)
        !          3262:            lprintf(LOG_ERR,"%04d !ERROR GetExitCodeProcess(%s) returned %d"
        !          3263:                        ,session->socket,getfname(cmdline),GetLastError());
        !          3264: 
        !          3265:        if(retval==STILL_ACTIVE) {
        !          3266:                lprintf(LOG_WARNING,"%04d Terminating CGI process: %s"
        !          3267:                        ,session->socket,getfname(cmdline));
        !          3268:                TerminateProcess(process_info.hProcess, GetLastError());
        !          3269:        }       
        !          3270: 
        !          3271:        if(rdpipe!=INVALID_HANDLE_VALUE)
        !          3272:                CloseHandle(rdpipe);
        !          3273:        if(wrpipe!=INVALID_HANDLE_VALUE)
        !          3274:                CloseHandle(wrpipe);
        !          3275:        CloseHandle(process_info.hProcess);
        !          3276: 
        !          3277:        if(!got_valid_headers)
        !          3278:                lprintf(LOG_WARNING,"%04d !CGI Process %s did not generate valid headers"
        !          3279:                        ,session->socket,getfname(cmdline));
        !          3280:        
        !          3281:        if(!done_parsing_headers)
        !          3282:                lprintf(LOG_WARNING,"%04d !CGI Process %s did not send data header termination"
        !          3283:                        ,session->socket,getfname(cmdline));
        !          3284: 
        !          3285:        return(TRUE);
        !          3286: #endif
        !          3287: }
        !          3288: 
        !          3289: /********************/
        !          3290: /* JavaScript stuff */
        !          3291: /********************/
        !          3292: 
        !          3293: JSObject* DLLCALL js_CreateHttpReplyObject(JSContext* cx
        !          3294:                                                                                   ,JSObject* parent, http_session_t *session)
        !          3295: {
        !          3296:        JSObject*       reply;
        !          3297:        JSObject*       headers;
        !          3298:        jsval           val;
        !          3299:        JSString*       js_str;
        !          3300:        
        !          3301:        /* Return existing object if it's already been created */
        !          3302:        if(JS_GetProperty(cx,parent,"http_reply",&val) && val!=JSVAL_VOID)  {
        !          3303:                reply = JSVAL_TO_OBJECT(val);
        !          3304:                JS_ClearScope(cx,reply);
        !          3305:        }
        !          3306:        else
        !          3307:                reply = JS_DefineObject(cx, parent, "http_reply", NULL
        !          3308:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
        !          3309: 
        !          3310:        if((js_str=JS_NewStringCopyZ(cx, session->req.status))==NULL)
        !          3311:                return(FALSE);
        !          3312:        JS_DefineProperty(cx, reply, "status", STRING_TO_JSVAL(js_str)
        !          3313:                ,NULL,NULL,JSPROP_ENUMERATE);
        !          3314: 
        !          3315:        /* Return existing object if it's already been created */
        !          3316:        if(JS_GetProperty(cx,reply,"header",&val) && val!=JSVAL_VOID)  {
        !          3317:                headers = JSVAL_TO_OBJECT(val);
        !          3318:                JS_ClearScope(cx,headers);
        !          3319:        }
        !          3320:        else
        !          3321:                headers = JS_DefineObject(cx, reply, "header", NULL
        !          3322:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
        !          3323: 
        !          3324:        if((js_str=JS_NewStringCopyZ(cx, "text/html"))==NULL)
        !          3325:                return(FALSE);
        !          3326:        JS_DefineProperty(cx, headers, "Content-Type", STRING_TO_JSVAL(js_str)
        !          3327:                ,NULL,NULL,JSPROP_ENUMERATE);
        !          3328: 
        !          3329:        return(reply);
        !          3330: }
        !          3331: 
        !          3332: JSObject* DLLCALL js_CreateHttpRequestObject(JSContext* cx
        !          3333:                                                                                         ,JSObject* parent, http_session_t *session)
        !          3334: {
        !          3335: /*     JSObject*       cookie; */
        !          3336:        jsval           val;
        !          3337: 
        !          3338:        /* Return existing object if it's already been created */
        !          3339:        if(JS_GetProperty(cx,parent,"http_request",&val) && val!=JSVAL_VOID)  {
        !          3340:                session->js_request=JSVAL_TO_OBJECT(val);
        !          3341:        }
        !          3342:        else
        !          3343:                session->js_request = JS_DefineObject(cx, parent, "http_request", NULL
        !          3344:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
        !          3345: 
        !          3346:        js_add_request_prop(session,"path_info",session->req.extra_path_info);
        !          3347:        js_add_request_prop(session,"method",methods[session->req.method]);
        !          3348:        js_add_request_prop(session,"virtual_path",session->req.virtual_path);
        !          3349: 
        !          3350:        /* Return existing object if it's already been created */
        !          3351:        if(JS_GetProperty(cx,session->js_request,"query",&val) && val!=JSVAL_VOID)  {
        !          3352:                session->js_query = JSVAL_TO_OBJECT(val);
        !          3353:                JS_ClearScope(cx,session->js_query);
        !          3354:        }
        !          3355:        else
        !          3356:                session->js_query = JS_DefineObject(cx, session->js_request, "query", NULL
        !          3357:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
        !          3358: 
        !          3359:        /* Return existing object if it's already been created */
        !          3360:        if(JS_GetProperty(cx,session->js_request,"header",&val) && val!=JSVAL_VOID)  {
        !          3361:                session->js_header = JSVAL_TO_OBJECT(val);
        !          3362:                JS_ClearScope(cx,session->js_header);
        !          3363:        }
        !          3364:        else
        !          3365:                session->js_header = JS_DefineObject(cx, session->js_request, "header", NULL
        !          3366:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
        !          3367: 
        !          3368:        /* Return existing object if it's already been created */
        !          3369:        if(JS_GetProperty(cx,session->js_request,"cookie",&val) && val!=JSVAL_VOID)  {
        !          3370:                session->js_cookie = JSVAL_TO_OBJECT(val);
        !          3371:                JS_ClearScope(cx,session->js_cookie);
        !          3372:        }
        !          3373:        else
        !          3374:                session->js_cookie = JS_DefineObject(cx, session->js_request, "cookie", NULL
        !          3375:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
        !          3376: 
        !          3377: 
        !          3378:        return(session->js_request);
        !          3379: }
        !          3380: 
        !          3381: static void
        !          3382: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
        !          3383: {
        !          3384:        char    line[64];
        !          3385:        char    file[MAX_PATH+1];
        !          3386:        char*   warning;
        !          3387:        http_session_t* session;
        !          3388: 
        !          3389:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3390:                return;
        !          3391:        
        !          3392:        if(report==NULL) {
        !          3393:                lprintf(LOG_ERR,"%04d !JavaScript: %s", session->socket, message);
        !          3394:                if(session->req.fp!=NULL)
        !          3395:                        fprintf(session->req.fp,"!JavaScript: %s", message);
        !          3396:                return;
        !          3397:     }
        !          3398: 
        !          3399:        if(report->filename)
        !          3400:                sprintf(file," %s",report->filename);
        !          3401:        else
        !          3402:                file[0]=0;
        !          3403: 
        !          3404:        if(report->lineno)
        !          3405:                sprintf(line," line %u",report->lineno);
        !          3406:        else
        !          3407:                line[0]=0;
        !          3408: 
        !          3409:        if(JSREPORT_IS_WARNING(report->flags)) {
        !          3410:                if(JSREPORT_IS_STRICT(report->flags))
        !          3411:                        warning="strict warning";
        !          3412:                else
        !          3413:                        warning="warning";
        !          3414:        } else
        !          3415:                warning="";
        !          3416: 
        !          3417:        lprintf(LOG_ERR,"%04d !JavaScript %s%s%s: %s",session->socket,warning,file,line,message);
        !          3418:        if(session->req.fp!=NULL)
        !          3419:                fprintf(session->req.fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
        !          3420: }
        !          3421: 
        !          3422: static void js_writebuf(http_session_t *session, const char *buf, size_t buflen)
        !          3423: {
        !          3424:        if(session->req.sent_headers) {
        !          3425:                if(session->req.method!=HTTP_HEAD && session->req.method!=HTTP_OPTIONS)
        !          3426:                        writebuf(session,buf,buflen);
        !          3427:        }
        !          3428:        else
        !          3429:                fwrite(buf,1,buflen,session->req.fp);
        !          3430: }
        !          3431: 
        !          3432: static JSBool
        !          3433: js_writefunc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, BOOL writeln)
        !          3434: {
        !          3435:     uintN              i;
        !          3436:     JSString*  str=NULL;
        !          3437:        http_session_t* session;
        !          3438: 
        !          3439:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3440:                return(JS_FALSE);
        !          3441: 
        !          3442:        if(session->req.fp==NULL)
        !          3443:                return(JS_FALSE);
        !          3444: 
        !          3445:        if((!session->req.prev_write) && (!session->req.sent_headers)) {
        !          3446:                if(session->http_ver>=HTTP_1_1 && session->req.keep_alive) {
        !          3447:                        if(!ssjs_send_headers(session,TRUE))
        !          3448:                                return(JS_FALSE);
        !          3449:                }
        !          3450:                else {
        !          3451:                        /* "Fast Mode" requested? */
        !          3452:                        jsval           val;
        !          3453:                        JSObject*       reply;
        !          3454:                        JS_GetProperty(cx, session->js_glob, "http_reply", &val);
        !          3455:                        reply=JSVAL_TO_OBJECT(val);
        !          3456:                        JS_GetProperty(cx, reply, "fast", &val);
        !          3457:                        if(JSVAL_IS_BOOLEAN(val) && JSVAL_TO_BOOLEAN(val)) {
        !          3458:                                session->req.keep_alive=FALSE;
        !          3459:                                if(!ssjs_send_headers(session,FALSE))
        !          3460:                                        return(JS_FALSE);
        !          3461:                        }
        !          3462:                }
        !          3463:        }
        !          3464: 
        !          3465:        session->req.prev_write=TRUE;
        !          3466: 
        !          3467:     for(i=0; i<argc; i++) {
        !          3468:                if((str=JS_ValueToString(cx, argv[i]))==NULL)
        !          3469:                        continue;
        !          3470:                if(JS_GetStringLength(str)<1 && !writeln)
        !          3471:                        continue;
        !          3472:                js_writebuf(session,JS_GetStringBytes(str), JS_GetStringLength(str));
        !          3473:                if(writeln)
        !          3474:                        js_writebuf(session, newline, 2);
        !          3475:        }
        !          3476: 
        !          3477:        if(str==NULL)
        !          3478:                *rval = JSVAL_VOID;
        !          3479:        else
        !          3480:                *rval = STRING_TO_JSVAL(str);
        !          3481: 
        !          3482:        return(JS_TRUE);
        !          3483: }
        !          3484: 
        !          3485: static JSBool
        !          3486: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !          3487: {
        !          3488:        http_session_t* session;
        !          3489: 
        !          3490:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3491:                return(JS_FALSE);
        !          3492: 
        !          3493:        js_writefunc(cx, obj, argc, argv, rval,FALSE);
        !          3494: 
        !          3495:        return(JS_TRUE);
        !          3496: }
        !          3497: 
        !          3498: static JSBool
        !          3499: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !          3500: {
        !          3501:        http_session_t* session;
        !          3502: 
        !          3503:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3504:                return(JS_FALSE);
        !          3505: 
        !          3506:        js_writefunc(cx, obj, argc, argv, rval,TRUE);
        !          3507: 
        !          3508:        return(JS_TRUE);
        !          3509: }
        !          3510: 
        !          3511: static JSBool
        !          3512: js_set_cookie(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !          3513: {
        !          3514:        char    header_buf[8192];
        !          3515:        char    *header;
        !          3516:        char    *p;
        !          3517:        int32   i;
        !          3518:        JSBool  b;
        !          3519:        struct tm tm;
        !          3520:        http_session_t* session;
        !          3521: 
        !          3522:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3523:                return(JS_FALSE);
        !          3524: 
        !          3525:        if(argc<2)
        !          3526:                return(JS_FALSE);
        !          3527: 
        !          3528:        header=header_buf;
        !          3529:        p=js_ValueToStringBytes(cx, argv[0],NULL);
        !          3530:        if(!p)
        !          3531:                return(JS_FALSE);
        !          3532:        header+=sprintf(header,"Set-Cookie: %s=",p);
        !          3533:        p=js_ValueToStringBytes(cx, argv[1],NULL);
        !          3534:        if(!p)
        !          3535:                return(JS_FALSE);
        !          3536:        header+=sprintf(header,"%s",p);
        !          3537:        if(argc>2) {
        !          3538:                JS_ValueToInt32(cx,argv[2],&i);
        !          3539:                if(i && gmtime_r((time_t *)&i,&tm)!=NULL)
        !          3540:                        header += strftime(header,50,"; expires=%a, %d-%b-%Y %H:%M:%S GMT",&tm);
        !          3541:        }
        !          3542:        if(argc>3) {
        !          3543:                if(((p=js_ValueToStringBytes(cx, argv[3], NULL))!=NULL) && *p)
        !          3544:                        header += sprintf(header,"; domain=%s",p);
        !          3545:        }
        !          3546:        if(argc>4) {
        !          3547:                if(((p=js_ValueToStringBytes(cx, argv[4], NULL))!=NULL) && *p)
        !          3548:                        header += sprintf(header,"; path=%s",p);
        !          3549:        }
        !          3550:        if(argc>5) {
        !          3551:                JS_ValueToBoolean(cx, argv[5], &b);
        !          3552:                if(b)
        !          3553:                        header += sprintf(header,"; secure");
        !          3554:        }
        !          3555:        strListPush(&session->req.dynamic_heads,header_buf);
        !          3556: 
        !          3557:        return(JS_TRUE);
        !          3558: }
        !          3559: 
        !          3560: static JSBool
        !          3561: js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !          3562: {
        !          3563:        char            str[512];
        !          3564:     uintN              i=0;
        !          3565:        int32           level=LOG_INFO;
        !          3566:     JSString*  js_str;
        !          3567:        http_session_t* session;
        !          3568: 
        !          3569:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3570:                return(JS_FALSE);
        !          3571: 
        !          3572:     if(startup==NULL || startup->lputs==NULL)
        !          3573:         return(JS_FALSE);
        !          3574: 
        !          3575:        if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
        !          3576:                JS_ValueToInt32(cx,argv[i++],&level);
        !          3577: 
        !          3578:        str[0]=0;
        !          3579:     for(;i<argc && strlen(str)<(sizeof(str)/2);i++) {
        !          3580:                if((js_str=JS_ValueToString(cx, argv[i]))==NULL)
        !          3581:                    return(JS_FALSE);
        !          3582:                strncat(str,JS_GetStringBytes(js_str),sizeof(str)/2);
        !          3583:                strcat(str," ");
        !          3584:        }
        !          3585: 
        !          3586:        lprintf(level,"%04d %s",session->socket,str);
        !          3587: 
        !          3588:        *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
        !          3589: 
        !          3590:     return(JS_TRUE);
        !          3591: }
        !          3592: 
        !          3593: static JSBool
        !          3594: js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !          3595: {
        !          3596:        char*           p;
        !          3597:        JSBool          inc_logons=JS_FALSE;
        !          3598:        user_t          user;
        !          3599:        JSString*       js_str;
        !          3600:        http_session_t* session;
        !          3601: 
        !          3602:        *rval = BOOLEAN_TO_JSVAL(JS_FALSE);
        !          3603: 
        !          3604:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3605:                return(JS_FALSE);
        !          3606: 
        !          3607:        /* User name */
        !          3608:        if((js_str=JS_ValueToString(cx, argv[0]))==NULL) 
        !          3609:                return(JS_FALSE);
        !          3610: 
        !          3611:        if((p=JS_GetStringBytes(js_str))==NULL) 
        !          3612:                return(JS_FALSE);
        !          3613: 
        !          3614:        memset(&user,0,sizeof(user));
        !          3615: 
        !          3616:        if(isdigit(*p))
        !          3617:                user.number=atoi(p);
        !          3618:        else if(*p)
        !          3619:                user.number=matchuser(&scfg,p,FALSE);
        !          3620: 
        !          3621:        if(getuserdat(&scfg,&user)!=0) {
        !          3622:                lprintf(LOG_NOTICE,"%04d !USER NOT FOUND: '%s'"
        !          3623:                        ,session->socket,p);
        !          3624:                return(JS_TRUE);
        !          3625:        }
        !          3626: 
        !          3627:        if(user.misc&(DELETED|INACTIVE)) {
        !          3628:                lprintf(LOG_WARNING,"%04d !DELETED OR INACTIVE USER #%d: %s"
        !          3629:                        ,session->socket,user.number,p);
        !          3630:                return(JS_TRUE);
        !          3631:        }
        !          3632: 
        !          3633:        /* Password */
        !          3634:        if(user.pass[0]) {
        !          3635:                if((js_str=JS_ValueToString(cx, argv[1]))==NULL) 
        !          3636:                        return(JS_FALSE);
        !          3637: 
        !          3638:                if((p=JS_GetStringBytes(js_str))==NULL) 
        !          3639:                        return(JS_FALSE);
        !          3640: 
        !          3641:                if(stricmp(user.pass,p)) { /* Wrong password */
        !          3642:                        lprintf(LOG_WARNING,"%04d !INVALID PASSWORD ATTEMPT FOR USER: %s"
        !          3643:                                ,session->socket,user.alias);
        !          3644:                        return(JS_TRUE);
        !          3645:                }
        !          3646:        }
        !          3647: 
        !          3648:        if(argc>2)
        !          3649:                JS_ValueToBoolean(cx,argv[2],&inc_logons);
        !          3650: 
        !          3651:        if(inc_logons) {
        !          3652:                user.logons++;
        !          3653:                user.ltoday++;
        !          3654:        }
        !          3655: 
        !          3656:        http_logon(session, &user);
        !          3657: 
        !          3658:        /* user-specific objects */
        !          3659:        if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user
        !          3660:                ,NULL /* ftp index file */, session->subscan /* subscan */)) {
        !          3661:                lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user objects",session->socket);
        !          3662:                send_error(session,"500 Error initializing JavaScript User Objects");
        !          3663:                return(FALSE);
        !          3664:        }
        !          3665: 
        !          3666:        *rval=BOOLEAN_TO_JSVAL(JS_TRUE);
        !          3667: 
        !          3668:        return(JS_TRUE);
        !          3669: }
        !          3670: 
        !          3671: #if 0
        !          3672: static char *find_next_pair(char *buffer, size_t buflen, char find)
        !          3673: {
        !          3674:        char    *p;
        !          3675:        char    *search;
        !          3676:        char    *end;
        !          3677:        size_t  buflen2;
        !          3678:        char    chars[5]="@%^<";
        !          3679: 
        !          3680:        end=buffer+buflen;
        !          3681:        search=buffer;
        !          3682:        buflen2=buflen;
        !          3683: 
        !          3684:        for(;search<end;) {
        !          3685:                p=memchr(search, chars[i], buflen2);
        !          3686:                /* Can't even find one... there's definatly no pair */
        !          3687:                if(p==NULL)
        !          3688:                        return(NULL);
        !          3689: 
        !          3690:                if(*(p+1)==find)
        !          3691:                        return(p);
        !          3692: 
        !          3693:                /* Next search pos is at the char after the match */
        !          3694:                search=p+1;
        !          3695:                buflen2=end-search;
        !          3696:        }
        !          3697: }
        !          3698: 
        !          3699: static void js_write_escaped(JSContext *cx, JSObject *obj, char *pos, size_t len, char *name_end, char *repeat_section)
        !          3700: {
        !          3701:        char    *name=pos+2;
        !          3702: 
        !          3703: }
        !          3704: 
        !          3705: enum {
        !          3706:         T_AT
        !          3707:        ,T_PERCENT
        !          3708:        ,T_CARET
        !          3709:        ,T_LT
        !          3710: };
        !          3711: 
        !          3712: static int js_write_template_part(JSContext *cx, JSObject *obj, char *template, size_t len, char *repeat_section)
        !          3713: {
        !          3714:        size_t          len2;
        !          3715:        char            *pos;
        !          3716:        char            *end;
        !          3717:        char            *p;
        !          3718:        char            *p2;
        !          3719:        char            *send_end;
        !          3720:        int                     no_more[4];
        !          3721:        char            *next[4];
        !          3722:        int                     i,j;
        !          3723:        char            chars[5]="@%^<";
        !          3724: 
        !          3725:        end=template+len;
        !          3726:        pos=template;
        !          3727:        memset(&next,0,sizeof(next));
        !          3728:        memset(&no_more,0,sizeof(no_more));
        !          3729: 
        !          3730:        while(pos<end) {
        !          3731:                send_end=NULL;
        !          3732: 
        !          3733:                /* Find next seperator */
        !          3734:                for(i=0; i<4; i++) {
        !          3735:                        if(!no_more[i]) {
        !          3736:                                if(next[i] < pos)
        !          3737:                                        next[i]=NULL;
        !          3738:                                if(next[i] == NULL) {
        !          3739:                                        if((next[i]=find_next_pair(pos, len, chars[i]))==NULL) {
        !          3740:                                                no_more[i]=TRUE;
        !          3741:                                                continue;
        !          3742:                                        }
        !          3743:                                }
        !          3744:                                if(!send_end || next[i] < send_end)
        !          3745:                                        send_end=next[i];
        !          3746:                        }
        !          3747:                }
        !          3748:                if(send_end==NULL) {
        !          3749:                        /* Nothing else matched... we're good now! */
        !          3750:                        js_writebuf(session, pos, len);
        !          3751:                        pos=end;
        !          3752:                        len=0;
        !          3753:                        continue;
        !          3754:                }
        !          3755:                if(send_end > pos) {
        !          3756:                        i=send_end-pos;
        !          3757:                        js_writebuf(session, pos, i);
        !          3758:                        pos+=i;
        !          3759:                        len-=i;
        !          3760:                }
        !          3761: 
        !          3762:                /*
        !          3763:                 * At this point, pos points to a matched introducer.
        !          3764:                 * If it's not a repeat section, we can just output it here.
        !          3765:                 */
        !          3766:                if(*pos != '<') {
        !          3767:                        /*
        !          3768:                         * If there is no corresponding terminator to this introdcer,
        !          3769:                         * force it to be output unchanged.
        !          3770:                         */
        !          3771:                        if((p=find_next_pair(pos, len, *pos))==NULL) {
        !          3772:                                no_more[strchr(chars,*pos)-char]=TRUE;
        !          3773:                                continue;
        !          3774:                        }
        !          3775:                        js_write_escaped(cx, obj, pos, len, p, repeat_section);
        !          3776:                        continue;
        !          3777:                }
        !          3778: 
        !          3779:                /*
        !          3780:                 * Pos is the start of a repeat section now... this is where things
        !          3781:                 * start to get tricky.  Set up RepeatObj object, then call self
        !          3782:                 * once for each repeat.
        !          3783:                 */
        !          3784:        }
        !          3785: }
        !          3786: 
        !          3787: static JSBool
        !          3788: js_write_template(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
        !          3789: {
        !          3790:        JSString*       js_str;
        !          3791:        char            *filename;
        !          3792:        char            *template;
        !          3793:        FILE            *tfile;
        !          3794:        size_t          len;
        !          3795:        http_session_t* session;
        !          3796: 
        !          3797:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3798:                return(JS_FALSE);
        !          3799: 
        !          3800:        if(session->req.fp==NULL)
        !          3801:                return(JS_FALSE);
        !          3802: 
        !          3803:        if((filename=js_ValueToStringBytes(cx, argv[0]))==NULL)
        !          3804:                return(JS_FALSE);
        !          3805: 
        !          3806:        if(!fexist(filename)) {
        !          3807:                JS_ReportError(cx, "Template file %s does not exist.", filename);
        !          3808:                return(JS_FALSE);
        !          3809:        }
        !          3810:        len=flength(filename);
        !          3811: 
        !          3812:        if((tfile=fopen(filename,"r"))==NULL) {
        !          3813:                JS_ReportError(cx, "Unable to open template %s for read.", filename);
        !          3814:                return(JS_FALSE);
        !          3815:        }
        !          3816: 
        !          3817:        if((template=(char *)alloca(len))==NULL) {
        !          3818:                JS_ReportError(cx, "Unable to allocate %u bytes for template.", len);
        !          3819:                return(JS_FALSE);
        !          3820:        }
        !          3821: 
        !          3822:        if(fread(template, 1, len, tfile) != len) {
        !          3823:                fclose(tfile);
        !          3824:                JS_ReportError(cx, "Unable to read %u bytes from template %s.", len, filename);
        !          3825:                return(JS_FALSE);
        !          3826:        }
        !          3827:        fclose(tfile);
        !          3828: 
        !          3829:        if((!session->req.prev_write) && (!session->req.sent_headers)) {
        !          3830:                if(session->http_ver>=HTTP_1_1 && session->req.keep_alive) {
        !          3831:                        if(!ssjs_send_headers(session,TRUE))
        !          3832:                                return(JS_FALSE);
        !          3833:                }
        !          3834:                else {
        !          3835:                        /* "Fast Mode" requested? */
        !          3836:                        jsval           val;
        !          3837:                        JSObject*       reply;
        !          3838:                        JS_GetProperty(cx, session->js_glob, "http_reply", &val);
        !          3839:                        reply=JSVAL_TO_OBJECT(val);
        !          3840:                        JS_GetProperty(cx, reply, "fast", &val);
        !          3841:                        if(JSVAL_IS_BOOLEAN(val) && JSVAL_TO_BOOLEAN(val)) {
        !          3842:                                session->req.keep_alive=FALSE;
        !          3843:                                if(!ssjs_send_headers(session,FALSE))
        !          3844:                                        return(JS_FALSE);
        !          3845:                        }
        !          3846:                }
        !          3847:        }
        !          3848: 
        !          3849:        session->req.prev_write=TRUE;
        !          3850:        js_write_template_part(cx, obj, template, len, NULL);
        !          3851: 
        !          3852:        return(JS_TRUE);
        !          3853: }
        !          3854: #endif
        !          3855: 
        !          3856: static JSFunctionSpec js_global_functions[] = {
        !          3857:        {"write",           js_write,           1},             /* write to HTML file */
        !          3858:        {"writeln",         js_writeln,         1},             /* write line to HTML file */
        !          3859:        {"print",                       js_writeln,                     1},             /* write line to HTML file (alias) */
        !          3860:        {"log",                         js_log,                         0},             /* Log a string */
        !          3861:        {"login",           js_login,           2},             /* log in as a different user */
        !          3862:        {"set_cookie",          js_set_cookie,          2},             /* Set a cookie */
        !          3863:        {0}
        !          3864: };
        !          3865: 
        !          3866: static JSBool
        !          3867: js_BranchCallback(JSContext *cx, JSScript *script)
        !          3868: {
        !          3869:        http_session_t* session;
        !          3870: 
        !          3871:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
        !          3872:                return(JS_FALSE);
        !          3873: 
        !          3874:     return(js_CommonBranchCallback(cx,&session->js_branch));
        !          3875: }
        !          3876: 
        !          3877: static JSContext* 
        !          3878: js_initcx(http_session_t *session)
        !          3879: {
        !          3880:        JSContext*      js_cx;
        !          3881: 
        !          3882:        lprintf(LOG_INFO,"%04d JavaScript: Initializing context (stack: %lu bytes)"
        !          3883:                ,session->socket,startup->js.cx_stack);
        !          3884: 
        !          3885:     if((js_cx = JS_NewContext(session->js_runtime, startup->js.cx_stack))==NULL)
        !          3886:                return(NULL);
        !          3887: 
        !          3888:        lprintf(LOG_INFO,"%04d JavaScript: Context created",session->socket);
        !          3889: 
        !          3890:     JS_SetErrorReporter(js_cx, js_ErrorReporter);
        !          3891: 
        !          3892:        JS_SetBranchCallback(js_cx, js_BranchCallback);
        !          3893: 
        !          3894:        lprintf(LOG_INFO,"%04d JavaScript: Creating Global Objects and Classes",session->socket);
        !          3895:        if((session->js_glob=js_CreateCommonObjects(js_cx, &scfg, NULL
        !          3896:                                                                        ,NULL                                           /* global */
        !          3897:                                                                        ,uptime                                         /* system */
        !          3898:                                                                        ,startup->host_name                     /* system */
        !          3899:                                                                        ,SOCKLIB_DESC                           /* system */
        !          3900:                                                                        ,&session->js_branch            /* js */
        !          3901:                                                                        ,&session->client                       /* client */
        !          3902:                                                                        ,session->socket                        /* client */
        !          3903:                                                                        ,&js_server_props                       /* server */
        !          3904:                ))==NULL
        !          3905:                || !JS_DefineFunctions(js_cx, session->js_glob, js_global_functions)) {
        !          3906:                JS_DestroyContext(js_cx);
        !          3907:                return(NULL);
        !          3908:        }
        !          3909: 
        !          3910:        return(js_cx);
        !          3911: }
        !          3912: 
        !          3913: static BOOL js_setup(http_session_t* session)
        !          3914: {
        !          3915:        JSObject*       argv;
        !          3916: 
        !          3917: #ifndef ONE_JS_RUNTIME
        !          3918:        if(session->js_runtime == NULL) {
        !          3919:                lprintf(LOG_INFO,"%04d JavaScript: Creating runtime: %lu bytes"
        !          3920:                        ,session->socket,startup->js.max_bytes);
        !          3921: 
        !          3922:                if((session->js_runtime=JS_NewRuntime(startup->js.max_bytes))==NULL) {
        !          3923:                        lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",session->socket);
        !          3924:                        return(FALSE);
        !          3925:                }
        !          3926:        }
        !          3927: #endif
        !          3928: 
        !          3929:        if(session->js_cx==NULL) {      /* Context not yet created, create it now */
        !          3930:                if(((session->js_cx=js_initcx(session))==NULL)) {
        !          3931:                        lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript context",session->socket);
        !          3932:                        return(FALSE);
        !          3933:                }
        !          3934:                argv=JS_NewArrayObject(session->js_cx, 0, NULL);
        !          3935: 
        !          3936:                JS_DefineProperty(session->js_cx, session->js_glob, "argv", OBJECT_TO_JSVAL(argv)
        !          3937:                        ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
        !          3938:                JS_DefineProperty(session->js_cx, session->js_glob, "argc", INT_TO_JSVAL(0)
        !          3939:                        ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
        !          3940: 
        !          3941:                JS_DefineProperty(session->js_cx, session->js_glob, "web_root_dir",
        !          3942:                        STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx, root_dir))
        !          3943:                        ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
        !          3944:                JS_DefineProperty(session->js_cx, session->js_glob, "web_error_dir",
        !          3945:                        STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx, session->req.error_dir?session->req.error_dir:error_dir))
        !          3946:                        ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
        !          3947: 
        !          3948:        }
        !          3949: 
        !          3950:        lprintf(LOG_INFO,"%04d JavaScript: Initializing HttpRequest object",session->socket);
        !          3951:        if(js_CreateHttpRequestObject(session->js_cx, session->js_glob, session)==NULL) {
        !          3952:                lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript HttpRequest object",session->socket);
        !          3953:                return(FALSE);
        !          3954:        }
        !          3955: 
        !          3956:        lprintf(LOG_INFO,"%04d JavaScript: Initializing HttpReply object",session->socket);
        !          3957:        if(js_CreateHttpReplyObject(session->js_cx, session->js_glob, session)==NULL) {
        !          3958:                lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript HttpReply object",session->socket);
        !          3959:                return(FALSE);
        !          3960:        }
        !          3961: 
        !          3962:        JS_SetContextPrivate(session->js_cx, session);
        !          3963: 
        !          3964:        return(TRUE);
        !          3965: }
        !          3966: 
        !          3967: static BOOL ssjs_send_headers(http_session_t* session,int chunked)
        !          3968: {
        !          3969:        jsval           val;
        !          3970:        JSObject*       reply;
        !          3971:        JSIdArray*      heads;
        !          3972:        JSObject*       headers;
        !          3973:        int                     i;
        !          3974:        JSString*       js_str;
        !          3975:        char            str[MAX_REQUEST_LINE+1];
        !          3976: 
        !          3977:        JS_GetProperty(session->js_cx,session->js_glob,"http_reply",&val);
        !          3978:        reply = JSVAL_TO_OBJECT(val);
        !          3979:        JS_GetProperty(session->js_cx,reply,"status",&val);
        !          3980:        SAFECOPY(session->req.status,JS_GetStringBytes(JSVAL_TO_STRING(val)));
        !          3981:        JS_GetProperty(session->js_cx,reply,"header",&val);
        !          3982:        headers = JSVAL_TO_OBJECT(val);
        !          3983:        heads=JS_Enumerate(session->js_cx,headers);
        !          3984:        if(heads != NULL) {
        !          3985:                for(i=0;i<heads->length;i++)  {
        !          3986:                        JS_IdToValue(session->js_cx,heads->vector[i],&val);
        !          3987:                        js_str=JSVAL_TO_STRING(val);
        !          3988:                        JS_GetProperty(session->js_cx,headers,JS_GetStringBytes(js_str),&val);
        !          3989:                        safe_snprintf(str,sizeof(str),"%s: %s"
        !          3990:                                ,JS_GetStringBytes(js_str),JS_GetStringBytes(JSVAL_TO_STRING(val)));
        !          3991:                        strListPush(&session->req.dynamic_heads,str);
        !          3992:                }
        !          3993:                JS_ClearScope(session->js_cx, headers);
        !          3994:        }
        !          3995:        return(send_headers(session,session->req.status,chunked));
        !          3996: }
        !          3997: 
        !          3998: static BOOL exec_ssjs(http_session_t* session, char* script)  {
        !          3999:        JSScript*       js_script;
        !          4000:        jsval           rval;
        !          4001:        char            path[MAX_PATH+1];
        !          4002:        BOOL            retval=TRUE;
        !          4003:        long double             start;
        !          4004: 
        !          4005:        /* External JavaScript handler? */
        !          4006:        if(script == session->req.physical_path && session->req.xjs_handler[0])
        !          4007:                script = session->req.xjs_handler;
        !          4008: 
        !          4009:        sprintf(path,"%sSBBS_SSJS.%u.%u.html",temp_dir,getpid(),session->socket);
        !          4010:        if((session->req.fp=fopen(path,"wb"))==NULL) {
        !          4011:                lprintf(LOG_ERR,"%04d !ERROR %d opening/creating %s", session->socket, errno, path);
        !          4012:                return(FALSE);
        !          4013:        }
        !          4014:        if(session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]) {
        !          4015:                if(!(startup->options&WEB_OPT_DEBUG_SSJS))
        !          4016:                        remove(session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]);
        !          4017:                free(session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]);
        !          4018:        }
        !          4019:        /* FREE()d in close_request() */
        !          4020:        session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]=strdup(path);
        !          4021: 
        !          4022:        js_add_request_prop(session,"real_path",session->req.physical_path);
        !          4023:        js_add_request_prop(session,"virtual_path",session->req.virtual_path);
        !          4024:        js_add_request_prop(session,"ars",session->req.ars);
        !          4025:        js_add_request_prop(session,"request_string",session->req.request_line);
        !          4026:        js_add_request_prop(session,"host",session->req.host);
        !          4027:        js_add_request_prop(session,"vhost",session->req.vhost);
        !          4028:        js_add_request_prop(session,"http_ver",http_vers[session->http_ver]);
        !          4029:        js_add_request_prop(session,"remote_ip",session->host_ip);
        !          4030:        js_add_request_prop(session,"remote_host",session->host_name);
        !          4031:        if(session->req.query_str && session->req.query_str[0])  {
        !          4032:                js_add_request_prop(session,"query_string",session->req.query_str);
        !          4033:                js_parse_query(session,session->req.query_str);
        !          4034:        }
        !          4035:        if(session->req.post_data && session->req.post_data[0]) {
        !          4036:                js_add_request_prop(session,"post_data",session->req.post_data);
        !          4037:                js_parse_query(session,session->req.post_data);
        !          4038:        }
        !          4039: 
        !          4040:        do {
        !          4041:                /* RUN SCRIPT */
        !          4042:                JS_ClearPendingException(session->js_cx);
        !          4043: 
        !          4044:                session->js_branch.counter=0;
        !          4045: 
        !          4046:                lprintf(LOG_DEBUG,"%04d JavaScript: Compiling script: %s",session->socket,script);
        !          4047:                if((js_script=JS_CompileFile(session->js_cx, session->js_glob
        !          4048:                        ,script))==NULL) {
        !          4049:                        lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)"
        !          4050:                                ,session->socket,script);
        !          4051:                        return(FALSE);
        !          4052:                }
        !          4053: 
        !          4054:                lprintf(LOG_DEBUG,"%04d JavaScript: Executing script: %s",session->socket,script);
        !          4055:                start=xp_timer();
        !          4056:                JS_ExecuteScript(session->js_cx, session->js_glob, js_script, &rval);
        !          4057:                js_EvalOnExit(session->js_cx, session->js_glob, &session->js_branch);
        !          4058:                lprintf(LOG_DEBUG,"%04d JavaScript: Done executing script: %s (%.2Lf seconds)"
        !          4059:                        ,session->socket,script,xp_timer()-start);
        !          4060:        } while(0);
        !          4061: 
        !          4062:        SAFECOPY(session->req.physical_path, path);
        !          4063:        if(session->req.fp!=NULL) {
        !          4064:                fclose(session->req.fp);
        !          4065:                session->req.fp=NULL;
        !          4066:        }
        !          4067: 
        !          4068: 
        !          4069:        /* Read http_reply object */
        !          4070:        if(!session->req.sent_headers) {
        !          4071:                retval=ssjs_send_headers(session,FALSE);
        !          4072:        }
        !          4073: 
        !          4074:        /* Free up temporary resources here */
        !          4075: 
        !          4076:        if(js_script!=NULL) 
        !          4077:                JS_DestroyScript(session->js_cx, js_script);
        !          4078:        session->req.dynamic=IS_SSJS;
        !          4079:        
        !          4080:        return(retval);
        !          4081: }
        !          4082: 
        !          4083: static void respond(http_session_t * session)
        !          4084: {
        !          4085:        BOOL            send_file=TRUE;
        !          4086: 
        !          4087:        if(session->req.method==HTTP_OPTIONS) {
        !          4088:                send_headers(session,session->req.status,FALSE);
        !          4089:        }
        !          4090:        else {
        !          4091:                if(session->req.dynamic==IS_CGI)  {
        !          4092:                        if(!exec_cgi(session))  {
        !          4093:                                send_error(session,error_500);
        !          4094:                                return;
        !          4095:                        }
        !          4096:                        session->req.finished=TRUE;
        !          4097:                        return;
        !          4098:                }
        !          4099: 
        !          4100:                if(session->req.dynamic==IS_SSJS) {     /* Server-Side JavaScript */
        !          4101:                        if(!exec_ssjs(session,session->req.physical_path))  {
        !          4102:                                send_error(session,error_500);
        !          4103:                                return;
        !          4104:                        }
        !          4105:                        sprintf(session->req.physical_path
        !          4106:                                ,"%sSBBS_SSJS.%u.%u.html",temp_dir,getpid(),session->socket);
        !          4107:                }
        !          4108:                else {
        !          4109:                        session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
        !          4110:                        send_file=send_headers(session,session->req.status,FALSE);
        !          4111:                }
        !          4112:        }
        !          4113:        if(session->req.method==HTTP_HEAD || session->req.method==HTTP_OPTIONS)
        !          4114:                send_file=FALSE;
        !          4115:        if(send_file)  {
        !          4116:                int snt=0;
        !          4117:                lprintf(LOG_INFO,"%04d Sending file: %s (%u bytes)"
        !          4118:                        ,session->socket, session->req.physical_path, flength(session->req.physical_path));
        !          4119:                snt=sock_sendfile(session,session->req.physical_path,session->req.range_start,session->req.range_end);
        !          4120:                if(session->req.ld!=NULL) {
        !          4121:                        if(snt<0)
        !          4122:                                snt=0;
        !          4123:                        session->req.ld->size=snt;
        !          4124:                }
        !          4125:                if(snt>0)
        !          4126:                        lprintf(LOG_INFO,"%04d Sent file: %s (%d bytes)"
        !          4127:                                ,session->socket, session->req.physical_path, snt);
        !          4128:        }
        !          4129:        session->req.finished=TRUE;
        !          4130: }
        !          4131: 
        !          4132: int read_post_data(http_session_t * session)
        !          4133: {
        !          4134:        unsigned i=0;
        !          4135: 
        !          4136:        if(session->req.dynamic!=IS_CGI && (session->req.post_len || session->req.read_chunked))  {
        !          4137:                if(session->req.read_chunked) {
        !          4138:                        char *p;
        !          4139:                        size_t  ch_len=0;
        !          4140:                        int     bytes_read=0;
        !          4141:                        char    ch_lstr[12];
        !          4142:                        session->req.post_len=0;
        !          4143: 
        !          4144:                        while(1) {
        !          4145:                                /* Read chunk length */
        !          4146:                                if(sockreadline(session,ch_lstr,sizeof(ch_lstr)-1)>0) {
        !          4147:                                        ch_len=strtol(ch_lstr,NULL,16);
        !          4148:                                }
        !          4149:                                else {
        !          4150:                                        send_error(session,error_500);
        !          4151:                                        return(FALSE);
        !          4152:                                }
        !          4153:                                if(ch_len==0)
        !          4154:                                        break;
        !          4155:                                /* Check size */
        !          4156:                                i += ch_len;
        !          4157:                                if(i > MAX_POST_LEN) {
        !          4158:                                        send_error(session,"413 Request entity too large");
        !          4159:                                        return(FALSE);
        !          4160:                                }
        !          4161:                                /* realloc() to new size */
        !          4162:                                /* FREE()d in close_request */
        !          4163:                                p=realloc(session->req.post_data, i);
        !          4164:                                if(p==NULL) {
        !          4165:                                        lprintf(LOG_CRIT,"%04d !ERROR Allocating %d bytes of memory",session->socket,session->req.post_len);
        !          4166:                                        send_error(session,"413 Request entity too large");
        !          4167:                                        return(FALSE);
        !          4168:                                }
        !          4169:                                session->req.post_data=p;
        !          4170:                                /* read new data */
        !          4171:                                bytes_read=recvbufsocket(&session->socket,session->req.post_data+session->req.post_len,ch_len);
        !          4172:                                if(!bytes_read) {
        !          4173:                                        send_error(session,error_500);
        !          4174:                                        return(FALSE);
        !          4175:                                }
        !          4176:                                session->req.post_len+=bytes_read;
        !          4177:                        }
        !          4178:                        /* Read more headers! */
        !          4179:                        if(!get_request_headers(session))
        !          4180:                                return(FALSE);
        !          4181:                        if(!parse_headers(session))
        !          4182:                                return(FALSE);
        !          4183:                }
        !          4184:                else {
        !          4185:                        i = session->req.post_len;
        !          4186:                        FREE_AND_NULL(session->req.post_data);
        !          4187:                        /* FREE()d in close_request()  */
        !          4188:                        if(i < (MAX_POST_LEN+1) && (session->req.post_data=malloc(i+1)) != NULL)
        !          4189:                                session->req.post_len=recvbufsocket(&session->socket,session->req.post_data,i);
        !          4190:                        else  {
        !          4191:                                lprintf(LOG_CRIT,"%04d !ERROR Allocating %d bytes of memory",session->socket,i);
        !          4192:                                send_error(session,"413 Request entity too large");
        !          4193:                                return(FALSE);
        !          4194:                        }
        !          4195:                }
        !          4196:                if(session->req.post_len != i)
        !          4197:                                lprintf(LOG_DEBUG,"%04d !ERROR Browser said they sent %d bytes, but I got %d",session->socket,i,session->req.post_len);
        !          4198:                if(session->req.post_len > i)
        !          4199:                        session->req.post_len = i;
        !          4200:                session->req.post_data[session->req.post_len]=0;
        !          4201:        }
        !          4202:        return(TRUE);
        !          4203: }
        !          4204: 
        !          4205: void http_output_thread(void *arg)
        !          4206: {
        !          4207:        http_session_t  *session=(http_session_t *)arg;
        !          4208:        RingBuf *obuf;
        !          4209:        char    buf[OUTBUF_LEN+12];                                             /* *MUST* be large enough to hold the buffer,
        !          4210:                                                                                                                the size of the buffer in hex, and four extra bytes. */
        !          4211:        char    *bufdata;
        !          4212:        int             failed=0;
        !          4213:        int             len;
        !          4214:        unsigned avail;
        !          4215:        int             chunked;
        !          4216:        int             i;
        !          4217:        unsigned mss=OUTBUF_LEN;
        !          4218: 
        !          4219:        obuf=&(session->outbuf);
        !          4220:        /* Destroyed at end of function */
        !          4221:        if((i=pthread_mutex_init(&session->outbuf_write,NULL))!=0) {
        !          4222:                lprintf(LOG_DEBUG,"Error %d initializing outbuf mutex",i);
        !          4223:                close_socket(&session->socket);
        !          4224:                return;
        !          4225:        }
        !          4226:        session->outbuf_write_initialized=1;
        !          4227: 
        !          4228: #ifdef TCP_MAXSEG
        !          4229:        /*
        !          4230:         * Auto-tune the highwater mark to be the negotiated MSS for the
        !          4231:         * socket (when possible)
        !          4232:         */
        !          4233:        if(!obuf->highwater_mark) {
        !          4234:                socklen_t   sl;
        !          4235:                sl=sizeof(i);
        !          4236:                if(!getsockopt(session->socket, IPPROTO_TCP, TCP_MAXSEG, &i, &sl)) {
        !          4237:                        /* Check for sanity... */
        !          4238:                        if(i>100) {
        !          4239:                                obuf->highwater_mark=i-12;
        !          4240:                                lprintf(LOG_DEBUG,"Autotuning outbuf highwater mark to %d based on MSS",i);
        !          4241:                                mss=obuf->highwater_mark;
        !          4242:                                if(mss>OUTBUF_LEN) {
        !          4243:                                        mss=OUTBUF_LEN;
        !          4244:                                        lprintf(LOG_DEBUG,"MSS (%d) is higher than OUTBUF_LEN (%d)",i,OUTBUF_LEN);
        !          4245:                                }
        !          4246:                        }
        !          4247:                }
        !          4248:        }
        !          4249: #endif
        !          4250: 
        !          4251:        thread_up(TRUE /* setuid */);
        !          4252:        /*
        !          4253:         * Do *not* exit on terminate_server... wait for session thread
        !          4254:         * to close the socket and set it to INVALID_SOCKET
        !          4255:         */
        !          4256:     while(session->socket!=INVALID_SOCKET) {
        !          4257: 
        !          4258:                /* Wait for something to output in the RingBuffer */
        !          4259:                if((avail=RingBufFull(obuf))==0) {      /* empty */
        !          4260:                        if(sem_trywait_block(&obuf->sem,1000))
        !          4261:                                continue;
        !          4262:                        /* Check for spurious sem post... */
        !          4263:                        if((avail=RingBufFull(obuf))==0)
        !          4264:                                continue;
        !          4265:                }
        !          4266:                else
        !          4267:                        sem_trywait(&obuf->sem);
        !          4268: 
        !          4269:                /* Wait for full buffer or drain timeout */
        !          4270:                if(obuf->highwater_mark) {
        !          4271:                        if(avail<obuf->highwater_mark) {
        !          4272:                                sem_trywait_block(&obuf->highwater_sem,startup->outbuf_drain_timeout);
        !          4273:                                /* We (potentially) blocked, so get fill level again */
        !          4274:                        avail=RingBufFull(obuf);
        !          4275:                        } else
        !          4276:                                sem_trywait(&obuf->highwater_sem);
        !          4277:                }
        !          4278: 
        !          4279:         /*
        !          4280:          * At this point, there's something to send and,
        !          4281:          * if the highwater mark is set, the timeout has
        !          4282:          * passed or we've hit highwater.  Read ring buffer
        !          4283:          * into linear buffer.
        !          4284:          */
        !          4285:         len=avail;
        !          4286:                if(avail>mss)
        !          4287:                        len=(avail=mss);
        !          4288: 
        !          4289:                /* 
        !          4290:                 * Read the current value of write_chunked... since we wait until the
        !          4291:                 * ring buffer is empty before fiddling with it.
        !          4292:                 */
        !          4293:                chunked=session->req.write_chunked;
        !          4294: 
        !          4295:                bufdata=buf;
        !          4296:                if(chunked) {
        !          4297:                        i=sprintf(buf, "%X\r\n", avail);
        !          4298:                        bufdata+=i;
        !          4299:                        len+=i;
        !          4300:                }
        !          4301: 
        !          4302:                pthread_mutex_lock(&session->outbuf_write);
        !          4303:         RingBufRead(obuf, bufdata, avail);
        !          4304:                if(chunked) {
        !          4305:                        bufdata+=avail;
        !          4306:                        *(bufdata++)='\r';
        !          4307:                        *(bufdata++)='\n';
        !          4308:                        len+=2;
        !          4309:                }
        !          4310: 
        !          4311:                if(!failed)
        !          4312:                        sock_sendbuf(&session->socket, buf, len, &failed);
        !          4313:                pthread_mutex_unlock(&session->outbuf_write);
        !          4314:     }
        !          4315:        thread_down();
        !          4316:        /* Ensure outbuf isn't currently being drained */
        !          4317:        pthread_mutex_lock(&session->outbuf_write);
        !          4318:        session->outbuf_write_initialized=0;
        !          4319:        pthread_mutex_unlock(&session->outbuf_write);
        !          4320:        pthread_mutex_destroy(&session->outbuf_write);
        !          4321:        sem_post(&session->output_thread_terminated);
        !          4322: }
        !          4323: 
        !          4324: void http_session_thread(void* arg)
        !          4325: {
        !          4326:        int                             i;
        !          4327:        char*                   host_name;
        !          4328:        HOSTENT*                host;
        !          4329:        SOCKET                  socket;
        !          4330:        char                    redir_req[MAX_REQUEST_LINE+1];
        !          4331:        char                    *redirp;
        !          4332:        http_session_t  session;
        !          4333:        int                             loop_count;
        !          4334:        BOOL                    init_error;
        !          4335: 
        !          4336:        pthread_mutex_lock(&((http_session_t*)arg)->struct_filled);
        !          4337:        pthread_mutex_unlock(&((http_session_t*)arg)->struct_filled);
        !          4338:        pthread_mutex_destroy(&((http_session_t*)arg)->struct_filled);
        !          4339: 
        !          4340:        session=*(http_session_t*)arg;  /* copies arg BEFORE it's freed */
        !          4341:        FREE_AND_NULL(arg);
        !          4342: 
        !          4343:        socket=session.socket;
        !          4344:        if(socket==INVALID_SOCKET) {
        !          4345:                session_threads--;
        !          4346:                return;
        !          4347:        }
        !          4348:        lprintf(LOG_DEBUG,"%04d Session thread started", session.socket);
        !          4349: 
        !          4350:        if(startup->index_file_name==NULL || startup->cgi_ext==NULL)
        !          4351:                lprintf(LOG_DEBUG,"%04d !!! DANGER WILL ROBINSON, DANGER !!!", session.socket);
        !          4352: 
        !          4353: #ifdef _WIN32
        !          4354:        if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
        !          4355:                PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
        !          4356: #endif
        !          4357: 
        !          4358:        thread_up(TRUE /* setuid */);
        !          4359:        session.finished=FALSE;
        !          4360: 
        !          4361:        /* Start up the output buffer */
        !          4362:        /* FREE()d in this block (RingBufDispose before all returns) */
        !          4363:        if(RingBufInit(&(session.outbuf), OUTBUF_LEN)) {
        !          4364:                lprintf(LOG_ERR,"%04d Canot create output ringbuffer!", session.socket);
        !          4365:                close_socket(&session.socket);
        !          4366:                thread_down();
        !          4367:                session_threads--;
        !          4368:                return;
        !          4369:        }
        !          4370: 
        !          4371:        /* Destroyed in this block (before all returns) */
        !          4372:        sem_init(&session.output_thread_terminated,0,0);
        !          4373:        _beginthread(http_output_thread, 0, &session);
        !          4374: 
        !          4375:        sbbs_srand();   /* Seed random number generator */
        !          4376: 
        !          4377:        if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
        !          4378:                host=NULL;
        !          4379:        else
        !          4380:                host=gethostbyaddr ((char *)&session.addr.sin_addr
        !          4381:                        ,sizeof(session.addr.sin_addr),AF_INET);
        !          4382: 
        !          4383:        if(host!=NULL && host->h_name!=NULL)
        !          4384:                host_name=host->h_name;
        !          4385:        else
        !          4386:                host_name=session.host_ip;
        !          4387: 
        !          4388:        SAFECOPY(session.host_name,host_name);
        !          4389: 
        !          4390:        if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP))  {
        !          4391:                lprintf(LOG_INFO,"%04d Hostname: %s", session.socket, host_name);
        !          4392:                for(i=0;host!=NULL && host->h_aliases!=NULL 
        !          4393:                        && host->h_aliases[i]!=NULL;i++)
        !          4394:                        lprintf(LOG_INFO,"%04d HostAlias: %s", session.socket, host->h_aliases[i]);
        !          4395:                if(trashcan(&scfg,host_name,"host")) {
        !          4396:                        lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", session.socket, host_name);
        !          4397:                        close_socket(&session.socket);
        !          4398:                        sem_wait(&session.output_thread_terminated);
        !          4399:                        sem_destroy(&session.output_thread_terminated);
        !          4400:                        RingBufDispose(&session.outbuf);
        !          4401:                        thread_down();
        !          4402:                        session_threads--;
        !          4403:                        return;
        !          4404:                }
        !          4405:        }
        !          4406: 
        !          4407:        /* host_ip wasn't defined in http_session_thread */
        !          4408:        if(trashcan(&scfg,session.host_ip,"ip")) {
        !          4409:                lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip);
        !          4410:                close_socket(&session.socket);
        !          4411:                sem_wait(&session.output_thread_terminated);
        !          4412:                sem_destroy(&session.output_thread_terminated);
        !          4413:                RingBufDispose(&session.outbuf);
        !          4414:                thread_down();
        !          4415:                session_threads--;
        !          4416:                return;
        !          4417:        }
        !          4418: 
        !          4419:        active_clients++;
        !          4420:        update_clients();
        !          4421:        SAFECOPY(session.username,unknown);
        !          4422: 
        !          4423:        SAFECOPY(session.client.addr,session.host_ip);
        !          4424:        SAFECOPY(session.client.host,session.host_name);
        !          4425:        session.client.port=ntohs(session.addr.sin_port);
        !          4426:        session.client.time=time(NULL);
        !          4427:        session.client.protocol="HTTP";
        !          4428:        session.client.user=session.username;
        !          4429:        session.client.size=sizeof(session.client);
        !          4430:        client_on(session.socket, &session.client, /* update existing client record? */FALSE);
        !          4431: 
        !          4432:        session.last_user_num=-1;
        !          4433:        session.last_js_user_num=-1;
        !          4434:        session.logon_time=0;
        !          4435: 
        !          4436:        session.subscan=(subscan_t*)alloca(sizeof(subscan_t)*scfg.total_subs);
        !          4437: 
        !          4438:        while(!session.finished) {
        !          4439:                init_error=FALSE;
        !          4440:            memset(&(session.req), 0, sizeof(session.req));
        !          4441:                redirp=NULL;
        !          4442:                loop_count=0;
        !          4443:                if(session.req.ld) {
        !          4444:                        FREE_AND_NULL(session.req.ld->hostname);
        !          4445:                        FREE_AND_NULL(session.req.ld->ident);
        !          4446:                        FREE_AND_NULL(session.req.ld->user);
        !          4447:                        FREE_AND_NULL(session.req.ld->request);
        !          4448:                        FREE_AND_NULL(session.req.ld->referrer);
        !          4449:                        FREE_AND_NULL(session.req.ld->agent);
        !          4450:                        FREE_AND_NULL(session.req.ld->vhost);
        !          4451:                        FREE_AND_NULL(session.req.ld);
        !          4452:                }
        !          4453:                if(startup->options&WEB_OPT_HTTP_LOGGING) {
        !          4454:                        /* FREE()d in http_logging_thread... passed there by close_request() */
        !          4455:                        if((session.req.ld=(struct log_data*)malloc(sizeof(struct log_data)))==NULL)
        !          4456:                                lprintf(LOG_ERR,"%04d Cannot allocate memory for log data!",session.socket);
        !          4457:                }
        !          4458:                if(session.req.ld!=NULL) {
        !          4459:                        memset(session.req.ld,0,sizeof(struct log_data));
        !          4460:                        /* FREE()d in http_logging_thread */
        !          4461:                        session.req.ld->hostname=strdup(session.host_name);
        !          4462:                }
        !          4463:                while((redirp==NULL || session.req.send_location >= MOVED_TEMP)
        !          4464:                                 && !session.finished && !session.req.finished 
        !          4465:                                 && session.socket!=INVALID_SOCKET) {
        !          4466:                        SAFECOPY(session.req.status,"200 OK");
        !          4467:                        session.req.send_location=NO_LOCATION;
        !          4468:                        if(session.req.headers==NULL) {
        !          4469:                                /* FREE()d in close_request() */
        !          4470:                                if((session.req.headers=strListInit())==NULL) {
        !          4471:                                        lprintf(LOG_ERR,"%04d !ERROR allocating memory for header list",session.socket);
        !          4472:                                        init_error=TRUE;
        !          4473:                                }
        !          4474:                        }
        !          4475:                        if(session.req.cgi_env==NULL) {
        !          4476:                                /* FREE()d in close_request() */
        !          4477:                                if((session.req.cgi_env=strListInit())==NULL) {
        !          4478:                                        lprintf(LOG_ERR,"%04d !ERROR allocating memory for CGI environment list",session.socket);
        !          4479:                                        init_error=TRUE;
        !          4480:                                }
        !          4481:                        }
        !          4482:                        if(session.req.dynamic_heads==NULL) {
        !          4483:                                /* FREE()d in close_request() */
        !          4484:                                if((session.req.dynamic_heads=strListInit())==NULL) {
        !          4485:                                        lprintf(LOG_ERR,"%04d !ERROR allocating memory for dynamic header list",session.socket);
        !          4486:                                        init_error=TRUE;
        !          4487:                                }
        !          4488:                        }
        !          4489: 
        !          4490:                        if(get_req(&session,redirp)) {
        !          4491:                                if(init_error) {
        !          4492:                                        send_error(&session, error_500);
        !          4493:                                }
        !          4494:                                /* At this point, if redirp is non-NULL then the headers have already been parsed */
        !          4495:                                if((session.http_ver<HTTP_1_0)||redirp!=NULL||parse_headers(&session)) {
        !          4496:                                        if(check_request(&session)) {
        !          4497:                                                if(session.req.send_location < MOVED_TEMP || session.req.virtual_path[0]!='/' || loop_count++ >= MAX_REDIR_LOOPS) {
        !          4498:                                                        if(read_post_data(&session))
        !          4499:                                                                respond(&session);
        !          4500:                                                }
        !          4501:                                                else {
        !          4502:                                                        safe_snprintf(redir_req,sizeof(redir_req),"%s %s%s%s",methods[session.req.method]
        !          4503:                                                                ,session.req.virtual_path,session.http_ver<HTTP_1_0?"":" ",http_vers[session.http_ver]);
        !          4504:                                                        lprintf(LOG_DEBUG,"%04d Internal Redirect to: %s",socket,redir_req);
        !          4505:                                                        redirp=redir_req;
        !          4506:                                                }
        !          4507:                                        }
        !          4508:                                }
        !          4509:                        }
        !          4510:                        else {
        !          4511:                                session.req.keep_alive=FALSE;
        !          4512:                                break;
        !          4513:                        }
        !          4514:                }
        !          4515:                close_request(&session);
        !          4516:        }
        !          4517: 
        !          4518:        http_logoff(&session,socket,__LINE__);
        !          4519: 
        !          4520:        if(session.js_cx!=NULL) {
        !          4521:                lprintf(LOG_INFO,"%04d JavaScript: Destroying context",socket);
        !          4522:                JS_DestroyContext(session.js_cx);       /* Free Context */
        !          4523:                session.js_cx=NULL;
        !          4524:        }
        !          4525: 
        !          4526: #ifndef ONE_JS_RUNTIME
        !          4527:        if(session.js_runtime!=NULL) {
        !          4528:                lprintf(LOG_INFO,"%04d JavaScript: Destroying runtime",socket);
        !          4529:                JS_DestroyRuntime(session.js_runtime);
        !          4530:                session.js_runtime=NULL;
        !          4531:        }
        !          4532: #endif
        !          4533: 
        !          4534: #ifdef _WIN32
        !          4535:        if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
        !          4536:                PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
        !          4537: #endif
        !          4538: 
        !          4539:        close_socket(&session.socket);
        !          4540:        sem_wait(&session.output_thread_terminated);
        !          4541:        sem_destroy(&session.output_thread_terminated);
        !          4542:        RingBufDispose(&session.outbuf);
        !          4543: 
        !          4544:        active_clients--;
        !          4545:        update_clients();
        !          4546:        client_off(socket);
        !          4547: 
        !          4548:        session_threads--;
        !          4549:        thread_down();
        !          4550: 
        !          4551:        if(startup->index_file_name==NULL || startup->cgi_ext==NULL)
        !          4552:                lprintf(LOG_DEBUG,"%04d !!! ALL YOUR BASE ARE BELONG TO US !!!", socket);
        !          4553: 
        !          4554:        lprintf(LOG_INFO,"%04d Session thread terminated (%u clients, %u threads remain, %lu served)"
        !          4555:                ,socket, active_clients, thread_count, served);
        !          4556: 
        !          4557: }
        !          4558: 
        !          4559: void DLLCALL web_terminate(void)
        !          4560: {
        !          4561:        lprintf(LOG_INFO,"%04d Web Server terminate",server_socket);
        !          4562:        terminate_server=TRUE;
        !          4563: }
        !          4564: 
        !          4565: static void cleanup(int code)
        !          4566: {
        !          4567:        while(session_threads) {
        !          4568:                lprintf(LOG_INFO,"#### Web Server waiting on %d active session threads",session_threads);
        !          4569:                SLEEP(1000);
        !          4570:        }
        !          4571:        free_cfg(&scfg);
        !          4572: 
        !          4573:        listFree(&log_list);
        !          4574: 
        !          4575:        mime_types=iniFreeNamedStringList(mime_types);
        !          4576: 
        !          4577:        cgi_handlers=iniFreeNamedStringList(cgi_handlers);
        !          4578:        xjs_handlers=iniFreeNamedStringList(xjs_handlers);
        !          4579: 
        !          4580:        semfile_list_free(&recycle_semfiles);
        !          4581:        semfile_list_free(&shutdown_semfiles);
        !          4582: 
        !          4583:        if(server_socket!=INVALID_SOCKET) {
        !          4584:                close_socket(&server_socket);
        !          4585:        }
        !          4586: 
        !          4587:        update_clients();
        !          4588: 
        !          4589: #ifdef _WINSOCKAPI_
        !          4590:        if(WSAInitialized && WSACleanup()!=0) 
        !          4591:                lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
        !          4592: #endif
        !          4593: 
        !          4594:        thread_down();
        !          4595:        status("Down");
        !          4596:        if(terminate_server || code)
        !          4597:                lprintf(LOG_INFO,"#### Web Server thread terminated (%u threads remain, %lu clients served)"
        !          4598:                        ,thread_count, served);
        !          4599:        if(startup!=NULL && startup->terminated!=NULL)
        !          4600:                startup->terminated(startup->cbdata,code);
        !          4601: }
        !          4602: 
        !          4603: const char* DLLCALL web_ver(void)
        !          4604: {
        !          4605:        static char ver[256];
        !          4606:        char compiler[32];
        !          4607: 
        !          4608:        DESCRIBE_COMPILER(compiler);
        !          4609: 
        !          4610:        sscanf("$Revision: 1.458 $", "%*s %s", revision);
        !          4611: 
        !          4612:        sprintf(ver,"%s %s%s  "
        !          4613:                "Compiled %s %s with %s"
        !          4614:                ,server_name
        !          4615:                ,revision
        !          4616: #ifdef _DEBUG
        !          4617:                ," Debug"
        !          4618: #else
        !          4619:                ,""
        !          4620: #endif
        !          4621:                ,__DATE__, __TIME__, compiler);
        !          4622: 
        !          4623:        return(ver);
        !          4624: }
        !          4625: 
        !          4626: void http_logging_thread(void* arg)
        !          4627: {
        !          4628:        char    base[MAX_PATH+1];
        !          4629:        char    filename[MAX_PATH+1];
        !          4630:        char    newfilename[MAX_PATH+1];
        !          4631:        FILE*   logfile=NULL;
        !          4632: 
        !          4633:        http_logging_thread_running=TRUE;
        !          4634:        terminate_http_logging_thread=FALSE;
        !          4635: 
        !          4636:        SAFECOPY(base,arg);
        !          4637:        if(!base[0])
        !          4638:                SAFEPRINTF(base,"%slogs/http-",scfg.logs_dir);
        !          4639: 
        !          4640:        filename[0]=0;
        !          4641:        newfilename[0]=0;
        !          4642: 
        !          4643:        thread_up(TRUE /* setuid */);
        !          4644: 
        !          4645:        lprintf(LOG_DEBUG,"%04d http logging thread started", server_socket);
        !          4646: 
        !          4647:        for(;;) {
        !          4648:                struct log_data *ld;
        !          4649:                char    timestr[128];
        !          4650:                char    sizestr[100];
        !          4651: 
        !          4652:                if(!listSemTryWait(&log_list)) {
        !          4653:                        if(logfile!=NULL)
        !          4654:                                fflush(logfile);
        !          4655:                        listSemWait(&log_list);
        !          4656:                }
        !          4657: 
        !          4658:                ld=listShiftNode(&log_list);
        !          4659:                /*
        !          4660:                 * Because the sem is posted when terminate_http_logging_thread is set, this will
        !          4661:                 * ensure that all pending log entries are written to disk
        !          4662:                 */
        !          4663:                if(ld==NULL) {
        !          4664:                        if(terminate_http_logging_thread)
        !          4665:                                break;
        !          4666:                        lprintf(LOG_ERR,"%04d http logging thread received NULL linked list log entry"
        !          4667:                                ,server_socket);
        !          4668:                        continue;
        !          4669:                }
        !          4670:                SAFECOPY(newfilename,base);
        !          4671:                if(startup->options&WEB_OPT_VIRTUAL_HOSTS && ld->vhost!=NULL) {
        !          4672:                        strcat(newfilename,ld->vhost);
        !          4673:                        if(ld->vhost[0])
        !          4674:                                strcat(newfilename,"-");
        !          4675:                }
        !          4676:                strftime(strchr(newfilename,0),15,"%Y-%m-%d.log",&ld->completed);
        !          4677:                if(strcmp(newfilename,filename)) {
        !          4678:                        if(logfile!=NULL)
        !          4679:                                fclose(logfile);
        !          4680:                        SAFECOPY(filename,newfilename);
        !          4681:                        logfile=fopen(filename,"ab");
        !          4682:                        lprintf(LOG_INFO,"%04d http logfile is now: %s",server_socket,filename);
        !          4683:                }
        !          4684:                if(logfile!=NULL) {
        !          4685:                        if(ld->status) {
        !          4686:                                sprintf(sizestr,"%d",ld->size);
        !          4687:                                strftime(timestr,sizeof(timestr),"%d/%b/%Y:%H:%M:%S %z",&ld->completed);
        !          4688:                                /*
        !          4689:                                 * In case of a termination, do no block for a lock... just discard
        !          4690:                                 * the output.
        !          4691:                                 */
        !          4692:                                while(lock(fileno(logfile),0,1) && !terminate_http_logging_thread) {
        !          4693:                                        SLEEP(10);
        !          4694:                                }
        !          4695:                                fprintf(logfile,"%s %s %s [%s] \"%s\" %d %s \"%s\" \"%s\"\n"
        !          4696:                                                ,ld->hostname?(ld->hostname[0]?ld->hostname:"-"):"-"
        !          4697:                                                ,ld->ident?(ld->ident[0]?ld->ident:"-"):"-"
        !          4698:                                                ,ld->user?(ld->user[0]?ld->user:"-"):"-"
        !          4699:                                                ,timestr
        !          4700:                                                ,ld->request?(ld->request[0]?ld->request:"-"):"-"
        !          4701:                                                ,ld->status
        !          4702:                                                ,ld->size?sizestr:"-"
        !          4703:                                                ,ld->referrer?(ld->referrer[0]?ld->referrer:"-"):"-"
        !          4704:                                                ,ld->agent?(ld->agent[0]?ld->agent:"-"):"-");
        !          4705:                                unlock(fileno(logfile),0,1);
        !          4706:                        }
        !          4707:                }
        !          4708:                else {
        !          4709:                        logfile=fopen(filename,"ab");
        !          4710:                        lprintf(LOG_ERR,"%04d http logfile %s was not open!",server_socket,filename);
        !          4711:                }
        !          4712:                FREE_AND_NULL(ld->hostname);
        !          4713:                FREE_AND_NULL(ld->ident);
        !          4714:                FREE_AND_NULL(ld->user);
        !          4715:                FREE_AND_NULL(ld->request);
        !          4716:                FREE_AND_NULL(ld->referrer);
        !          4717:                FREE_AND_NULL(ld->agent);
        !          4718:                FREE_AND_NULL(ld->vhost);
        !          4719:                FREE_AND_NULL(ld);
        !          4720:        }
        !          4721:        if(logfile!=NULL) {
        !          4722:                fclose(logfile);
        !          4723:                logfile=NULL;
        !          4724:        }
        !          4725:        thread_down();
        !          4726:        lprintf(LOG_DEBUG,"%04d http logging thread terminated",server_socket);
        !          4727: 
        !          4728:        http_logging_thread_running=FALSE;
        !          4729: }
        !          4730: 
        !          4731: void DLLCALL web_server(void* arg)
        !          4732: {
        !          4733:        int                             i;
        !          4734:        int                             result;
        !          4735:        time_t                  start;
        !          4736:        WORD                    host_port;
        !          4737:        char                    host_ip[32];
        !          4738:        char                    path[MAX_PATH+1];
        !          4739:        char                    logstr[256];
        !          4740:        SOCKADDR_IN             server_addr={0};
        !          4741:        SOCKADDR_IN             client_addr;
        !          4742:        socklen_t               client_addr_len;
        !          4743:        SOCKET                  client_socket;
        !          4744:        SOCKET                  high_socket_set;
        !          4745:        fd_set                  socket_set;
        !          4746:        time_t                  t;
        !          4747:        time_t                  initialized=0;
        !          4748:        char*                   p;
        !          4749:        char                    compiler[32];
        !          4750:        http_session_t *        session=NULL;
        !          4751:        struct timeval tv;
        !          4752: #ifdef ONE_JS_RUNTIME
        !          4753:        JSRuntime*      js_runtime;
        !          4754: #endif
        !          4755: #ifdef SO_ACCEPTFILTER
        !          4756:        struct accept_filter_arg afa;
        !          4757: #endif
        !          4758: 
        !          4759:        startup=(web_startup_t*)arg;
        !          4760: 
        !          4761:        web_ver();      /* get CVS revision */
        !          4762: 
        !          4763:     if(startup==NULL) {
        !          4764:        sbbs_beep(100,500);
        !          4765:        fprintf(stderr, "No startup structure passed!\n");
        !          4766:        return;
        !          4767:     }
        !          4768: 
        !          4769:        if(startup->size!=sizeof(web_startup_t)) {      /* verify size */
        !          4770:                sbbs_beep(100,500);
        !          4771:                sbbs_beep(300,500);
        !          4772:                sbbs_beep(100,500);
        !          4773:                fprintf(stderr, "Invalid startup structure!\n");
        !          4774:                return;
        !          4775:        }
        !          4776: 
        !          4777: #ifdef _THREAD_SUID_BROKEN
        !          4778:        if(thread_suid_broken)
        !          4779:                startup->seteuid(TRUE);
        !          4780: #endif
        !          4781: 
        !          4782:        /* Setup intelligent defaults */
        !          4783:        if(startup->port==0)                                    startup->port=IPPORT_HTTP;
        !          4784:        if(startup->root_dir[0]==0)                             SAFECOPY(startup->root_dir,WEB_DEFAULT_ROOT_DIR);
        !          4785:        if(startup->error_dir[0]==0)                    SAFECOPY(startup->error_dir,WEB_DEFAULT_ERROR_DIR);
        !          4786:        if(startup->cgi_dir[0]==0)                              SAFECOPY(startup->cgi_dir,WEB_DEFAULT_CGI_DIR);
        !          4787:        if(startup->default_cgi_content[0]==0)  SAFECOPY(startup->default_cgi_content,WEB_DEFAULT_CGI_CONTENT);
        !          4788:        if(startup->max_inactivity==0)                  startup->max_inactivity=120; /* seconds */
        !          4789:        if(startup->max_cgi_inactivity==0)              startup->max_cgi_inactivity=120; /* seconds */
        !          4790:        if(startup->sem_chk_freq==0)                    startup->sem_chk_freq=2; /* seconds */
        !          4791:        if(startup->js.max_bytes==0)                    startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
        !          4792:        if(startup->js.cx_stack==0)                             startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
        !          4793:        if(startup->ssjs_ext[0]==0)                             SAFECOPY(startup->ssjs_ext,".ssjs");
        !          4794:        if(startup->js_ext[0]==0)                               SAFECOPY(startup->js_ext,".bbs");
        !          4795: 
        !          4796:        ZERO_VAR(js_server_props);
        !          4797:        SAFEPRINTF2(js_server_props.version,"%s %s",server_name,revision);
        !          4798:        js_server_props.version_detail=web_ver();
        !          4799:        js_server_props.clients=&active_clients;
        !          4800:        js_server_props.options=&startup->options;
        !          4801:        js_server_props.interface_addr=&startup->interface_addr;
        !          4802: 
        !          4803:        uptime=0;
        !          4804:        served=0;
        !          4805:        startup->recycle_now=FALSE;
        !          4806:        startup->shutdown_now=FALSE;
        !          4807:        terminate_server=FALSE;
        !          4808: 
        !          4809:        do {
        !          4810: 
        !          4811:                thread_up(FALSE /* setuid */);
        !          4812: 
        !          4813:                status("Initializing");
        !          4814: 
        !          4815:                /* Copy html directories */
        !          4816:                SAFECOPY(root_dir,startup->root_dir);
        !          4817:                SAFECOPY(error_dir,startup->error_dir);
        !          4818:                SAFECOPY(cgi_dir,startup->cgi_dir);
        !          4819:                if(startup->temp_dir[0])
        !          4820:                        SAFECOPY(temp_dir,startup->temp_dir);
        !          4821:                else
        !          4822:                        SAFECOPY(temp_dir,"../temp");
        !          4823: 
        !          4824:                /* Change to absolute path */
        !          4825:                prep_dir(startup->ctrl_dir, root_dir, sizeof(root_dir));
        !          4826:                prep_dir(startup->ctrl_dir, temp_dir, sizeof(temp_dir));
        !          4827:                prep_dir(root_dir, error_dir, sizeof(error_dir));
        !          4828:                prep_dir(root_dir, cgi_dir, sizeof(cgi_dir));
        !          4829: 
        !          4830:                /* Trim off trailing slash/backslash */
        !          4831:                if(IS_PATH_DELIM(*(p=lastchar(root_dir))))      *p=0;
        !          4832: 
        !          4833:                memset(&scfg, 0, sizeof(scfg));
        !          4834: 
        !          4835:                lprintf(LOG_INFO,"%s Revision %s%s"
        !          4836:                        ,server_name
        !          4837:                        ,revision
        !          4838: #ifdef _DEBUG
        !          4839:                        ," Debug"
        !          4840: #else
        !          4841:                        ,""
        !          4842: #endif
        !          4843:                        );
        !          4844: 
        !          4845:                DESCRIBE_COMPILER(compiler);
        !          4846: 
        !          4847:                lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
        !          4848: 
        !          4849:                if(!winsock_startup()) {
        !          4850:                        cleanup(1);
        !          4851:                        return;
        !          4852:                }
        !          4853: 
        !          4854:                t=time(NULL);
        !          4855:                lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
        !          4856:                        ,CTIME_R(&t,logstr),startup->options);
        !          4857: 
        !          4858:                if(chdir(startup->ctrl_dir)!=0)
        !          4859:                        lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir);
        !          4860: 
        !          4861:                /* Initial configuration and load from CNF files */
        !          4862:                SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
        !          4863:                lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
        !          4864:                scfg.size=sizeof(scfg);
        !          4865:                SAFECOPY(logstr,UNKNOWN_LOAD_ERROR);
        !          4866:                if(!load_cfg(&scfg, NULL, TRUE, logstr)) {
        !          4867:                        lprintf(LOG_ERR,"!ERROR %s",logstr);
        !          4868:                        lprintf(LOG_ERR,"!FAILED to load configuration files");
        !          4869:                        cleanup(1);
        !          4870:                        return;
        !          4871:                }
        !          4872:                scfg_reloaded=TRUE;
        !          4873: 
        !          4874:                lprintf(LOG_DEBUG,"Temporary file directory: %s", temp_dir);
        !          4875:                MKDIR(temp_dir);
        !          4876:                if(!isdir(temp_dir)) {
        !          4877:                        lprintf(LOG_ERR,"!Invalid temp directory: %s", temp_dir);
        !          4878:                        cleanup(1);
        !          4879:                        return;
        !          4880:                }
        !          4881:                lprintf(LOG_DEBUG,"Root directory: %s", root_dir);
        !          4882:                lprintf(LOG_DEBUG,"Error directory: %s", error_dir);
        !          4883:                lprintf(LOG_DEBUG,"CGI directory: %s", cgi_dir);
        !          4884: 
        !          4885:                mime_types=read_ini_list("mime_types.ini",NULL /* root section */,"MIME types"
        !          4886:                        ,mime_types);
        !          4887:                cgi_handlers=read_ini_list("web_handler.ini","CGI","CGI content handlers"
        !          4888:                        ,cgi_handlers);
        !          4889:                xjs_handlers=read_ini_list("web_handler.ini","JavaScript","JavaScript content handlers"
        !          4890:                        ,xjs_handlers);
        !          4891: 
        !          4892:                /* Don't do this for *each* CGI request, just once here during [re]init */
        !          4893:                iniFileName(cgi_env_ini,sizeof(cgi_env_ini),scfg.ctrl_dir,"cgi_env.ini");
        !          4894: 
        !          4895:                if(startup->host_name[0]==0)
        !          4896:                        SAFECOPY(startup->host_name,scfg.sys_inetaddr);
        !          4897: 
        !          4898:                if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&BBS_OPT_LOCAL_TIMEZONE)) {
        !          4899:                        if(putenv("TZ=UTC0"))
        !          4900:                                lprintf(LOG_WARNING,"!putenv() FAILED");
        !          4901:                        tzset();
        !          4902:                }
        !          4903: 
        !          4904:                if(uptime==0)
        !          4905:                        uptime=time(NULL);      /* this must be done *after* setting the timezone */
        !          4906: 
        !          4907:                active_clients=0;
        !          4908:                update_clients();
        !          4909: 
        !          4910:                /* open a socket and wait for a client */
        !          4911: 
        !          4912:                server_socket = open_socket(SOCK_STREAM);
        !          4913: 
        !          4914:                if(server_socket == INVALID_SOCKET) {
        !          4915:                        lprintf(LOG_ERR,"!ERROR %d creating HTTP socket", ERROR_VALUE);
        !          4916:                        cleanup(1);
        !          4917:                        return;
        !          4918:                }
        !          4919:                
        !          4920: /*
        !          4921:  *             i=1;
        !          4922:  *             if(setsockopt(server_socket, IPPROTO_TCP, TCP_NOPUSH, &i, sizeof(i)))
        !          4923:  *                     lprintf("Cannot set TCP_NOPUSH socket option");
        !          4924:  */
        !          4925: 
        !          4926: #ifdef SO_ACCEPTFILTER
        !          4927:                memset(&afa, 0, sizeof(afa));
        !          4928:                strcpy(afa.af_name, "httpready");
        !          4929:                setsockopt(server_socket, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
        !          4930: #endif
        !          4931: 
        !          4932:                lprintf(LOG_INFO,"%04d Web Server socket opened",server_socket);
        !          4933: 
        !          4934:                /*****************************/
        !          4935:                /* Listen for incoming calls */
        !          4936:                /*****************************/
        !          4937:                memset(&server_addr, 0, sizeof(server_addr));
        !          4938: 
        !          4939:                server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
        !          4940:                server_addr.sin_family = AF_INET;
        !          4941:                server_addr.sin_port   = htons(startup->port);
        !          4942: 
        !          4943:                if(startup->seteuid!=NULL)
        !          4944:                        startup->seteuid(FALSE);
        !          4945:                result = retry_bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
        !          4946:                        ,startup->bind_retry_count,startup->bind_retry_delay,"Web Server",lprintf);
        !          4947:                if(startup->seteuid!=NULL)
        !          4948:                        startup->seteuid(TRUE);
        !          4949:                if(result != 0) {
        !          4950:                        lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
        !          4951:                        cleanup(1);
        !          4952:                        return;
        !          4953:                }
        !          4954: 
        !          4955:                result = listen(server_socket, 64);
        !          4956: 
        !          4957:                if(result != 0) {
        !          4958:                        lprintf(LOG_ERR,"%04d !ERROR %d (%d) listening on socket"
        !          4959:                                ,server_socket, result, ERROR_VALUE);
        !          4960:                        cleanup(1);
        !          4961:                        return;
        !          4962:                }
        !          4963:                lprintf(LOG_INFO,"%04d Web Server listening on port %d"
        !          4964:                        ,server_socket, startup->port);
        !          4965:                status("Listening");
        !          4966: 
        !          4967:                lprintf(LOG_INFO,"%04d Web Server thread started", server_socket);
        !          4968: 
        !          4969:                listInit(&log_list,/* flags */ LINK_LIST_MUTEX|LINK_LIST_SEMAPHORE);
        !          4970:                if(startup->options&WEB_OPT_HTTP_LOGGING) {
        !          4971:                        /********************/
        !          4972:                        /* Start log thread */
        !          4973:                        /********************/
        !          4974:                        _beginthread(http_logging_thread, 0, startup->logfile_base);
        !          4975:                }
        !          4976: 
        !          4977: #ifdef ONE_JS_RUNTIME
        !          4978:            if(js_runtime == NULL) {
        !          4979:            lprintf(LOG_INFO,"%04d JavaScript: Creating runtime: %lu bytes"
        !          4980:                    ,server_socket,startup->js.max_bytes);
        !          4981: 
        !          4982:            if((js_runtime=JS_NewRuntime(startup->js.max_bytes))==NULL) {
        !          4983:                    lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",server_socket);
        !          4984:                                /* Sleep 15 seconds then try again */
        !          4985:                                /* ToDo: Something better should be used here. */
        !          4986:                                SLEEP(15000);
        !          4987:                                continue;
        !          4988:                }
        !          4989:        }
        !          4990: #endif
        !          4991: 
        !          4992:                /* Setup recycle/shutdown semaphore file lists */
        !          4993:                shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","web");
        !          4994:                recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","web");
        !          4995:                SAFEPRINTF(path,"%swebsrvr.rec",scfg.ctrl_dir); /* legacy */
        !          4996:                semfile_list_add(&recycle_semfiles,path);
        !          4997:                if(!initialized) {
        !          4998:                        initialized=time(NULL);
        !          4999:                        semfile_list_check(&initialized,recycle_semfiles);
        !          5000:                        semfile_list_check(&initialized,shutdown_semfiles);
        !          5001:                }
        !          5002: 
        !          5003:                /* signal caller that we've started up successfully */
        !          5004:                if(startup->started!=NULL)
        !          5005:                startup->started(startup->cbdata);
        !          5006: 
        !          5007:                while(server_socket!=INVALID_SOCKET && !terminate_server) {
        !          5008: 
        !          5009:                        /* check for re-cycle/shutdown semaphores */
        !          5010:                        if(active_clients==0) {
        !          5011:                                if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
        !          5012:                                        if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
        !          5013:                                                lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected"
        !          5014:                                                        ,server_socket,p);
        !          5015:                                                if(session!=NULL) {
        !          5016:                                                        pthread_mutex_unlock(&session->struct_filled);
        !          5017:                                                        session=NULL;
        !          5018:                                                }
        !          5019:                                                break;
        !          5020:                                        }
        !          5021: #if 0  /* unused */
        !          5022:                                        if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0)
        !          5023:                                                startup->recycle_now=TRUE;
        !          5024: #endif
        !          5025:                                        if(startup->recycle_now==TRUE) {
        !          5026:                                                lprintf(LOG_INFO,"%04d Recycle semaphore signaled",server_socket);
        !          5027:                                                startup->recycle_now=FALSE;
        !          5028:                                                if(session!=NULL) {
        !          5029:                                                        pthread_mutex_unlock(&session->struct_filled);
        !          5030:                                                        session=NULL;
        !          5031:                                                }
        !          5032:                                                break;
        !          5033:                                        }
        !          5034:                                }
        !          5035:                                if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
        !          5036:                                                && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected"
        !          5037:                                                        ,server_socket,p))
        !          5038:                                        || (startup->shutdown_now==TRUE
        !          5039:                                                && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled"
        !          5040:                                                        ,server_socket))) {
        !          5041:                                        startup->shutdown_now=FALSE;
        !          5042:                                        terminate_server=TRUE;
        !          5043:                                        if(session!=NULL) {
        !          5044:                                                pthread_mutex_unlock(&session->struct_filled);
        !          5045:                                                session=NULL;
        !          5046:                                        }
        !          5047:                                        break;
        !          5048:                                }
        !          5049:                        }       
        !          5050: 
        !          5051:                        /* Startup next session thread */
        !          5052:                        if(session==NULL) {
        !          5053:                                /* FREE()d at the start of the session thread */
        !          5054:                                if((session=malloc(sizeof(http_session_t)))==NULL) {
        !          5055:                                        lprintf(LOG_CRIT,"%04d !ERROR allocating %u bytes of memory for http_session_t"
        !          5056:                                                ,client_socket, sizeof(http_session_t));
        !          5057:                                        mswait(3000);
        !          5058:                                        continue;
        !          5059:                                }
        !          5060:                                memset(session, 0, sizeof(http_session_t));
        !          5061:                                session->socket=INVALID_SOCKET;
        !          5062:                                /* Destroyed in http_session_thread */
        !          5063:                                pthread_mutex_init(&session->struct_filled,NULL);
        !          5064:                                pthread_mutex_lock(&session->struct_filled);
        !          5065:                                session_threads++;
        !          5066:                                _beginthread(http_session_thread, 0, session);
        !          5067:                        }
        !          5068: 
        !          5069:                        /* now wait for connection */
        !          5070: 
        !          5071:                        FD_ZERO(&socket_set);
        !          5072:                        FD_SET(server_socket,&socket_set);
        !          5073:                        high_socket_set=server_socket+1;
        !          5074: 
        !          5075:                        tv.tv_sec=startup->sem_chk_freq;
        !          5076:                        tv.tv_usec=0;
        !          5077: 
        !          5078:                        if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
        !          5079:                                if(i==0)
        !          5080:                                        continue;
        !          5081:                                if(ERROR_VALUE==EINTR)
        !          5082:                                        lprintf(LOG_DEBUG,"Web Server listening interrupted");
        !          5083:                                else if(ERROR_VALUE == ENOTSOCK)
        !          5084:                        lprintf(LOG_INFO,"Web Server socket closed");
        !          5085:                                else
        !          5086:                                        lprintf(LOG_WARNING,"!ERROR %d selecting socket",ERROR_VALUE);
        !          5087:                                continue;
        !          5088:                        }
        !          5089: 
        !          5090:                        if(server_socket==INVALID_SOCKET) {     /* terminated */
        !          5091:                                pthread_mutex_unlock(&session->struct_filled);
        !          5092:                                session=NULL;
        !          5093:                                break;
        !          5094:                        }
        !          5095: 
        !          5096:                        client_addr_len = sizeof(client_addr);
        !          5097: 
        !          5098:                        if(server_socket!=INVALID_SOCKET
        !          5099:                                && FD_ISSET(server_socket,&socket_set)) {
        !          5100:                                client_socket = accept(server_socket, (struct sockaddr *)&client_addr
        !          5101:                                ,&client_addr_len);
        !          5102:                        }
        !          5103:                        else {
        !          5104:                                lprintf(LOG_NOTICE,"!NO SOCKETS set by select");
        !          5105:                                continue;
        !          5106:                        }
        !          5107: 
        !          5108:                        if(client_socket == INVALID_SOCKET)     {
        !          5109:                                lprintf(LOG_WARNING,"!ERROR %d accepting connection", ERROR_VALUE);
        !          5110: #ifdef _WIN32
        !          5111:                                if(WSAGetLastError()==WSAENOBUFS) {     /* recycle (re-init WinSock) on this error */
        !          5112:                                        pthread_mutex_unlock(&session->struct_filled);
        !          5113:                                        session=NULL;
        !          5114:                                        break;
        !          5115:                                }
        !          5116: #endif
        !          5117:                                continue;
        !          5118:                        }
        !          5119: 
        !          5120:                        if(startup->socket_open!=NULL)
        !          5121:                                startup->socket_open(startup->cbdata,TRUE);
        !          5122: 
        !          5123:                        SAFECOPY(host_ip,inet_ntoa(client_addr.sin_addr));
        !          5124: 
        !          5125:                        if(trashcan(&scfg,host_ip,"ip-silent")) {
        !          5126:                                close_socket(&client_socket);
        !          5127:                                continue;
        !          5128:                        }
        !          5129: 
        !          5130:                        if(startup->max_clients && active_clients>=startup->max_clients) {
        !          5131:                                lprintf(LOG_WARNING,"%04d !MAXIMUM CLIENTS (%d) reached, access denied"
        !          5132:                                        ,client_socket, startup->max_clients);
        !          5133:                                mswait(3000);
        !          5134:                                close_socket(&client_socket);
        !          5135:                                continue;
        !          5136:                        }
        !          5137: 
        !          5138:                        host_port=ntohs(client_addr.sin_port);
        !          5139: 
        !          5140:                        lprintf(LOG_INFO,"%04d HTTP connection accepted from: %s port %u"
        !          5141:                                ,client_socket
        !          5142:                                ,host_ip, host_port);
        !          5143: 
        !          5144:                        SAFECOPY(session->host_ip,host_ip);
        !          5145:                        session->addr=client_addr;
        !          5146:                        session->socket=client_socket;
        !          5147:                        session->js_branch.auto_terminate=TRUE;
        !          5148:                        session->js_branch.terminated=&terminate_server;
        !          5149:                        session->js_branch.limit=startup->js.branch_limit;
        !          5150:                        session->js_branch.gc_interval=startup->js.gc_interval;
        !          5151:                        session->js_branch.yield_interval=startup->js.yield_interval;
        !          5152: #ifdef ONE_JS_RUNTIME
        !          5153:                        session->js_runtime=js_runtime;
        !          5154: #endif
        !          5155: 
        !          5156:                        pthread_mutex_unlock(&session->struct_filled);
        !          5157:                        session=NULL;
        !          5158:                        served++;
        !          5159:                }
        !          5160: 
        !          5161:                if(session) {
        !          5162:                        pthread_mutex_unlock(&session->struct_filled);
        !          5163:                        session=NULL;
        !          5164:                }
        !          5165: 
        !          5166:                /* Wait for active clients to terminate */
        !          5167:                if(active_clients) {
        !          5168:                        lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..."
        !          5169:                                ,server_socket, active_clients);
        !          5170:                        start=time(NULL);
        !          5171:                        while(active_clients) {
        !          5172:                                if(time(NULL)-start>startup->max_inactivity) {
        !          5173:                                        lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients"
        !          5174:                                                ,server_socket, active_clients);
        !          5175:                                        break;
        !          5176:                                }
        !          5177:                                mswait(100);
        !          5178:                        }
        !          5179:                }
        !          5180: 
        !          5181:                if(http_logging_thread_running) {
        !          5182:                        terminate_http_logging_thread=TRUE;
        !          5183:                        listSemPost(&log_list);
        !          5184:                        mswait(100);
        !          5185:                }
        !          5186:                if(http_logging_thread_running) {
        !          5187:                        lprintf(LOG_DEBUG,"%04d Waiting for HTTP logging thread to terminate..."
        !          5188:                                ,server_socket);
        !          5189:                        start=time(NULL);
        !          5190:                        while(http_logging_thread_running) {
        !          5191:                                if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
        !          5192:                                        lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for HTTP logging thread to "
        !          5193:                                "terminate", server_socket);
        !          5194:                                        break;
        !          5195:                                }
        !          5196:                                mswait(100);
        !          5197:                        }
        !          5198:                }
        !          5199: 
        !          5200: #ifdef ONE_JS_RUNTIME
        !          5201:        if(js_runtime!=NULL) {
        !          5202:                lprintf(LOG_INFO,"%04d JavaScript: Destroying runtime",server_socket);
        !          5203:                JS_DestroyRuntime(js_runtime);
        !          5204:            js_runtime=NULL;
        !          5205:            }
        !          5206: #endif
        !          5207: 
        !          5208:                cleanup(0);
        !          5209: 
        !          5210:                if(!terminate_server) {
        !          5211:                        lprintf(LOG_INFO,"Recycling server...");
        !          5212:                        mswait(2000);
        !          5213:                        if(startup->recycle!=NULL)
        !          5214:                                startup->recycle(startup->cbdata);
        !          5215:                }
        !          5216: 
        !          5217:        } while(!terminate_server);
        !          5218: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.