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

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

unix.superglobalmegacorp.com

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