Annotation of sbbs/src/sbbs3/websrvr.c, revision 1.1.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.