Annotation of sbbs/sbbs3/websrvr.c, revision 1.1.1.1

1.1       root        1: /* websrvr.c */
                      2: 
                      3: /* Synchronet Web Server */
                      4: 
                      5: /* $Id: websrvr.c,v 1.247 2004/12/18 00:40:22 deuce Exp $ */
                      6: 
                      7: /****************************************************************************
                      8:  * @format.tab-size 4          (Plain Text/Source Code File Header)                    *
                      9:  * @format.use-tabs true       (see http://www.synchro.net/ptsc_hdr.html)              *
                     10:  *                                                                                                                                                     *
                     11:  * Copyright 2004 Rob Swindell - http://www.synchro.net/copyright.html         *
                     12:  *                                                                                                                                                     *
                     13:  * This program is free software; you can redistribute it and/or                       *
                     14:  * modify it under the terms of the GNU General Public License                         *
                     15:  * as published by the Free Software Foundation; either version 2                      *
                     16:  * of the License, or (at your option) any later version.                                      *
                     17:  * See the GNU General Public License for more details: gpl.txt or                     *
                     18:  * http://www.fsf.org/copyleft/gpl.html                                                                                *
                     19:  *                                                                                                                                                     *
                     20:  * Anonymous FTP access to the most recent released source is available at     *
                     21:  * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net     *
                     22:  *                                                                                                                                                     *
                     23:  * Anonymous CVS access to the development source and modification history     *
                     24:  * is available at cvs.synchro.net:/cvsroot/sbbs, example:                                     *
                     25:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs login                       *
                     26:  *     (just hit return, no password is necessary)                                                     *
                     27:  * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src                *
                     28:  *                                                                                                                                                     *
                     29:  * For Synchronet coding style and modification guidelines, see                                *
                     30:  * http://www.synchro.net/source.html                                                                          *
                     31:  *                                                                                                                                                     *
                     32:  * You are encouraged to submit any modifications (preferably in Unix diff     *
                     33:  * format) via e-mail to [email protected]                                                                      *
                     34:  *                                                                                                                                                     *
                     35:  * Note: If this box doesn't appear square, then you need to fix your tabs.    *
                     36:  ****************************************************************************/
                     37: 
                     38: /*
                     39:  * General notes: (ToDo stuff)
                     40:  * strtok() is used a LOT in here... notice that there is a strtok_r() for reentrant...
                     41:  * this may imply that strtok() is NOT thread-safe... if in fact it isn't this HAS
                     42:  * to be fixed before any real production-level quality is achieved with this web server
                     43:  * however, strtok_r() may not be a standard function.
                     44:  *
                     45:  * RE: not sending the headers if an nph scrpit is detected.  (The headers buffer could
                     46:  * just be free()ed and NULLed)
                     47:  *
                     48:  * Currently, all SSJS requests for a session are ran in the same context without clearing the context in
                     49:  * any way.  This behaviour should not be relied on as it may disappear in the future... this will require
                     50:  * some thought as it COULD be handy in some circumstances and COULD cause weird bugs in others.
                     51:  *
                     52:  * Dynamic content is always resent on an If-Modified-Since request... this may not be optimal behaviour
                     53:  * for GET requests...
                     54:  *
                     55:  * Should support RFC2617 Digest auth.
                     56:  *
                     57:  * Fix up all the logging stuff.
                     58:  *
                     59:  * SSJS stuff could work using three different methods:
                     60:  * 1) Temporary file as happens currently
                     61:  *             Advantages:
                     62:  *                     Allows to keep current connection (keep-alive works)
                     63:  *                     write() doesn't need to be "special"
                     64:  *             Disadvantages:
                     65:  *                     Depends on the temp dir being writable and capable of holding
                     66:  *                             the full reply
                     67:  *                     Everything goes throug the disk, so probobly some performance
                     68:  *                             penalty is involved
                     69:  *                     No way of sending directly to the remote system
                     70:  * 2) nph- style
                     71:  *             Advantages:
                     72:  *                     No file I/O involved
                     73:  *                     Can do magic tricks (ala my perl web wrapper)
                     74:  *             Disadvantages:
                     75:  *                     Pretty much everything needs to be handled by the script.
                     76:  * 3) Return body in http_reply object
                     77:  *             All the advantages of 1)
                     78:  *             Could use a special write() to make everything just great.
                     79:  *             Still doesn't allow page to be sent until fully composed (ie: long
                     80:  *                     delays)
                     81:  * 4) Type three with a callback that sends the header and current body, then
                     82:  *             converts write() to send directly to remote.
                     83:  *
                     84:  * Add in support to pass connections through to a different webserver...
                     85:  *      probobly in access.ars... with like a simplified mod_rewrite.
                     86:  *      This would allow people to run apache and Synchronet as the same site.
                     87:  */
                     88: 
                     89: /* Headers for CGI stuff */
                     90: #if defined(__unix__)
                     91:        #include <sys/wait.h>           /* waitpid() */
                     92:        #include <sys/types.h>
                     93:        #include <signal.h>                     /* kill() */
                     94: #endif
                     95: 
                     96: #ifndef JAVASCRIPT
                     97: #define JAVASCRIPT
                     98: #endif
                     99: 
                    100: #undef SBBS    /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
                    101: #include "sbbs.h"
                    102: #include "sockwrap.h"          /* sendfilesocket() */
                    103: #include "threadwrap.h"                /* pthread_mutex_t */
                    104: #include "semwrap.h"
                    105: #include "websrvr.h"
                    106: #include "base64.h"
                    107: 
                    108: static const char*     server_name="Synchronet Web Server";
                    109: static const char*     newline="\r\n";
                    110: static const char*     http_scheme="http://";
                    111: static const size_t    http_scheme_len=7;
                    112: static const char*     error_404="404 Not Found";
                    113: static const char*     error_500="500 Internal Server Error";
                    114: static const char*     unknown="<unknown>";
                    115: 
                    116: /* Is this not in a header somewhere? */
                    117: extern const uchar* nular;
                    118: 
                    119: #define TIMEOUT_THREAD_WAIT            60              /* Seconds */
                    120: #define MAX_REQUEST_LINE               1024    /* NOT including terminator */
                    121: #define MAX_HEADERS_SIZE               16384   /* Maximum total size of all headers 
                    122:                                                                                   (Including terminator )*/
                    123: #define MAX_REDIR_LOOPS                        20              /* Max. times to follow internal redirects for a single request */
                    124: 
                    125: static scfg_t  scfg;
                    126: static BOOL            scfg_reloaded=TRUE;
                    127: static BOOL            http_logging_thread_running=FALSE;
                    128: static ulong   active_clients=0;
                    129: static ulong   sockets=0;
                    130: static BOOL            terminate_server=FALSE;
                    131: static BOOL            terminate_http_logging_thread=FALSE;
                    132: static uint            thread_count=0;
                    133: static SOCKET  server_socket=INVALID_SOCKET;
                    134: static char            revision[16];
                    135: static char            root_dir[MAX_PATH+1];
                    136: static char            error_dir[MAX_PATH+1];
                    137: static char            cgi_dir[MAX_PATH+1];
                    138: static time_t  uptime=0;
                    139: static DWORD   served=0;
                    140: static web_startup_t* startup=NULL;
                    141: static js_server_props_t js_server_props;
                    142: static link_list_t recycle_semfiles;
                    143: static link_list_t shutdown_semfiles;
                    144: 
                    145: static named_string_t** mime_types;
                    146: 
                    147: /* Logging stuff */
                    148: sem_t  log_sem;
                    149: pthread_mutex_t        log_mutex;
                    150: link_list_t    log_list;
                    151: struct log_data {
                    152:        char    *hostname;
                    153:        char    *ident;
                    154:        char    *user;
                    155:        char    *request;
                    156:        char    *referrer;
                    157:        char    *agent;
                    158:        int             status;
                    159:        unsigned int    size;
                    160:        struct tm completed;
                    161: };
                    162: 
                    163: typedef struct  {
                    164:        int                     method;
                    165:        char            virtual_path[MAX_PATH+1];
                    166:        char            physical_path[MAX_PATH+1];
                    167:        BOOL            parsed_headers;
                    168:        BOOL            expect_go_ahead;
                    169:        time_t          if_modified_since;
                    170:        BOOL            keep_alive;
                    171:        char            ars[256];
                    172:        char            auth[128];                              /* UserID:Password */
                    173:        char            host[128];                              /* The requested host. (virtual hosts) */
                    174:        int                     send_location;
                    175:        const char*     mime_type;
                    176:        link_list_t     headers;
                    177:        char            status[MAX_REQUEST_LINE+1];
                    178:        char *          post_data;
                    179:        size_t          post_len;
                    180:        int                     dynamic;
                    181:        struct log_data *ld;
                    182:        char            request_line[MAX_REQUEST_LINE+1];
                    183: 
                    184:        /* CGI parameters */
                    185:        char            query_str[MAX_REQUEST_LINE+1];
                    186:        char            extra_path_info[MAX_REQUEST_LINE+1];
                    187:        link_list_t     cgi_env;
                    188:        link_list_t     dynamic_heads;
                    189: 
                    190:        /* Dynamically (sever-side JS) generated HTML parameters */
                    191:        FILE*   fp;
                    192: 
                    193: } http_request_t;
                    194: 
                    195: typedef struct  {
                    196:        SOCKET                  socket;
                    197:        SOCKADDR_IN             addr;
                    198:        http_request_t  req;
                    199:        char                    host_ip[64];
                    200:        char                    host_name[128]; /* Resolved remote host */
                    201:        int                             http_ver;       /* HTTP version.  0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
                    202:        BOOL                    finished;               /* Do not accept any more imput from client */
                    203:        user_t                  user;
                    204:        int                             last_user_num;
                    205:        time_t                  logon_time;
                    206:        char                    username[LEN_NAME+1];
                    207:        int                             last_js_user_num;
                    208: 
                    209:        /* JavaScript parameters */
                    210:        JSRuntime*              js_runtime;
                    211:        JSContext*              js_cx;
                    212:        JSObject*               js_glob;
                    213:        JSObject*               js_query;
                    214:        JSObject*               js_header;
                    215:        JSObject*               js_request;
                    216:        js_branch_t             js_branch;
                    217:        subscan_t               *subscan;
                    218: 
                    219:        /* Client info */
                    220:        client_t                client;
                    221: } http_session_t;
                    222: 
                    223: enum { 
                    224:         HTTP_0_9
                    225:        ,HTTP_1_0
                    226:        ,HTTP_1_1
                    227: };
                    228: static char* http_vers[] = {
                    229:         ""
                    230:        ,"HTTP/1.0"
                    231:        ,"HTTP/1.1"
                    232:        ,NULL   /* terminator */
                    233: };
                    234: 
                    235: enum { 
                    236:         HTTP_HEAD
                    237:        ,HTTP_GET
                    238: };
                    239: 
                    240: static char* methods[] = {
                    241:         "HEAD"
                    242:        ,"GET"
                    243:        ,"POST"
                    244:        ,NULL   /* terminator */
                    245: };
                    246: 
                    247: enum {
                    248:         IS_STATIC
                    249:        ,IS_CGI
                    250:        ,IS_JS
                    251:        ,IS_SSJS
                    252: };
                    253: 
                    254: enum { 
                    255:         HEAD_DATE
                    256:        ,HEAD_HOST
                    257:        ,HEAD_IFMODIFIED
                    258:        ,HEAD_LENGTH
                    259:        ,HEAD_TYPE
                    260:        ,HEAD_AUTH
                    261:        ,HEAD_CONNECTION
                    262:        ,HEAD_WWWAUTH
                    263:        ,HEAD_STATUS
                    264:        ,HEAD_ALLOW
                    265:        ,HEAD_EXPIRES
                    266:        ,HEAD_LASTMODIFIED
                    267:        ,HEAD_LOCATION
                    268:        ,HEAD_PRAGMA
                    269:        ,HEAD_SERVER
                    270:        ,HEAD_REFERER
                    271:        ,HEAD_AGENT
                    272: };
                    273: 
                    274: static struct {
                    275:        int             id;
                    276:        char*   text;
                    277: } headers[] = {
                    278:        { HEAD_DATE,                    "Date"                                  },
                    279:        { HEAD_HOST,                    "Host"                                  },
                    280:        { HEAD_IFMODIFIED,              "If-Modified-Since"             },
                    281:        { HEAD_LENGTH,                  "Content-Length"                },
                    282:        { HEAD_TYPE,                    "Content-Type"                  },
                    283:        { HEAD_AUTH,                    "Authorization"                 },
                    284:        { HEAD_CONNECTION,              "Connection"                    },
                    285:        { HEAD_WWWAUTH,                 "WWW-Authenticate"              },
                    286:        { HEAD_STATUS,                  "Status"                                },
                    287:        { HEAD_ALLOW,                   "Allow"                                 },
                    288:        { HEAD_EXPIRES,                 "Expires"                               },
                    289:        { HEAD_LASTMODIFIED,    "Last-Modified"                 },
                    290:        { HEAD_LOCATION,                "Location"                              },
                    291:        { HEAD_PRAGMA,                  "Pragma"                                },
                    292:        { HEAD_SERVER,                  "Server"                                },
                    293:        { HEAD_REFERER,                 "Referer"                               },
                    294:        { HEAD_AGENT,                   "User-Agent"                    },
                    295:        { -1,                                   NULL /* terminator */   },
                    296: };
                    297: 
                    298: /* Everything MOVED_TEMP and everything after is a magical internal redirect */
                    299: enum  {
                    300:         NO_LOCATION
                    301:        ,MOVED_PERM
                    302:        ,MOVED_TEMP
                    303:        ,MOVED_STAT
                    304: };
                    305: 
                    306: static char    *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
                    307: static char    *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
                    308: 
                    309: static void respond(http_session_t * session);
                    310: static BOOL js_setup(http_session_t* session);
                    311: static char *find_last_slash(char *str);
                    312: static BOOL check_extra_path(http_session_t * session);
                    313: 
                    314: static time_t
                    315: sub_mkgmt(struct tm *tm)
                    316: {
                    317:         int y, nleapdays;
                    318:         time_t t;
                    319:         /* days before the month */
                    320:         static const unsigned short moff[12] = {
                    321:                 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
                    322:         };
                    323: 
                    324:         /*
                    325:          * XXX: This code assumes the given time to be normalized.
                    326:          * Normalizing here is impossible in case the given time is a leap
                    327:          * second but the local time library is ignorant of leap seconds.
                    328:          */
                    329: 
                    330:         /* minimal sanity checking not to access outside of the array */
                    331:         if ((unsigned) tm->tm_mon >= 12)
                    332:                 return (time_t) -1;
                    333:         if (tm->tm_year < 1970 - 1900)
                    334:                 return (time_t) -1;
                    335: 
                    336:         y = tm->tm_year + 1900 - (tm->tm_mon < 2);
                    337:         nleapdays = y / 4 - y / 100 + y / 400 -
                    338:             ((1970-1) / 4 - (1970-1) / 100 + (1970-1) / 400);
                    339:         t = ((((time_t) (tm->tm_year - (1970 - 1900)) * 365 +
                    340:                         moff[tm->tm_mon] + tm->tm_mday - 1 + nleapdays) * 24 +
                    341:                 tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
                    342: 
                    343:         return (t < 0 ? (time_t) -1 : t);
                    344: }
                    345: 
                    346: time_t
                    347: time_gm(struct tm *tm)
                    348: {
                    349:         time_t t, t2;
                    350:         struct tm *tm2;
                    351:         int sec;
                    352: 
                    353:         /* Do the first guess. */
                    354:         if ((t = sub_mkgmt(tm)) == (time_t) -1)
                    355:                 return (time_t) -1;
                    356: 
                    357:         /* save value in case *tm is overwritten by gmtime() */
                    358:         sec = tm->tm_sec;
                    359: 
                    360:         tm2 = gmtime(&t);
                    361:         if ((t2 = sub_mkgmt(tm2)) == (time_t) -1)
                    362:                 return (time_t) -1;
                    363: 
                    364:         if (t2 < t || tm2->tm_sec != sec) {
                    365:                 /*
                    366:                  * Adjust for leap seconds.
                    367:                  *
                    368:                  *     real time_t time
                    369:                  *           |
                    370:                  *          tm
                    371:                  *         /        ... (a) first sub_mkgmt() conversion
                    372:                  *       t
                    373:                  *       |
                    374:                  *      tm2
                    375:                  *     /        ... (b) second sub_mkgmt() conversion
                    376:                  *   t2
                    377:                  *                        --->time
                    378:                  */
                    379:                 /*
                    380:                  * Do the second guess, assuming (a) and (b) are almost equal.
                    381:                  */
                    382:                 t += t - t2;
                    383:                 tm2 = gmtime(&t);
                    384: 
                    385:                 /*
                    386:                  * Either (a) or (b), may include one or two extra
                    387:                  * leap seconds.  Try t, t + 2, t - 2, t + 1, and t - 1.
                    388:                  */
                    389:                 if (tm2->tm_sec == sec
                    390:                     || (t += 2, tm2 = gmtime(&t), tm2->tm_sec == sec)
                    391:                     || (t -= 4, tm2 = gmtime(&t), tm2->tm_sec == sec)
                    392:                     || (t += 3, tm2 = gmtime(&t), tm2->tm_sec == sec)
                    393:                     || (t -= 2, tm2 = gmtime(&t), tm2->tm_sec == sec))
                    394:                         ;        /* found */
                    395:                 else {
                    396:                         /*
                    397:                          * Not found.
                    398:                          */
                    399:                         if (sec >= 60)
                    400:                                 /*
                    401:                                  * The given time is a leap second
                    402:                                  * (sec 60 or 61), but the time library
                    403:                                  * is ignorant of the leap second.
                    404:                                  */
                    405:                                 ;        /* treat sec 60 as 59,
                    406:                                            sec 61 as 0 of the next minute */
                    407:                         else
                    408:                                 /* The given time may not be normalized. */
                    409:                                 t++;        /* restore t */
                    410:                 }
                    411:         }
                    412: 
                    413:         return (t < 0 ? (time_t) -1 : t);
                    414: }
                    415: 
                    416: static int lprintf(int level, char *fmt, ...)
                    417: {
                    418:        va_list argptr;
                    419:        char sbuf[1024];
                    420: 
                    421:     if(startup==NULL || startup->lputs==NULL)
                    422:         return(0);
                    423: 
                    424:        va_start(argptr,fmt);
                    425:     vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
                    426:        sbuf[sizeof(sbuf)-1]=0;
                    427:     va_end(argptr);
                    428:     return(startup->lputs(startup->cbdata,level,sbuf));
                    429: }
                    430: 
                    431: #ifdef _WINSOCKAPI_
                    432: 
                    433: static WSADATA WSAData;
                    434: #define SOCKLIB_DESC WSAData.szDescription
                    435: static BOOL WSAInitialized=FALSE;
                    436: 
                    437: static BOOL winsock_startup(void)
                    438: {
                    439:        int             status;             /* Status Code */
                    440: 
                    441:     if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
                    442:                lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
                    443:                WSAInitialized=TRUE;
                    444:                return (TRUE);
                    445:        }
                    446: 
                    447:     lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
                    448:        return (FALSE);
                    449: }
                    450: 
                    451: #else /* No WINSOCK */
                    452: 
                    453: #define winsock_startup()      (TRUE)
                    454: #define SOCKLIB_DESC NULL
                    455: 
                    456: #endif
                    457: 
                    458: static void status(char* str)
                    459: {
                    460:        if(startup!=NULL && startup->status!=NULL)
                    461:            startup->status(startup->cbdata,str);
                    462: }
                    463: 
                    464: static void update_clients(void)
                    465: {
                    466:        if(startup!=NULL && startup->clients!=NULL)
                    467:                startup->clients(startup->cbdata,active_clients);
                    468: }
                    469: 
                    470: static void client_on(SOCKET sock, client_t* client, BOOL update)
                    471: {
                    472:        if(startup!=NULL && startup->client_on!=NULL)
                    473:                startup->client_on(startup->cbdata,TRUE,sock,client,update);
                    474: }
                    475: 
                    476: static void client_off(SOCKET sock)
                    477: {
                    478:        if(startup!=NULL && startup->client_on!=NULL)
                    479:                startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
                    480: }
                    481: 
                    482: static void thread_up(BOOL setuid)
                    483: {
                    484:        thread_count++;
                    485:        if(startup!=NULL && startup->thread_up!=NULL)
                    486:                startup->thread_up(startup->cbdata,TRUE, setuid);
                    487: }
                    488: 
                    489: static void thread_down(void)
                    490: {
                    491:        if(thread_count>0)
                    492:                thread_count--;
                    493:        if(startup!=NULL && startup->thread_up!=NULL)
                    494:                startup->thread_up(startup->cbdata,FALSE, FALSE);
                    495: }
                    496: 
                    497: /*********************************************************************/
                    498: /* Adds an environment variable to the sessions  cgi_env linked list */
                    499: /*********************************************************************/
                    500: static void add_env(http_session_t *session, const char *name,const char *value)  {
                    501:        char    newname[129];
                    502:        char    *p;
                    503: 
                    504:        if(name==NULL || value==NULL)  {
                    505:                lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
                    506:                return;
                    507:        }
                    508:        SAFECOPY(newname,name);
                    509: 
                    510:        for(p=newname;*p;p++)  {
                    511:                *p=toupper(*p);
                    512:                if(*p=='-')
                    513:                        *p='_';
                    514:        }
                    515:        p=(char *)malloc(strlen(name)+strlen(value)+2);
                    516:        if(p==NULL) {
                    517:                lprintf(LOG_WARNING,"%04d Cannot allocate memory for string", session->socket);
                    518:                return;
                    519:        }
                    520:        sprintf(p,"%s=%s",newname,value);
                    521:        listPushNodeString(&session->req.cgi_env,p);
                    522:        free(p);
                    523: }
                    524: 
                    525: /***************************************/
                    526: /* Initializes default CGI envirnoment */
                    527: /***************************************/
                    528: static void init_enviro(http_session_t *session)  {
                    529:        char    str[128];
                    530: 
                    531:        add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
                    532:        sprintf(str,"%d",startup->port);
                    533:        add_env(session,"SERVER_PORT",str);
                    534:        add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
                    535:        if(!strcmp(session->host_name,session->host_ip))
                    536:                add_env(session,"REMOTE_HOST",session->host_name);
                    537:        add_env(session,"REMOTE_ADDR",session->host_ip);
                    538: }
                    539: 
                    540: /*
                    541:  * Sends string str to socket sock... returns number of bytes written, or 0 on an error
                    542:  * (Should it be -1 on an error?)
                    543:  * Can not close the socket since it can not set it to INVALID_SOCKET
                    544:  * ToDo - Decide error behaviour, should a SOCKET * be passed around rather than a socket?
                    545:  */
                    546: static int sockprint(SOCKET sock, const char *str)
                    547: {
                    548:        int len;
                    549:        int     result;
                    550:        int written=0;
                    551:        BOOL    wr;
                    552: 
                    553:        if(sock==INVALID_SOCKET)
                    554:                return(0);
                    555:        if(startup->options&WEB_OPT_DEBUG_TX)
                    556:                lprintf(LOG_DEBUG,"%04d TX: %s", sock, str);
                    557:        len=strlen(str);
                    558: 
                    559:        while(socket_check(sock,NULL,&wr,startup->max_inactivity*1000) && wr && written<len)  {
                    560:                result=sendsocket(sock,str+written,len-written);
                    561:                if(result==SOCKET_ERROR) {
                    562:                        if(ERROR_VALUE==ECONNRESET) 
                    563:                                lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",sock);
                    564:                        else if(ERROR_VALUE==ECONNABORTED) 
                    565:                                lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",sock);
                    566:                        else
                    567:                                lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
                    568:                        return(0);
                    569:                }
                    570:                written+=result;
                    571:        }
                    572:        if(written != len) {
                    573:                lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
                    574:                return(0);
                    575:        }
                    576:        return(len);
                    577: }
                    578: 
                    579: /**********************************************************/
                    580: /* Converts a month name/abbr to the 0-based month number */
                    581: /* ToDo: This probobly exists somewhere else already     */
                    582: /**********************************************************/
                    583: static int getmonth(char *mon)
                    584: {
                    585:        int     i;
                    586:        for(i=0;i<12;i++)
                    587:                if(!stricmp(mon,months[i]))
                    588:                        return(i);
                    589: 
                    590:        return 0;
                    591: }
                    592: 
                    593: /*******************************************************************/
                    594: /* Converts a date string in any of the common formats to a time_t */
                    595: /*******************************************************************/
                    596: static time_t decode_date(char *date)
                    597: {
                    598:        struct  tm      ti;
                    599:        char    *token;
                    600:        time_t  t;
                    601: 
                    602:        ti.tm_sec=0;            /* seconds (0 - 60) */
                    603:        ti.tm_min=0;            /* minutes (0 - 59) */
                    604:        ti.tm_hour=0;           /* hours (0 - 23) */
                    605:        ti.tm_mday=1;           /* day of month (1 - 31) */
                    606:        ti.tm_mon=0;            /* month of year (0 - 11) */
                    607:        ti.tm_year=0;           /* year - 1900 */
                    608:        ti.tm_isdst=0;          /* is summer time in effect? */
                    609: 
                    610:        token=strtok(date,",");
                    611:        if(token==NULL)
                    612:                return(0);
                    613:        /* This probobly only needs to be 9, but the extra one is for luck. */
                    614:        if(strlen(date)>15) {
                    615:                /* asctime() */
                    616:                /* Toss away week day */
                    617:                token=strtok(date," ");
                    618:                if(token==NULL)
                    619:                        return(0);
                    620:                token=strtok(NULL," ");
                    621:                if(token==NULL)
                    622:                        return(0);
                    623:                ti.tm_mon=getmonth(token);
                    624:                token=strtok(NULL," ");
                    625:                if(token==NULL)
                    626:                        return(0);
                    627:                ti.tm_mday=atoi(token);
                    628:                token=strtok(NULL,":");
                    629:                if(token==NULL)
                    630:                        return(0);
                    631:                ti.tm_hour=atoi(token);
                    632:                token=strtok(NULL,":");
                    633:                if(token==NULL)
                    634:                        return(0);
                    635:                ti.tm_min=atoi(token);
                    636:                token=strtok(NULL," ");
                    637:                if(token==NULL)
                    638:                        return(0);
                    639:                ti.tm_sec=atoi(token);
                    640:                token=strtok(NULL,"");
                    641:                if(token==NULL)
                    642:                        return(0);
                    643:                ti.tm_year=atoi(token)-1900;
                    644:        }
                    645:        else  {
                    646:                /* RFC 1123 or RFC 850 */
                    647:                token=strtok(NULL," -");
                    648:                if(token==NULL)
                    649:                        return(0);
                    650:                ti.tm_mday=atoi(token);
                    651:                token=strtok(NULL," -");
                    652:                if(token==NULL)
                    653:                        return(0);
                    654:                ti.tm_mon=getmonth(token);
                    655:                token=strtok(NULL," ");
                    656:                if(token==NULL)
                    657:                        return(0);
                    658:                ti.tm_year=atoi(token);
                    659:                token=strtok(NULL,":");
                    660:                if(token==NULL)
                    661:                        return(0);
                    662:                ti.tm_hour=atoi(token);
                    663:                token=strtok(NULL,":");
                    664:                if(token==NULL)
                    665:                        return(0);
                    666:                ti.tm_min=atoi(token);
                    667:                token=strtok(NULL," ");
                    668:                if(token==NULL)
                    669:                        return(0);
                    670:                ti.tm_sec=atoi(token);
                    671:                if(ti.tm_year>1900)
                    672:                        ti.tm_year -= 1900;
                    673:        }
                    674: 
                    675:        t=time_gm(&ti);
                    676:        return(t);
                    677: }
                    678: 
                    679: static SOCKET open_socket(int type)
                    680: {
                    681:        char    error[256];
                    682:        SOCKET  sock;
                    683: 
                    684:        sock=socket(AF_INET, type, IPPROTO_IP);
                    685:        if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
                    686:                startup->socket_open(startup->cbdata,TRUE);
                    687:        if(sock!=INVALID_SOCKET) {
                    688:                if(set_socket_options(&scfg, sock,error))
                    689:                        lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
                    690: 
                    691:                sockets++;
                    692:        }
                    693:        return(sock);
                    694: }
                    695: 
                    696: static int close_socket(SOCKET sock)
                    697: {
                    698:        int             result;
                    699: 
                    700:        if(sock==INVALID_SOCKET)
                    701:                return(-1);
                    702: 
                    703:        shutdown(sock,SHUT_RDWR);       /* required on Unix */
                    704:        result=closesocket(sock);
                    705:        if(startup!=NULL && startup->socket_open!=NULL) {
                    706:                startup->socket_open(startup->cbdata,FALSE);
                    707:        }
                    708:        sockets--;
                    709:        if(result!=0) {
                    710:                if(ERROR_VALUE!=ENOTSOCK)
                    711:                        lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",sock, ERROR_VALUE);
                    712:        }
                    713: 
                    714:        return(result);
                    715: }
                    716: 
                    717: /**************************************************/
                    718: /* End of a single request...                                    */
                    719: /* This is called at the end of EVERY request    */
                    720: /*  Log the request                                                      */
                    721: /*  Free request-specific data ie: dynamic stuff  */
                    722: /*  Close socket unless it's being kept alive     */
                    723: /*   If the socket is closed, the session is done */
                    724: /**************************************************/
                    725: static void close_request(http_session_t * session)
                    726: {
                    727:        time_t          now;
                    728: 
                    729:        if(session->req.ld!=NULL) {
                    730:                now=time(NULL);
                    731:                localtime_r(&now,&session->req.ld->completed);
                    732:                pthread_mutex_lock(&log_mutex);
                    733:                listPushNode(&log_list,session->req.ld);
                    734:                pthread_mutex_unlock(&log_mutex);
                    735:                sem_post(&log_sem);
                    736:                session->req.ld=NULL;
                    737:        }
                    738: 
                    739:        listFree(&session->req.headers);
                    740:        listFree(&session->req.dynamic_heads);
                    741:        listFree(&session->req.cgi_env);
                    742:        FREE_AND_NULL(session->req.post_data);
                    743:        if(!session->req.keep_alive) {
                    744:                close_socket(session->socket);
                    745:                session->socket=INVALID_SOCKET;
                    746:        }
                    747:        if(session->socket==INVALID_SOCKET)
                    748:                session->finished=TRUE;
                    749: 
                    750:        if(session->js_cx!=NULL && (session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)) {
                    751:                JS_GC(session->js_cx);
                    752:        }
                    753:        if(session->subscan!=NULL)
                    754:                putmsgptrs(&scfg, session->user.number, session->subscan);
                    755: 
                    756:        memset(&session->req,0,sizeof(session->req));
                    757: }
                    758: 
                    759: static int get_header_type(char *header)
                    760: {
                    761:        int i;
                    762:        for(i=0; headers[i].text!=NULL; i++) {
                    763:                if(!stricmp(header,headers[i].text)) {
                    764:                        return(headers[i].id);
                    765:                }
                    766:        }
                    767:        return(-1);
                    768: }
                    769: 
                    770: /* Opposite of get_header_type() */
                    771: static char *get_header(int id) 
                    772: {
                    773:        int i;
                    774:        if(headers[id].id==id)
                    775:                return(headers[id].text);
                    776: 
                    777:        for(i=0;headers[i].text!=NULL;i++) {
                    778:                if(headers[i].id==id) {
                    779:                        return(headers[i].text);
                    780:                }
                    781:        }
                    782:        return(NULL);
                    783: }
                    784: 
                    785: static const char* unknown_mime_type="application/octet-stream";
                    786: 
                    787: static const char* get_mime_type(char *ext)
                    788: {
                    789:        uint i;
                    790: 
                    791:        if(ext==NULL)
                    792:                return(unknown_mime_type);
                    793: 
                    794:        for(i=0;mime_types[i]!=NULL;i++)
                    795:                if(!stricmp(ext+1,mime_types[i]->name))
                    796:                        return(mime_types[i]->value);
                    797: 
                    798:        return(unknown_mime_type);
                    799: }
                    800: 
                    801: /* This function appends append plus a newline IF the final dst string would have a length less than maxlen */
                    802: static void safecat(char *dst, const char *append, size_t maxlen) {
                    803:        size_t dstlen,appendlen;
                    804:        dstlen=strlen(dst);
                    805:        appendlen=strlen(append);
                    806:        if(dstlen+appendlen+2 < maxlen) {
                    807:                strcat(dst,append);
                    808:                strcat(dst,newline);
                    809:        }
                    810: }
                    811: 
                    812: /*************************************************/
                    813: /* Sends headers for the reply.                                         */
                    814: /* HTTP/0.9 doesn't use headers, so just returns */
                    815: /*************************************************/
                    816: static BOOL send_headers(http_session_t *session, const char *status)
                    817: {
                    818:        int             ret;
                    819:        BOOL    send_file=TRUE;
                    820:        time_t  ti;
                    821:        const char      *status_line;
                    822:        struct stat     stats;
                    823:        struct tm       tm;
                    824:        char    *headers;
                    825:        char    header[MAX_REQUEST_LINE+1];
                    826:        list_node_t     *node;
                    827: 
                    828:        lprintf(LOG_DEBUG,"%04d Request resolved to: %s"
                    829:                ,session->socket,session->req.physical_path);
                    830:        if(session->http_ver <= HTTP_0_9) {
                    831:                if(session->req.ld != NULL)
                    832:                        session->req.ld->status=atoi(status);
                    833:                return(TRUE);
                    834:        }
                    835: 
                    836:        status_line=status;
                    837:        ret=stat(session->req.physical_path,&stats);
                    838:        if(!ret && session->req.if_modified_since && (stats.st_mtime <= session->req.if_modified_since) && !session->req.dynamic) {
                    839:                status_line="304 Not Modified";
                    840:                ret=-1;
                    841:                send_file=FALSE;
                    842:        }
                    843:        if(session->req.send_location==MOVED_PERM)  {
                    844:                status_line="301 Moved Permanently";
                    845:                ret=-1;
                    846:                send_file=FALSE;
                    847:        }
                    848:        if(session->req.send_location==MOVED_TEMP)  {
                    849:                status_line="302 Moved Temporarily";
                    850:                ret=-1;
                    851:                send_file=FALSE;
                    852:        }
                    853: 
                    854:        if(session->req.ld!=NULL)
                    855:                session->req.ld->status=atoi(status_line);
                    856: 
                    857:        headers=malloc(MAX_HEADERS_SIZE);
                    858:        if(headers==NULL)  {
                    859:                lprintf(LOG_CRIT,"Could not allocate memory for response headers.");
                    860:                return(FALSE);
                    861:        }
                    862:        *headers=0;
                    863:        /* Status-Line */
                    864:        safe_snprintf(header,sizeof(header),"%s %s",http_vers[session->http_ver],status_line);
                    865: 
                    866:        lprintf(LOG_DEBUG,"%04d Result: %s",session->socket,header);
                    867: 
                    868:        safecat(headers,header,MAX_HEADERS_SIZE);
                    869: 
                    870:        /* General Headers */
                    871:        ti=time(NULL);
                    872:        if(gmtime_r(&ti,&tm)==NULL)
                    873:                memset(&tm,0,sizeof(tm));
                    874:        safe_snprintf(header,sizeof(header),"%s: %s, %02d %s %04d %02d:%02d:%02d GMT"
                    875:                ,get_header(HEAD_DATE)
                    876:                ,days[tm.tm_wday],tm.tm_mday,months[tm.tm_mon]
                    877:                ,tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
                    878:        safecat(headers,header,MAX_HEADERS_SIZE);
                    879:        if(session->req.keep_alive) {
                    880:                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_CONNECTION),"Keep-Alive");
                    881:                safecat(headers,header,MAX_HEADERS_SIZE);
                    882:        }
                    883:        else {
                    884:                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_CONNECTION),"Close");
                    885:                safecat(headers,header,MAX_HEADERS_SIZE);
                    886:        }
                    887: 
                    888:        /* Response Headers */
                    889:        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_SERVER),VERSION_NOTICE);
                    890:        safecat(headers,header,MAX_HEADERS_SIZE);
                    891:        
                    892:        /* Entity Headers */
                    893:        if(session->req.dynamic) {
                    894:                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD, POST");
                    895:                safecat(headers,header,MAX_HEADERS_SIZE);
                    896:        }
                    897:        else {
                    898:                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD");
                    899:                safecat(headers,header,MAX_HEADERS_SIZE);
                    900:        }
                    901: 
                    902:        if(session->req.send_location) {
                    903:                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_LOCATION),(session->req.virtual_path));
                    904:                safecat(headers,header,MAX_HEADERS_SIZE);
                    905:        }
                    906:        if(session->req.keep_alive) {
                    907:                if(ret)  {
                    908:                        safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_LENGTH),"0");
                    909:                        safecat(headers,header,MAX_HEADERS_SIZE);
                    910:                }
                    911:                else  {
                    912:                        safe_snprintf(header,sizeof(header),"%s: %d",get_header(HEAD_LENGTH),(int)stats.st_size);
                    913:                        safecat(headers,header,MAX_HEADERS_SIZE);
                    914:                }
                    915:        }
                    916: 
                    917:        if(!ret && !session->req.dynamic)  {
                    918:                safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_TYPE),session->req.mime_type);
                    919:                safecat(headers,header,MAX_HEADERS_SIZE);
                    920:                gmtime_r(&stats.st_mtime,&tm);
                    921:                safe_snprintf(header,sizeof(header),"%s: %s, %02d %s %04d %02d:%02d:%02d GMT"
                    922:                        ,get_header(HEAD_LASTMODIFIED)
                    923:                        ,days[tm.tm_wday],tm.tm_mday,months[tm.tm_mon]
                    924:                        ,tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
                    925:                safecat(headers,header,MAX_HEADERS_SIZE);
                    926:        } 
                    927: 
                    928:        if(session->req.dynamic)  {
                    929:                /* Dynamic headers */
                    930:                /* Set up environment */
                    931:                for(node=listFirstNode(&session->req.dynamic_heads);node!=NULL;node=listNextNode(node))
                    932:                        safecat(headers,listNodeData(node),MAX_HEADERS_SIZE);
                    933:        }
                    934: 
                    935:        safecat(headers,"",MAX_HEADERS_SIZE);
                    936:        send_file = (sockprint(session->socket,headers) && send_file);
                    937:        FREE_AND_NULL(headers);
                    938:        return(send_file);
                    939: }
                    940: 
                    941: static int sock_sendfile(SOCKET socket,char *path)
                    942: {
                    943:        int             file;
                    944:        long    offset=0;
                    945:        int             ret=0;
                    946: 
                    947:        if(startup->options&WEB_OPT_DEBUG_TX)
                    948:                lprintf(LOG_DEBUG,"%04d Sending %s",socket,path);
                    949:        if((file=open(path,O_RDONLY|O_BINARY))==-1)
                    950:                lprintf(LOG_WARNING,"%04d !ERROR %d opening %s",socket,errno,path);
                    951:        else {
                    952:                if((ret=sendfilesocket(socket, file, &offset, 0)) < 1) {
                    953:                        lprintf(LOG_DEBUG,"%04d !ERROR %d sending %s"
                    954:                                , socket, errno, path);
                    955:                        ret=0;
                    956:                }
                    957:                close(file);
                    958:        }
                    959:        return(ret);
                    960: }
                    961: 
                    962: /********************************************************/
                    963: /* Sends a specified error message, closes the request, */
                    964: /* and marks the session to be closed                                  */
                    965: /********************************************************/
                    966: static void send_error(http_session_t * session, const char* message)
                    967: {
                    968:        char    error_code[4];
                    969:        struct stat     sb;
                    970:        char    sbuf[1024];
                    971: 
                    972:        session->req.if_modified_since=0;
                    973:        lprintf(LOG_INFO,"%04d !ERROR: %s",session->socket,message);
                    974:        session->req.keep_alive=FALSE;
                    975:        session->req.send_location=NO_LOCATION;
                    976:        SAFECOPY(error_code,message);
                    977:        sprintf(session->req.physical_path,"%s%s.html",error_dir,error_code);
                    978:        session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
                    979:        send_headers(session,message);
                    980:        if(!stat(session->req.physical_path,&sb)) {
                    981:                int     snt=0;
                    982:                snt=sock_sendfile(session->socket,session->req.physical_path);
                    983:                if(snt<0)
                    984:                        snt=0;
                    985:                if(session->req.ld!=NULL)
                    986:                        session->req.ld->size=snt;
                    987:        }
                    988:        else {
                    989:                lprintf(LOG_NOTICE,"%04d Error message file %s doesn't exist"
                    990:                        ,session->socket,session->req.physical_path);
                    991:                safe_snprintf(sbuf,sizeof(sbuf)
                    992:                        ,"<HTML><HEAD><TITLE>%s Error</TITLE></HEAD>"
                    993:                        "<BODY><H1>%s Error</H1><BR><H3>In addition, "
                    994:                        "I can't seem to find the %s error file</H3><br>"
                    995:                        "please notify <a href=\"mailto:sysop@%s\">"
                    996:                        "%s</a></BODY></HTML>"
                    997:                        ,error_code,error_code,error_code,scfg.sys_inetaddr,scfg.sys_op);
                    998:                sockprint(session->socket,sbuf);
                    999:                if(session->req.ld!=NULL)
                   1000:                        session->req.ld->size=strlen(sbuf);
                   1001:        }
                   1002:        close_request(session);
                   1003: }
                   1004: 
                   1005: void http_logon(http_session_t * session, user_t *usr)
                   1006: {
                   1007:        if(usr==NULL)
                   1008:                getuserdat(&scfg, &session->user);
                   1009:        else
                   1010:                session->user=*usr;
                   1011: 
                   1012:        if(session->user.number==session->last_user_num)
                   1013:                return;
                   1014: 
                   1015:        lprintf(LOG_DEBUG,"%04d HTTP Logon (%d)",session->socket,session->user.number);
                   1016: 
                   1017:        if(session->subscan!=NULL)
                   1018:                getmsgptrs(&scfg,session->user.number,session->subscan);
                   1019: 
                   1020:        if(session->user.number==0)
                   1021:                SAFECOPY(session->username,unknown);
                   1022:        else {
                   1023:                SAFECOPY(session->username,session->user.alias);
                   1024:                /* Adjust Connect and host */
                   1025:                putuserrec(&scfg,session->user.number,U_MODEM,LEN_MODEM,"HTTP");
                   1026:                putuserrec(&scfg,session->user.number,U_COMP,LEN_COMP,session->host_name);
                   1027:                putuserrec(&scfg,session->user.number,U_NOTE,LEN_NOTE,session->host_ip);
                   1028:        }
                   1029:        session->client.user=session->username;
                   1030:        client_on(session->socket, &session->client, /* update existing client record? */TRUE);
                   1031: 
                   1032:        session->last_user_num=session->user.number;
                   1033:        session->logon_time=time(NULL);
                   1034: }
                   1035: 
                   1036: void http_logoff(http_session_t * session)
                   1037: {
                   1038:        if(session->last_user_num<=0)
                   1039:                return;
                   1040: 
                   1041:        lprintf(LOG_DEBUG,"%04d HTTP Logoff (%d)",session->socket,session->user.number);
                   1042: 
                   1043:        SAFECOPY(session->username,unknown);
                   1044:        logoutuserdat(&scfg, &session->user, time(NULL), session->logon_time);
                   1045:        memset(&session->user,0,sizeof(session->user));
                   1046:        session->last_user_num=session->user.number;
                   1047: }
                   1048: 
                   1049: BOOL http_checkuser(http_session_t * session)
                   1050: {
                   1051:        if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
                   1052:                if(session->last_js_user_num==session->user.number)
                   1053:                        return(TRUE);
                   1054:                lprintf(LOG_INFO,"%04d JavaScript: Initializing User Objects",session->socket);
                   1055:                if(session->user.number>0) {
                   1056:                        if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user
                   1057:                                ,NULL /* ftp index file */, session->subscan /* subscan */)) {
                   1058:                                lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user objects",session->socket);
                   1059:                                send_error(session,"500 Error initializing JavaScript User Objects");
                   1060:                                return(FALSE);
                   1061:                        }
                   1062:                }
                   1063:                else {
                   1064:                        if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, NULL
                   1065:                                ,NULL /* ftp index file */, session->subscan /* subscan */)) {
                   1066:                                lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript User Objects",session->socket);
                   1067:                                send_error(session,"500 Error initializing JavaScript User Objects");
                   1068:                                return(FALSE);
                   1069:                        }
                   1070:                }
                   1071:                session->last_js_user_num=session->user.number;
                   1072:        }
                   1073:        return(TRUE);
                   1074: }
                   1075: 
                   1076: static BOOL check_ars(http_session_t * session)
                   1077: {
                   1078:        char    *username;
                   1079:        char    *password;
                   1080:        uchar   *ar;
                   1081:        BOOL    authorized;
                   1082:        char    auth_req[MAX_REQUEST_LINE+1];
                   1083:        int             i;
                   1084:        user_t  thisuser;
                   1085: 
                   1086:        if(session->req.auth[0]==0) {
                   1087:                /* No authentication information... */
                   1088:                if(session->last_user_num!=0) {
                   1089:                        if(session->last_user_num>0)
                   1090:                                http_logoff(session);
                   1091:                        session->user.number=0;
                   1092:                        http_logon(session,NULL);
                   1093:                }
                   1094:                if(!http_checkuser(session))
                   1095:                        return(FALSE);
                   1096:                if(session->req.ars[0]) {
                   1097:                        /* There *IS* an ARS string  ie: Auth is required */
                   1098:                        if(startup->options&WEB_OPT_DEBUG_RX)
                   1099:                                lprintf(LOG_NOTICE,"%04d !No authentication information",session->socket);
                   1100:                        return(FALSE);
                   1101:                }
                   1102:                /* No auth required, allow */
                   1103:                return(TRUE);
                   1104:        }
                   1105:        SAFECOPY(auth_req,session->req.auth);
                   1106: 
                   1107:        username=strtok(auth_req,":");
                   1108:        if(username==NULL)
                   1109:                username="";
                   1110:        password=strtok(NULL,":");
                   1111:        /* Require a password */
                   1112:        if(password==NULL)
                   1113:                password="";
                   1114:        i=matchuser(&scfg, username, FALSE);
                   1115:        if(i==0) {
                   1116:                if(session->last_user_num!=0) {
                   1117:                        if(session->last_user_num>0)
                   1118:                                http_logoff(session);
                   1119:                        session->user.number=0;
                   1120:                        http_logon(session,NULL);
                   1121:                }
                   1122:                if(!http_checkuser(session))
                   1123:                        return(FALSE);
                   1124:                if(scfg.sys_misc&SM_ECHO_PW)
                   1125:                        lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s, Password: %s"
                   1126:                                ,session->socket,username,password);
                   1127:                else
                   1128:                        lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s"
                   1129:                                ,session->socket,username);
                   1130:                return(FALSE);
                   1131:        }
                   1132:        thisuser.number=i;
                   1133:        getuserdat(&scfg, &thisuser);
                   1134:        if(thisuser.pass[0] && stricmp(thisuser.pass,password)) {
                   1135:                if(session->last_user_num!=0) {
                   1136:                        if(session->last_user_num>0)
                   1137:                                http_logoff(session);
                   1138:                        session->user.number=0;
                   1139:                        http_logon(session,NULL);
                   1140:                }
                   1141:                if(!http_checkuser(session))
                   1142:                        return(FALSE);
                   1143:                /* Should go to the hack log? */
                   1144:                if(scfg.sys_misc&SM_ECHO_PW)
                   1145:                        lprintf(LOG_WARNING,"%04d !PASSWORD FAILURE for user %s: '%s' expected '%s'"
                   1146:                                ,session->socket,username,password,session->user.pass);
                   1147:                else
                   1148:                        lprintf(LOG_WARNING,"%04d !PASSWORD FAILURE for user %s"
                   1149:                                ,session->socket,username);
                   1150: #ifdef _WIN32
                   1151:                if(startup->hack_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
                   1152:                        PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
                   1153: #endif
                   1154:                return(FALSE);
                   1155:        }
                   1156: 
                   1157:        if(i != session->last_user_num) {
                   1158:                http_logoff(session);
                   1159:                session->user.number=i;
                   1160:                http_logon(session,&thisuser);
                   1161:        }
                   1162:        if(!http_checkuser(session))
                   1163:                return(FALSE);
                   1164: 
                   1165:        if(session->req.ld!=NULL)
                   1166:                session->req.ld->user=strdup(username);
                   1167: 
                   1168:        ar = arstr(NULL,session->req.ars,&scfg);
                   1169:        authorized=chk_ar(&scfg,ar,&session->user);
                   1170:        if(ar!=NULL && ar!=nular)
                   1171:                FREE_AND_NULL(ar);
                   1172: 
                   1173:        if(authorized)  {
                   1174:                if(session->req.dynamic==IS_CGI)  {
                   1175:                        add_env(session,"AUTH_TYPE","Basic");
                   1176:                        /* Should use real name if set to do so somewhere ToDo */
                   1177:                        add_env(session,"REMOTE_USER",session->user.alias);
                   1178:                }
                   1179: 
                   1180:                return(TRUE);
                   1181:        }
                   1182: 
                   1183:        /* Should go to the hack log? */
                   1184:        lprintf(LOG_WARNING,"%04d !AUTHORIZATION FAILURE for user %s, ARS: %s"
                   1185:                ,session->socket,username,session->req.ars);
                   1186: 
                   1187: #ifdef _WIN32
                   1188:        if(startup->hack_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
                   1189:                PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
                   1190: #endif
                   1191: 
                   1192:        return(FALSE);
                   1193: }
                   1194: 
                   1195: static BOOL read_mime_types(char* fname)
                   1196: {
                   1197:        int             mime_count;
                   1198:        FILE*   fp;
                   1199: 
                   1200:        mime_types=iniFreeNamedStringList(mime_types);
                   1201: 
                   1202:        lprintf(LOG_DEBUG,"Reading %s",fname);
                   1203:        if((fp=iniOpenFile(fname))==NULL) {
                   1204:                lprintf(LOG_WARNING,"Error %d opening %s",errno,fname);
                   1205:                return(FALSE);
                   1206:        }
                   1207:        mime_types=iniReadNamedStringList(fp,NULL /* root section */);
                   1208:        iniCloseFile(fp);
                   1209: 
                   1210:        COUNT_LIST_ITEMS(mime_types,mime_count);
                   1211:        lprintf(LOG_DEBUG,"Loaded %d mime types", mime_count);
                   1212:        return(mime_count>0);
                   1213: }
                   1214: 
                   1215: static int sockreadline(http_session_t * session, char *buf, size_t length)
                   1216: {
                   1217:        char    ch;
                   1218:        DWORD   i;
                   1219:        BOOL    rd;
                   1220:        DWORD   chucked=0;
                   1221: 
                   1222:        for(i=0;TRUE;) {
                   1223:                if(!socket_check(session->socket,&rd,NULL,startup->max_inactivity*1000) 
                   1224:                        || !rd || recv(session->socket, &ch, 1, 0)!=1)  {
                   1225:                        session->req.keep_alive=FALSE;
                   1226:                        close_request(session);
                   1227:                        return(-1);        /* time-out */
                   1228:                }
                   1229: 
                   1230:                if(ch=='\n')
                   1231:                        break;
                   1232: 
                   1233:                if(i<length)
                   1234:                        buf[i++]=ch;
                   1235:                else
                   1236:                        chucked++;
                   1237:        }
                   1238: 
                   1239:        /* Terminate at length if longer */
                   1240:        if(i>length)
                   1241:                i=length;
                   1242:                
                   1243:        if(i>0 && buf[i-1]=='\r')
                   1244:                buf[--i]=0;
                   1245:        else
                   1246:                buf[i]=0;
                   1247: 
                   1248:        if(startup->options&WEB_OPT_DEBUG_RX) {
                   1249:                lprintf(LOG_DEBUG,"%04d RX: %s",session->socket,buf);
                   1250:                if(chucked)
                   1251:                        lprintf(LOG_DEBUG,"%04d Long header, chucked %d bytes",session->socket,chucked);
                   1252:        }
                   1253:        return(i);
                   1254: }
                   1255: 
                   1256: static int pipereadline(int pipe, char *buf, size_t length)
                   1257: {
                   1258:        char    ch;
                   1259:        DWORD   i;
                   1260:        time_t  start;
                   1261:        int             ret=0;
                   1262: 
                   1263:        start=time(NULL);
                   1264:        for(i=0;TRUE;) {
                   1265:                if(time(NULL)-start>startup->max_cgi_inactivity)
                   1266:                        return(-1);
                   1267: 
                   1268:                ret=read(pipe, &ch, 1);
                   1269:                if(ret==1)  {
                   1270:                        start=time(NULL);
                   1271: 
                   1272:                        if(ch=='\n')
                   1273:                                break;
                   1274: 
                   1275:                        if(i<length)
                   1276:                                buf[i++]=ch;
                   1277:                }
                   1278:                else
                   1279:                        return(-1);
                   1280:        }
                   1281: 
                   1282:        /* Terminate at length if longer */
                   1283:        if(i>length)
                   1284:                i=length;
                   1285: 
                   1286:        if(i>0 && buf[i-1]=='\r')
                   1287:                buf[--i]=0;
                   1288:        else
                   1289:                buf[i]=0;
                   1290: 
                   1291:        return(i);
                   1292: }
                   1293: 
                   1294: int recvbufsocket(int sock, char *buf, long count)
                   1295: {
                   1296:        int             rd=0;
                   1297:        int             i;
                   1298:        time_t  start;
                   1299: 
                   1300:        if(count<1) {
                   1301:                errno=ERANGE;
                   1302:                return(0);
                   1303:        }
                   1304: 
                   1305:        while(rd<count && socket_check(sock,NULL,NULL,startup->max_inactivity*1000))  {
                   1306:                i=recv(sock,buf,count-rd,0);
                   1307:                if(i<=0)  {
                   1308:                        *buf=0;
                   1309:                        return(0);
                   1310:                }
                   1311: 
                   1312:                rd+=i;
                   1313:                start=time(NULL);
                   1314:        }
                   1315: 
                   1316:        if(rd==count)  {
                   1317:                return(rd);
                   1318:        }
                   1319: 
                   1320:        *buf=0;
                   1321:        return(0);
                   1322: }
                   1323: 
                   1324: /* Wasn't this done up as a JS thing too?  ToDo */
                   1325: static void unescape(char *p)
                   1326: {
                   1327:        char *  dst;
                   1328:        char    code[3];
                   1329:        
                   1330:        dst=p;
                   1331:        for(;*p;p++) {
                   1332:                if(*p=='%' && isxdigit(*(p+1)) && isxdigit(*(p+2))) {
                   1333:                        sprintf(code,"%.2s",p+1);
                   1334:                        *(dst++)=(char)strtol(code,NULL,16);
                   1335:                        p+=2;
                   1336:                }
                   1337:                else  {
                   1338:                        if(*p=='+')  {
                   1339:                                *(dst++)=' ';
                   1340:                        }
                   1341:                        else  {
                   1342:                                *(dst++)=*p;
                   1343:                        }
                   1344:                }
                   1345:        }
                   1346:        *(dst)=0;
                   1347: }
                   1348: 
                   1349: static void js_add_queryval(http_session_t * session, char *key, char *value)
                   1350: {
                   1351:        JSObject*       keyarray;
                   1352:        jsval           val;
                   1353:        jsint           len;
                   1354:        int                     alen;
                   1355: 
                   1356:        /* Return existing object if it's already been created */
                   1357:        if(JS_GetProperty(session->js_cx,session->js_query,key,&val) && val!=JSVAL_VOID)  {
                   1358:                keyarray = JSVAL_TO_OBJECT(val);
                   1359:                alen=-1;
                   1360:        }
                   1361:        else {
                   1362:                keyarray = JS_NewArrayObject(session->js_cx, 0, NULL);
                   1363:                if(!JS_DefineProperty(session->js_cx, session->js_query, key, OBJECT_TO_JSVAL(keyarray)
                   1364:                        , NULL, NULL, JSPROP_ENUMERATE))
                   1365:                        return;
                   1366:                alen=0;
                   1367:        }
                   1368: 
                   1369:        if(alen==-1) {
                   1370:                if(JS_GetArrayLength(session->js_cx, keyarray, &len)==JS_FALSE)
                   1371:                        return;
                   1372:                alen=len;
                   1373:        }
                   1374: 
                   1375:        lprintf(LOG_DEBUG,"%04d Adding query value %s=%s at pos %d",session->socket,key,value,alen);
                   1376:        val=STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx,value));
                   1377:        JS_SetElement(session->js_cx, keyarray, alen, &val);
                   1378: }
                   1379: 
                   1380: static void js_add_request_prop(http_session_t * session, char *key, char *value)  
                   1381: {
                   1382:        JSString*       js_str;
                   1383: 
                   1384:        if(session->js_cx==NULL || session->js_request==NULL)
                   1385:                return;
                   1386:        if(key==NULL || value==NULL)
                   1387:                return;
                   1388:        if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL)
                   1389:                return;
                   1390:        JS_DefineProperty(session->js_cx, session->js_request, key, STRING_TO_JSVAL(js_str)
                   1391:                ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
                   1392: }
                   1393: 
                   1394: static void js_add_header(http_session_t * session, char *key, char *value)  
                   1395: {
                   1396:        JSString*       js_str;
                   1397:        char            *lckey;
                   1398: 
                   1399:        if((lckey=(char *)malloc(strlen(key)+1))==NULL)
                   1400:                return;
                   1401:        strcpy(lckey,key);
                   1402:        strlwr(lckey);
                   1403:        if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL) {
                   1404:                free(lckey);
                   1405:                return;
                   1406:        }
                   1407:        JS_DefineProperty(session->js_cx, session->js_header, lckey, STRING_TO_JSVAL(js_str)
                   1408:                ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
                   1409:        free(lckey);
                   1410: }
                   1411: 
                   1412: static void js_parse_query(http_session_t * session, char *p)  {
                   1413:        size_t          key_len;
                   1414:        size_t          value_len;
                   1415:        char            *lp;
                   1416:        char            *key;
                   1417:        char            *value;
                   1418: 
                   1419:        if(p == NULL)
                   1420:                return;
                   1421: 
                   1422:        lp=p;
                   1423: 
                   1424:        while(key_len=strcspn(lp,"="))  {
                   1425:                key=lp;
                   1426:                lp+=key_len;
                   1427:                if(*lp) {
                   1428:                        *lp=0;
                   1429:                        lp++;
                   1430:                }
                   1431:                value_len=strcspn(lp,"&");
                   1432:                value=lp;
                   1433:                lp+=value_len;
                   1434:                if(*lp) {
                   1435:                        *lp=0;
                   1436:                        lp++;
                   1437:                }
                   1438:                unescape(value);
                   1439:                unescape(key);
                   1440:                js_add_queryval(session, key, value);
                   1441:        }
                   1442: }
                   1443: 
                   1444: static BOOL parse_headers(http_session_t * session)
                   1445: {
                   1446:        char    *head_line;
                   1447:        char    *value;
                   1448:        char    *p;
                   1449:        int             i;
                   1450:        size_t  content_len=0;
                   1451:        char    env_name[128];
                   1452:        list_node_t     *node;
                   1453: 
                   1454:        for(node=listFirstNode(&session->req.headers);node!=NULL;node=listNextNode(node)) {
                   1455:                head_line=listNodeData(node);
                   1456:                if((strtok(head_line,":"))!=NULL && (value=strtok(NULL,""))!=NULL) {
                   1457:                        i=get_header_type(head_line);
                   1458:                        while(*value && *value<=' ') value++;
                   1459:                        if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)
                   1460:                                js_add_header(session,head_line,value);
                   1461:                        switch(i) {
                   1462:                                case HEAD_AUTH:
                   1463:                                        strtok(value," ");
                   1464:                                        p=strtok(NULL," ");
                   1465:                                        if(p==NULL)
                   1466:                                                break;
                   1467:                                        while(*p && *p<' ') p++;
                   1468:                                        b64_decode(session->req.auth,sizeof(session->req.auth),p,strlen(p));
                   1469:                                        break;
                   1470:                                case HEAD_LENGTH:
                   1471:                                        if(session->req.dynamic==IS_CGI)
                   1472:                                                add_env(session,"CONTENT_LENGTH",value);
                   1473:                                        content_len=atoi(value);
                   1474:                                        break;
                   1475:                                case HEAD_TYPE:
                   1476:                                        if(session->req.dynamic==IS_CGI)
                   1477:                                                add_env(session,"CONTENT_TYPE",value);
                   1478:                                        break;
                   1479:                                case HEAD_IFMODIFIED:
                   1480:                                        session->req.if_modified_since=decode_date(value);
                   1481:                                        break;
                   1482:                                case HEAD_CONNECTION:
                   1483:                                        if(!stricmp(value,"Keep-Alive")) {
                   1484:                                                session->req.keep_alive=TRUE;
                   1485:                                        }
                   1486:                                        if(!stricmp(value,"Close")) {
                   1487:                                                session->req.keep_alive=FALSE;
                   1488:                                        }
                   1489:                                        break;
                   1490:                                case HEAD_REFERER:
                   1491:                                        if(session->req.ld!=NULL)
                   1492:                                                session->req.ld->referrer=strdup(value);
                   1493:                                        break;
                   1494:                                case HEAD_AGENT:
                   1495:                                        if(session->req.ld!=NULL)
                   1496:                                                session->req.ld->agent=strdup(value);
                   1497:                                        break;
                   1498:                                default:
                   1499:                                        break;
                   1500:                        }
                   1501:                        if(session->req.dynamic==IS_CGI)  {
                   1502:                                sprintf(env_name,"HTTP_%s",head_line);
                   1503:                                add_env(session,env_name,value);
                   1504:                        }
                   1505:                }
                   1506:        }
                   1507:        if(content_len)  {
                   1508:                if((session->req.post_data=malloc(content_len+1)) != NULL)  {
                   1509:                        session->req.post_len=recvbufsocket(session->socket,session->req.post_data,content_len);
                   1510:                        if(session->req.post_len != content_len)
                   1511:                                lprintf(LOG_DEBUG,"%04d !ERROR Browser said they sent %d bytes, but I got %d",session->socket,content_len,session->req.post_len);
                   1512:                        if(session->req.post_len<0)
                   1513:                                session->req.post_len=0;
                   1514:                        session->req.post_data[session->req.post_len]=0;
                   1515:                        if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS)  {
                   1516:                                js_add_request_prop(session,"post_data",session->req.post_data);
                   1517:                                js_parse_query(session,session->req.post_data);
                   1518:                        }
                   1519:                }
                   1520:                else  {
                   1521:                        lprintf(LOG_CRIT,"%04d !ERROR Allocating %d bytes of memory",session->socket,content_len);
                   1522:                        return(FALSE);
                   1523:                }
                   1524:        }
                   1525:        if(session->req.dynamic==IS_CGI)
                   1526:                add_env(session,"SERVER_NAME",session->req.host[0] ? session->req.host : startup->host_name );
                   1527:        return TRUE;
                   1528: }
                   1529: 
                   1530: static int get_version(char *p)
                   1531: {
                   1532:        int             i;
                   1533:        if(p==NULL)
                   1534:                return(0);
                   1535:        while(*p && *p<' ') p++;
                   1536:        if(*p==0)
                   1537:                return(0);
                   1538:        for(i=1;http_vers[i]!=NULL;i++) {
                   1539:                if(!stricmp(p,http_vers[i])) {
                   1540:                        return(i);
                   1541:                }
                   1542:        }
                   1543:        return(i-1);
                   1544: }
                   1545: 
                   1546: static int is_dynamic_req(http_session_t* session)
                   1547: {
                   1548:        int             i=0;
                   1549:        char    drive[4];
                   1550:        char    dir[MAX_PATH+1];
                   1551:        char    fname[MAX_PATH+1];
                   1552:        char    ext[MAX_PATH+1];
                   1553:        char    path[MAX_PATH+1];
                   1554: 
                   1555:        check_extra_path(session);
                   1556:        _splitpath(session->req.physical_path, drive, dir, fname, ext);
                   1557: 
                   1558:        if(stricmp(ext,startup->ssjs_ext)==0)
                   1559:                i=IS_SSJS;
                   1560:        else if(stricmp(ext,startup->js_ext)==0)
                   1561:                i=IS_JS;
                   1562:        if(!(startup->options&BBS_OPT_NO_JAVASCRIPT) && i)  {
                   1563:                lprintf(LOG_INFO,"%04d Setting up JavaScript support", session->socket);
                   1564:                if(!js_setup(session)) {
                   1565:                        lprintf(LOG_ERR,"%04d !ERROR setting up JavaScript support", session->socket);
                   1566:                        send_error(session,error_500);
                   1567:                        return(IS_STATIC);
                   1568:                }
                   1569: 
                   1570:                sprintf(path,"%s/SBBS_SSJS.%d.html",startup->cgi_temp_dir,session->socket);
                   1571:                if((session->req.fp=fopen(path,"wb"))==NULL) {
                   1572:                        lprintf(LOG_ERR,"%04d !ERROR %d opening/creating %s", session->socket, errno, path);
                   1573:                        send_error(session,error_500);
                   1574:                        return(IS_STATIC);
                   1575:                }
                   1576:                return(i);
                   1577:        }
                   1578: 
                   1579:        if(!(startup->options&WEB_OPT_NO_CGI)) {
                   1580:                for(i=0; startup->cgi_ext!=NULL && startup->cgi_ext[i]!=NULL; i++)  {
                   1581:                        if(stricmp(ext,startup->cgi_ext[i])==0)  {
                   1582:                                init_enviro(session);
                   1583:                                return(IS_CGI);
                   1584:                        }
                   1585:                }
                   1586:                if(stricmp(dir,cgi_dir)==0)  {
                   1587:                        init_enviro(session);
                   1588:                        return(IS_CGI);
                   1589:                }
                   1590:        }
                   1591: 
                   1592:        return(IS_STATIC);
                   1593: }
                   1594: 
                   1595: static char *get_request(http_session_t * session, char *req_line)
                   1596: {
                   1597:        char*   p;
                   1598:        char*   query;
                   1599:        char*   retval;
                   1600:        int             offset;
                   1601: 
                   1602:        SKIP_WHITESPACE(req_line);
                   1603:        SAFECOPY(session->req.virtual_path,req_line);
                   1604:        strtok(session->req.virtual_path," \t");
                   1605:        SAFECOPY(session->req.request_line,session->req.virtual_path);
                   1606:        retval=strtok(NULL," \t");
                   1607:        strtok(session->req.virtual_path,"?");
                   1608:        query=strtok(NULL,"");
                   1609: 
                   1610:        /* Must initialize physical_path before calling is_dynamic_req() */
                   1611:        SAFECOPY(session->req.physical_path,session->req.virtual_path);
                   1612:        unescape(session->req.physical_path);
                   1613:        if(!strnicmp(session->req.physical_path,http_scheme,http_scheme_len)) {
                   1614:                /* Set HOST value... ignore HOST header */
                   1615:                SAFECOPY(session->req.host,session->req.physical_path+http_scheme_len);
                   1616:                strtok(session->req.physical_path,"/");
                   1617:                p=strtok(NULL,"/");
                   1618:                if(p==NULL) {
                   1619:                        /* Do not allow host values larger than 128 bytes */
                   1620:                        session->req.host[0]=0;
                   1621:                        p=session->req.physical_path+http_scheme_len;
                   1622:                }
                   1623:                offset=p-session->req.physical_path;
                   1624:                memmove(session->req.physical_path
                   1625:                        ,session->req.physical_path+offset
                   1626:                        ,strlen(session->req.physical_path+offset)+1    /* move '\0' terminator too */
                   1627:                        );
                   1628:        }
                   1629:        if(query!=NULL)
                   1630:                SAFECOPY(session->req.query_str,query);
                   1631: 
                   1632:        return(retval);
                   1633: }
                   1634: 
                   1635: static char *get_method(http_session_t * session, char *req_line)
                   1636: {
                   1637:        int i;
                   1638: 
                   1639:        for(i=0;methods[i]!=NULL;i++) {
                   1640:                if(!strnicmp(req_line,methods[i],strlen(methods[i]))) {
                   1641:                        session->req.method=i;
                   1642:                        if(strlen(req_line)<strlen(methods[i])+2) {
                   1643:                                send_error(session,"400 Bad Request");
                   1644:                                return(NULL);
                   1645:                        }
                   1646:                        return(req_line+strlen(methods[i])+1);
                   1647:                }
                   1648:        }
                   1649:        if(req_line!=NULL && *req_line>=' ')
                   1650:                send_error(session,"501 Not Implemented");
                   1651:        return(NULL);
                   1652: }
                   1653: 
                   1654: static BOOL get_request_headers(http_session_t * session)
                   1655: {
                   1656:        char    head_line[MAX_REQUEST_LINE+1];
                   1657:        char    next_char;
                   1658:        char    *value;
                   1659:        int             i;
                   1660: 
                   1661:        while(sockreadline(session,head_line,sizeof(head_line)-1)>0) {
                   1662:                /* Multi-line headers */
                   1663:                while((recvfrom(session->socket,&next_char,1,MSG_PEEK,NULL,0)>0)
                   1664:                        && (next_char=='\t' || next_char==' ')) {
                   1665:                        i=strlen(head_line);
                   1666:                        if(i>sizeof(head_line)-1) {
                   1667:                                lprintf(LOG_ERR,"%04d !ERROR long multi-line header. The web server is broken!", session->socket);
                   1668:                                i=sizeof(head_line)/2;
                   1669:                                break;
                   1670:                        }
                   1671:                        sockreadline(session,head_line+i,sizeof(head_line)-i-1);
                   1672:                }
                   1673:                listPushNodeString(&session->req.headers,head_line);
                   1674: 
                   1675:                if((strtok(head_line,":"))!=NULL && (value=strtok(NULL,""))!=NULL) {
                   1676:                        i=get_header_type(head_line);
                   1677:                        while(*value && *value<=' ') value++;
                   1678:                        switch(i) {
                   1679:                                case HEAD_HOST:
                   1680:                                        if(session->req.host[0]==0) {
                   1681:                                                SAFECOPY(session->req.host,value);
                   1682:                                                if(startup->options&WEB_OPT_DEBUG_RX)
                   1683:                                                        lprintf(LOG_INFO,"%04d Grabbing from virtual host: %s"
                   1684:                                                                ,session->socket,value);
                   1685:                                        }
                   1686:                                        break;
                   1687:                                default:
                   1688:                                        break;
                   1689:                        }
                   1690:                }
                   1691:        }
                   1692:        return TRUE;
                   1693: }
                   1694: 
                   1695: static BOOL get_fullpath(http_session_t * session)
                   1696: {
                   1697:        char    str[MAX_PATH+1];
                   1698: 
                   1699:        if(!(startup->options&WEB_OPT_VIRTUAL_HOSTS))
                   1700:                session->req.host[0]=0;
                   1701:        if(session->req.host[0]) {
                   1702:                safe_snprintf(str,sizeof(str),"%s/%s",root_dir,session->req.host);
                   1703:                if(isdir(str))
                   1704:                        safe_snprintf(str,sizeof(str),"%s/%s%s",root_dir,session->req.host,session->req.physical_path);
                   1705:                else
                   1706:                        safe_snprintf(str,sizeof(str),"%s%s",root_dir,session->req.physical_path);
                   1707:        } else
                   1708:                sprintf(str,"%s%s",root_dir,session->req.physical_path);
                   1709: 
                   1710:        if(FULLPATH(session->req.physical_path,str,sizeof(session->req.physical_path))==NULL) {
                   1711:                send_error(session,error_500);
                   1712:                return(FALSE);
                   1713:        }
                   1714: 
                   1715:        return(TRUE);
                   1716: }
                   1717: 
                   1718: static BOOL get_req(http_session_t * session, char *request_line)
                   1719: {
                   1720:        char    req_line[MAX_REQUEST_LINE+1];
                   1721:        char *  p;
                   1722:        int             is_redir=0;
                   1723: 
                   1724:        req_line[0]=0;
                   1725:        if(request_line == NULL) {
                   1726:                if(sockreadline(session,req_line,sizeof(req_line)-1)<0)
                   1727:                        req_line[0]=0;
                   1728:                if(req_line[0])
                   1729:                        lprintf(LOG_DEBUG,"%04d Request: %s",session->socket,req_line);
                   1730:        }
                   1731:        else {
                   1732:                lprintf(LOG_DEBUG,"%04d Handling Internal Redirect to: %s",session->socket,request_line);
                   1733:                SAFECOPY(req_line,request_line);
                   1734:                is_redir=1;
                   1735:        }
                   1736:        if(session->req.ld!=NULL)
                   1737:                session->req.ld->request=strdup(req_line);
                   1738:        if(req_line[0]) {
                   1739:                p=NULL;
                   1740:                p=get_method(session,req_line);
                   1741:                if(p!=NULL) {
                   1742:                        p=get_request(session,p);
                   1743:                        session->http_ver=get_version(p);
                   1744:                        if(session->http_ver>=HTTP_1_1)
                   1745:                                session->req.keep_alive=TRUE;
                   1746:                        if(!is_redir)
                   1747:                                get_request_headers(session);
                   1748:                        if(!get_fullpath(session))
                   1749:                                return(FALSE);
                   1750:                        session->req.dynamic=is_dynamic_req(session);
                   1751:                        if(session->req.query_str[0])  {
                   1752:                                switch(session->req.dynamic) {
                   1753:                                        case IS_CGI:
                   1754:                                                add_env(session,"QUERY_STRING",session->req.query_str);
                   1755:                                                break;
                   1756:                                        case IS_JS:
                   1757:                                        case IS_SSJS:
                   1758:                                                js_add_request_prop(session,"query_string",session->req.query_str);
                   1759:                                                js_parse_query(session,session->req.query_str);
                   1760:                                                break;
                   1761:                                }
                   1762:                        }
                   1763: 
                   1764:                        if(session->req.dynamic==IS_CGI)  {
                   1765:                                add_env(session,"REQUEST_METHOD",methods[session->req.method]);
                   1766:                                add_env(session,"SERVER_PROTOCOL",session->http_ver ? 
                   1767:                                        http_vers[session->http_ver] : "HTTP/0.9");
                   1768:                        }
                   1769:                        return(TRUE);
                   1770:                }
                   1771:        }
                   1772:        session->req.keep_alive=FALSE;
                   1773:        close_request(session);
                   1774:        return FALSE;
                   1775: }
                   1776: 
                   1777: /* This may exist somewhere else - ToDo */
                   1778: static char *find_last_slash(char *str)
                   1779: {
                   1780: #ifdef _WIN32
                   1781:        char * LastFSlash;
                   1782:        char * LastBSlash;
                   1783: 
                   1784:        LastFSlash=strrchr(str,'/');
                   1785:        LastBSlash=strrchr(str,'\\');
                   1786:        if(LastFSlash==NULL)
                   1787:                return(LastBSlash);
                   1788:        if(LastBSlash==NULL)
                   1789:                return(LastFSlash);
                   1790:        if(LastBSlash < LastFSlash)
                   1791:                return(LastFSlash);
                   1792:        return(LastBSlash);
                   1793: #else
                   1794:        return(strrchr(str,'/'));
                   1795: #endif
                   1796: }
                   1797: 
                   1798: /* This may exist somewhere else - ToDo */
                   1799: static char *find_first_slash(char *str)
                   1800: {
                   1801: #ifdef _WIN32
                   1802:        char * FirstFSlash;
                   1803:        char * FirstBSlash;
                   1804: 
                   1805:        FirstFSlash=strchr(str,'/');
                   1806:        FirstBSlash=strchr(str,'\\');
                   1807:        if(FirstFSlash==NULL)
                   1808:                return(FirstBSlash);
                   1809:        if(FirstBSlash==NULL)
                   1810:                return(FirstFSlash);
                   1811:        if(FirstBSlash > FirstFSlash)
                   1812:                return(FirstFSlash);
                   1813:        return(FirstBSlash);
                   1814: #else
                   1815:        return(strchr(str,'/'));
                   1816: #endif
                   1817: }
                   1818: 
                   1819: static BOOL check_extra_path(http_session_t * session)
                   1820: {
                   1821:        char    *p;
                   1822:        char    rpath[MAX_PATH+1];
                   1823:        char    vpath[MAX_PATH+1];
                   1824:        char    epath[MAX_PATH+1];
                   1825:        char    str[MAX_PATH+1];
                   1826:        struct  stat sb;
                   1827: 
                   1828:        epath[0]=0;
                   1829:        if(IS_PATH_DELIM(*lastchar(session->req.physical_path)) || stat(session->req.physical_path,&sb)==-1 /* && errno==ENOTDIR */)
                   1830:        {
                   1831:                SAFECOPY(vpath,session->req.virtual_path);
                   1832:                SAFECOPY(rpath,session->req.physical_path);
                   1833:                while((p=find_last_slash(vpath))!=NULL)
                   1834:                {
                   1835:                        *p=0;
                   1836:                        if(p==vpath)
                   1837:                                return(FALSE);
                   1838:                        if((p=find_last_slash(rpath))==NULL)
                   1839:                                return(FALSE);
                   1840:                        *p=0;
                   1841:                        SAFECOPY(str,epath);
                   1842:                        sprintf(epath,"/%s%s",(p+1),str);
                   1843:                        if(stat(rpath,&sb)!=-1 && (!(sb.st_mode&S_IFDIR)))
                   1844:                        {
                   1845:                                SAFECOPY(session->req.extra_path_info,epath);
                   1846:                                SAFECOPY(session->req.virtual_path,vpath);
                   1847:                                SAFECOPY(session->req.physical_path,rpath);
                   1848:                                session->req.dynamic=IS_CGI;
                   1849:                                return(TRUE);
                   1850:                        }
                   1851:                }
                   1852:        }
                   1853:        return(FALSE);
                   1854: }
                   1855: 
                   1856: static BOOL check_request(http_session_t * session)
                   1857: {
                   1858:        char    path[MAX_PATH+1];
                   1859:        char    str[MAX_PATH+1];
                   1860:        char    last_ch;
                   1861:        char*   last_slash;
                   1862:        char*   p;
                   1863:        FILE*   file;
                   1864:        int             i;
                   1865:        struct stat sb;
                   1866:        int             send404=0;
                   1867: 
                   1868:        SAFECOPY(path,session->req.physical_path);
                   1869:        if(startup->options&WEB_OPT_DEBUG_TX)
                   1870:                lprintf(LOG_DEBUG,"%04d Path is: %s",session->socket,path);
                   1871: 
                   1872:        if(isdir(path)) {
                   1873:                last_ch=*lastchar(path);
                   1874:                if(!IS_PATH_DELIM(last_ch))  {
                   1875:                        session->req.send_location=MOVED_PERM;
                   1876:                        strcat(path,"/");
                   1877:                }
                   1878:                last_ch=*lastchar(session->req.virtual_path);
                   1879:                if(!IS_PATH_DELIM(last_ch))  {
                   1880:                        session->req.send_location=MOVED_PERM;
                   1881:                        strcat(session->req.virtual_path,"/");
                   1882:                }
                   1883:                last_slash=find_last_slash(path);
                   1884:                if(last_slash==NULL) {
                   1885:                        send_error(session,error_500);
                   1886:                        return(FALSE);
                   1887:                }
                   1888:                last_slash++;
                   1889:                for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++)  {
                   1890:                        *last_slash=0;
                   1891:                        strcat(path,startup->index_file_name[i]);
                   1892:                        if(startup->options&WEB_OPT_DEBUG_TX)
                   1893:                                lprintf(LOG_DEBUG,"%04d Checking for %s",session->socket,path);
                   1894:                        if(!stat(path,&sb))
                   1895:                                break;
                   1896:                }
                   1897: 
                   1898:                /* Don't send 404 unless authourized... prevent info leak */
                   1899:                if(startup->index_file_name[i] == NULL)
                   1900:                        send404=1;
                   1901:                else {
                   1902:                        strcat(session->req.virtual_path,startup->index_file_name[i]);
                   1903:                        if(session->req.send_location != MOVED_PERM)
                   1904:                                session->req.send_location=MOVED_STAT;
                   1905:                }
                   1906:        }
                   1907:        if(strnicmp(path,root_dir,strlen(root_dir))) {
                   1908:                session->req.keep_alive=FALSE;
                   1909:                send_error(session,"400 Bad Request");
                   1910:                lprintf(LOG_NOTICE,"%04d !ERROR Request for %s is outside of web root %s"
                   1911:                        ,session->socket,path,root_dir);
                   1912:                return(FALSE);
                   1913:        }
                   1914: 
                   1915:        /* Set default ARS to a 0-length string */
                   1916:        session->req.ars[0]=0;
                   1917:        /* Walk up from root_dir checking for access.ars */
                   1918:        SAFECOPY(str,path);
                   1919:        last_slash=str+strlen(root_dir)-1;
                   1920:        /* Loop while there's more /s in path*/
                   1921:        p=last_slash;
                   1922: 
                   1923:        while((last_slash=find_first_slash(p+1))!=NULL) {
                   1924:                p=last_slash;
                   1925:                /* Terminate the path after the slash */
                   1926:                *(last_slash+1)=0;
                   1927:                strcat(str,"access.ars");
                   1928:                if(!stat(str,&sb)) {
                   1929:                        if(!strcmp(path,str)) {
                   1930:                                send_error(session,"403 Forbidden");
                   1931:                                return(FALSE);
                   1932:                        }
                   1933:                        /* Read access.ars file */
                   1934:                        if((file=fopen(str,"r"))!=NULL) {
                   1935:                                fgets(session->req.ars,sizeof(session->req.ars),file);
                   1936:                                fclose(file);
                   1937:                        }
                   1938:                        else  {
                   1939:                                /* If cannot open access.ars, only allow sysop access */
                   1940:                                SAFECOPY(session->req.ars,"LEVEL 90");
                   1941:                                break;
                   1942:                        }
                   1943:                        /* Truncate at \r or \n - can use last_slash since I'm done with it.*/
                   1944:                        truncsp(session->req.ars);
                   1945:                }
                   1946:                SAFECOPY(str,path);
                   1947:        }
                   1948: 
                   1949:        if(!check_ars(session)) {
                   1950:                /* No authentication provided */
                   1951:                sprintf(str,"401 Unauthorized%s%s: Basic realm=\"%s\""
                   1952:                        ,newline,get_header(HEAD_WWWAUTH),scfg.sys_name);
                   1953:                send_error(session,str);
                   1954:                return(FALSE);
                   1955:        }
                   1956: 
                   1957:        if(stat(path,&sb) || IS_PATH_DELIM(*(lastchar(path))) || send404) {
                   1958:                if(startup->options&WEB_OPT_DEBUG_TX)
                   1959:                        lprintf(LOG_DEBUG,"%04d 404 - %s does not exist",session->socket,path);
                   1960:                send_error(session,error_404);
                   1961:                return(FALSE);
                   1962:        }
                   1963:        SAFECOPY(session->req.physical_path,path);
                   1964:        if(session->req.dynamic==IS_CGI)  {
                   1965:                add_env(session,"SCRIPT_NAME",session->req.virtual_path);
                   1966:        }
                   1967:        SAFECOPY(str,session->req.virtual_path);
                   1968:        last_slash=find_last_slash(str);
                   1969:        if(last_slash!=NULL)
                   1970:                *(last_slash+1)=0;
                   1971:        if(session->req.dynamic==IS_CGI && *(session->req.extra_path_info))
                   1972:        {
                   1973:                sprintf(str,"%s%s",startup->root_dir,session->req.extra_path_info);
                   1974:                add_env(session,"PATH_TRANSLATED",str);
                   1975:                add_env(session,"PATH_INFO",session->req.extra_path_info);
                   1976:        }
                   1977: 
                   1978:        return(TRUE);
                   1979: }
                   1980: 
                   1981: static BOOL exec_cgi(http_session_t *session)
                   1982: {
                   1983:        char    cmdline[MAX_PATH+256];
                   1984: #ifdef __unix__
                   1985:        /* ToDo: Damn, that's WAY too many variables */
                   1986:        int             i=0;
                   1987:        int             status=0;
                   1988:        pid_t   child=0;
                   1989:        int             in_pipe[2];
                   1990:        int             out_pipe[2];
                   1991:        int             err_pipe[2];
                   1992:        struct timeval tv={0,0};
                   1993:        fd_set  read_set;
                   1994:        fd_set  write_set;
                   1995:        int             high_fd=0;
                   1996:        char    buf[1024];
                   1997:        size_t  post_offset=0;
                   1998:        BOOL    done_parsing_headers=FALSE;
                   1999:        BOOL    done_reading=FALSE;
                   2000:        char    cgi_status[MAX_REQUEST_LINE+1];
                   2001:        char    header[MAX_REQUEST_LINE+1];
                   2002:        char    *directive=NULL;
                   2003:        char    *value=NULL;
                   2004:        BOOL    done_wait=FALSE;
                   2005:        BOOL    got_valid_headers=FALSE;
                   2006:        time_t  start;
                   2007:        char    cgipath[MAX_PATH+1];
                   2008:        char    *p;
                   2009:        char    ch;
                   2010:        list_node_t     *node;
                   2011: #endif
                   2012: 
                   2013:        SAFECOPY(cmdline,session->req.physical_path);
                   2014:        
                   2015: #ifdef __unix__
                   2016: 
                   2017:        lprintf(LOG_INFO,"%04d Executing %s",session->socket,cmdline);
                   2018: 
                   2019:        /* ToDo: Should only do this if the Content-Length header was NOT sent */
                   2020:        session->req.keep_alive=FALSE;
                   2021: 
                   2022:        /* Set up I/O pipes */
                   2023:        if(pipe(in_pipe)!=0) {
                   2024:                lprintf(LOG_ERR,"%04d Can't create in_pipe",session->socket,buf);
                   2025:                return(FALSE);
                   2026:        }
                   2027: 
                   2028:        if(pipe(out_pipe)!=0) {
                   2029:                lprintf(LOG_ERR,"%04d Can't create out_pipe",session->socket,buf);
                   2030:                return(FALSE);
                   2031:        }
                   2032: 
                   2033:        if(pipe(err_pipe)!=0) {
                   2034:                lprintf(LOG_ERR,"%04d Can't create err_pipe",session->socket,buf);
                   2035:                return(FALSE);
                   2036:        }
                   2037: 
                   2038:        if((child=fork())==0)  {
                   2039:                /* Do a full suid thing. */
                   2040:                if(startup->setuid!=NULL)
                   2041:                        startup->setuid(TRUE);
                   2042: 
                   2043:                /* Set up environment */
                   2044:                for(node=listFirstNode(&session->req.cgi_env);node!=NULL;node=listNextNode(node))
                   2045:                        putenv(listNodeData(node));
                   2046: 
                   2047:                /* Set up STDIO */
                   2048:                close(in_pipe[1]);              /* close write-end of pipe */
                   2049:                dup2(in_pipe[0],0);             /* redirect stdin */
                   2050:                close(in_pipe[0]);              /* close excess file descriptor */
                   2051:                close(out_pipe[0]);             /* close read-end of pipe */
                   2052:                dup2(out_pipe[1],1);    /* stdout */
                   2053:                close(out_pipe[1]);             /* close excess file descriptor */
                   2054:                close(err_pipe[0]);             /* close read-end of pipe */
                   2055:                dup2(err_pipe[1],2);    /* stderr */
                   2056:                close(err_pipe[1]);             /* close excess file descriptor */
                   2057: 
                   2058:                SAFECOPY(cgipath,cmdline);
                   2059:                if((p=strrchr(cgipath,'/'))!=NULL)
                   2060:                {
                   2061:                        *p=0;
                   2062:                        chdir(cgipath);
                   2063:                }
                   2064: 
                   2065:                /* Execute command */
                   2066:                execl(cmdline,cmdline,NULL);
                   2067:                lprintf(LOG_ERR,"%04d !FAILED! execl()",session->socket);
                   2068:                exit(EXIT_FAILURE); /* Should never happen */
                   2069:        }
                   2070: 
                   2071:        if(child==-1)  {
                   2072:                lprintf(LOG_ERR,"%04d !FAILED! fork() errno=%d",session->socket,errno);
                   2073:                close(in_pipe[1]);              /* close write-end of pipe */
                   2074:                close(out_pipe[0]);             /* close read-end of pipe */
                   2075:                close(err_pipe[0]);             /* close read-end of pipe */
                   2076:        }
                   2077: 
                   2078:        close(in_pipe[0]);              /* close excess file descriptor */
                   2079:        close(out_pipe[1]);             /* close excess file descriptor */
                   2080:        close(err_pipe[1]);             /* close excess file descriptor */
                   2081: 
                   2082:        if(child==-1)
                   2083:                return(FALSE);
                   2084: 
                   2085:        start=time(NULL);
                   2086: 
                   2087: 
                   2088:        post_offset+=write(in_pipe[1],
                   2089:                session->req.post_data+post_offset,
                   2090:                session->req.post_len-post_offset);
                   2091: 
                   2092:        high_fd=out_pipe[0];
                   2093:        if(err_pipe[0]>high_fd)
                   2094:                high_fd=err_pipe[0];
                   2095:        if(in_pipe[1]>high_fd)
                   2096:                high_fd=in_pipe[1];
                   2097:        if(session->socket>high_fd)
                   2098:                high_fd=session->socket;
                   2099: 
                   2100:        /* ToDo: Magically set done_parsing_headers for nph-* scripts */
                   2101:        cgi_status[0]=0;
                   2102:        while(!done_reading)  {
                   2103:                tv.tv_sec=startup->max_cgi_inactivity;
                   2104:                tv.tv_usec=0;
                   2105: 
                   2106:                FD_ZERO(&read_set);
                   2107:                FD_SET(out_pipe[0],&read_set);
                   2108:                FD_SET(err_pipe[0],&read_set);
                   2109:                FD_SET(session->socket,&read_set);
                   2110:                FD_ZERO(&write_set);
                   2111:                if(post_offset < session->req.post_len)
                   2112:                        FD_SET(in_pipe[1],&write_set);
                   2113: 
                   2114:                if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0)  {
                   2115:                        if(FD_ISSET(session->socket,&read_set))  {
                   2116:                                if(recv(session->socket,&ch,1,MSG_PEEK) < 1) /* Is there no data waiting? */
                   2117:                                        done_reading=TRUE;
                   2118:                        }
                   2119:                        if(FD_ISSET(in_pipe[1],&write_set))  {
                   2120:                                if(post_offset < session->req.post_len)  {
                   2121:                                        i=write(in_pipe[1],
                   2122:                                                session->req.post_data+post_offset,
                   2123:                                                session->req.post_len-post_offset);
                   2124:                                        post_offset += i;
                   2125:                                        if(post_offset>=session->req.post_len || done_reading)
                   2126:                                                close(in_pipe[1]);
                   2127:                                        else if(i!=post_offset)
                   2128:                                                start=time(NULL);
                   2129:                                }
                   2130:                        }
                   2131:                        if(FD_ISSET(out_pipe[0],&read_set))  {
                   2132:                                if(done_parsing_headers && got_valid_headers)  {
                   2133:                                        i=read(out_pipe[0],buf,sizeof(buf));
                   2134:                                        if(i>0)  {
                   2135:                                                int snt=0;
                   2136:                                                start=time(NULL);
                   2137:                                                if(session->req.method!=HTTP_HEAD) {
                   2138:                                                        snt=write(session->socket,buf,i);
                   2139:                                                        if(session->req.ld!=NULL && snt>0) {
                   2140:                                                                session->req.ld->size+=snt;
                   2141:                                                        }
                   2142:                                                }
                   2143:                                        }
                   2144:                                        else
                   2145:                                                done_reading=TRUE;
                   2146:                                }
                   2147:                                else  {
                   2148:                                        /* This is the tricky part */
                   2149:                                        i=pipereadline(out_pipe[0],buf,sizeof(buf));
                   2150:                                        if(i<0)  {
                   2151:                                                done_reading=TRUE;
                   2152:                                                got_valid_headers=FALSE;
                   2153:                                        }
                   2154:                                        else
                   2155:                                                start=time(NULL);
                   2156: 
                   2157:                                        if(!done_parsing_headers && *buf)  {
                   2158:                                                SAFECOPY(header,buf);
                   2159:                                                directive=strtok(header,":");
                   2160:                                                if(directive != NULL)  {
                   2161:                                                        value=strtok(NULL,"");
                   2162:                                                        i=get_header_type(directive);
                   2163:                                                        switch (i)  {
                   2164:                                                                case HEAD_LOCATION:
                   2165:                                                                        got_valid_headers=TRUE;
                   2166:                                                                        if(*value=='/')  {
                   2167:                                                                                unescape(value);
                   2168:                                                                                SAFECOPY(session->req.virtual_path,value);
                   2169:                                                                                session->req.send_location=MOVED_STAT;
                   2170:                                                                                if(cgi_status[0]==0)
                   2171:                                                                                        SAFECOPY(cgi_status,"302 Moved Temporarily");
                   2172:                                                                        } else  {
                   2173:                                                                                SAFECOPY(session->req.virtual_path,value);
                   2174:                                                                                session->req.send_location=MOVED_STAT;
                   2175:                                                                                if(cgi_status[0]==0)
                   2176:                                                                                        SAFECOPY(cgi_status,"302 Moved Temporarily");
                   2177:                                                                        }
                   2178:                                                                        break;
                   2179:                                                                case HEAD_STATUS:
                   2180:                                                                        SAFECOPY(cgi_status,value);
                   2181:                                                                        break;
                   2182:                                                                case HEAD_TYPE:
                   2183:                                                                        got_valid_headers=TRUE;
                   2184:                                                                default:
                   2185:                                                                        listPushNodeString(&session->req.dynamic_heads,buf);
                   2186:                                                        }
                   2187:                                                }
                   2188:                                        }
                   2189:                                        else  {
                   2190:                                                if(got_valid_headers)  {
                   2191:                                                        session->req.dynamic=IS_CGI;
                   2192:                                                        if(cgi_status[0]==0)
                   2193:                                                                SAFECOPY(cgi_status,"200 OK");
                   2194:                                                        send_headers(session,cgi_status);
                   2195:                                                }
                   2196:                                                done_parsing_headers=TRUE;
                   2197:                                        }
                   2198:                                }
                   2199:                        }
                   2200:                        if(FD_ISSET(err_pipe[0],&read_set))  {
                   2201:                                i=read(err_pipe[0],buf,sizeof(buf));
                   2202:                                buf[i]=0;
                   2203:                                if(i>0)
                   2204:                                        start=time(NULL);
                   2205:                        }
                   2206:                }
                   2207:                else  {
                   2208:                        if((time(NULL)-start) >= startup->max_cgi_inactivity)  {
                   2209:                                lprintf(LOG_ERR,"%04d CGI Script %s Timed out",session->socket,cmdline);
                   2210:                                done_reading=TRUE;
                   2211:                                start=0;
                   2212:                        }
                   2213:                }
                   2214:        }
                   2215: 
                   2216:        /* Drain STDERR */      
                   2217:        tv.tv_sec=1;
                   2218:        tv.tv_usec=0;
                   2219:        FD_ZERO(&read_set);
                   2220:        FD_SET(err_pipe[0],&read_set);
                   2221:        if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0)
                   2222:        if(FD_ISSET(err_pipe[0],&read_set)) {
                   2223:                while(pipereadline(err_pipe[0],buf,sizeof(buf))!=-1)
                   2224:                        lprintf(LOG_ERR,"%s",buf);
                   2225:        }
                   2226: 
                   2227:        if(!done_wait)  {
                   2228:                if(start)
                   2229:                        lprintf(LOG_NOTICE,"%04d CGI Script %s still alive on client exit",session->socket,cmdline);
                   2230:                kill(child,SIGTERM);
                   2231:                mswait(1000);
                   2232:                done_wait = (waitpid(child,&status,WNOHANG)==child);
                   2233:                if(!done_wait)  {
                   2234:                        kill(child,SIGKILL);
                   2235:                        done_wait = (waitpid(child,&status,0)==child);
                   2236:                }
                   2237:        }
                   2238: 
                   2239:        close(in_pipe[1]);              /* close write-end of pipe */
                   2240:        close(out_pipe[0]);             /* close read-end of pipe */
                   2241:        close(err_pipe[0]);             /* close read-end of pipe */
                   2242:        if(!got_valid_headers) {
                   2243:                lprintf(LOG_ERR,"%04d CGI Script %s did not generate valid headers",session->socket,cmdline);
                   2244:                return(FALSE);
                   2245:        }
                   2246: 
                   2247:        if(!done_parsing_headers) {
                   2248:                lprintf(LOG_ERR,"%04d CGI Script %s did not send data header termination",session->socket,cmdline);
                   2249:                return(FALSE);
                   2250:        }
                   2251: 
                   2252:        if(!done_parsing_headers || !got_valid_headers) {
                   2253:                return(FALSE);
                   2254:        }
                   2255:        return(TRUE);
                   2256: #else
                   2257:        /* Win32 exec_cgi() */
                   2258:        return(FALSE);
                   2259: #endif
                   2260: }
                   2261: 
                   2262: /********************/
                   2263: /* JavaScript stuff */
                   2264: /********************/
                   2265: 
                   2266: JSObject* DLLCALL js_CreateHttpReplyObject(JSContext* cx
                   2267:                                                                                   ,JSObject* parent, http_session_t *session)
                   2268: {
                   2269:        JSObject*       reply;
                   2270:        JSObject*       headers;
                   2271:        jsval           val;
                   2272:        JSString*       js_str;
                   2273:        
                   2274:        /* Return existing object if it's already been created */
                   2275:        if(JS_GetProperty(cx,parent,"http_reply",&val) && val!=JSVAL_VOID)  {
                   2276:                reply = JSVAL_TO_OBJECT(val);
                   2277:                JS_ClearScope(cx,reply);
                   2278:        }
                   2279:        else
                   2280:                reply = JS_DefineObject(cx, parent, "http_reply", NULL
                   2281:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
                   2282: 
                   2283:        if((js_str=JS_NewStringCopyZ(cx, "200 OK"))==NULL)
                   2284:                return(FALSE);
                   2285:        JS_DefineProperty(cx, reply, "status", STRING_TO_JSVAL(js_str)
                   2286:                ,NULL,NULL,JSPROP_ENUMERATE);
                   2287: 
                   2288:        /* Return existing object if it's already been created */
                   2289:        if(JS_GetProperty(cx,reply,"header",&val) && val!=JSVAL_VOID)  {
                   2290:                headers = JSVAL_TO_OBJECT(val);
                   2291:                JS_ClearScope(cx,headers);
                   2292:        }
                   2293:        else
                   2294:                headers = JS_DefineObject(cx, reply, "header", NULL
                   2295:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
                   2296: 
                   2297:        if((js_str=JS_NewStringCopyZ(cx, "text/html"))==NULL)
                   2298:                return(FALSE);
                   2299:        JS_DefineProperty(cx, headers, "Content-Type", STRING_TO_JSVAL(js_str)
                   2300:                ,NULL,NULL,JSPROP_ENUMERATE);
                   2301: 
                   2302:        return(reply);
                   2303: }
                   2304: 
                   2305: JSObject* DLLCALL js_CreateHttpRequestObject(JSContext* cx
                   2306:                                                                                         ,JSObject* parent, http_session_t *session)
                   2307: {
                   2308: /*     JSObject*       cookie; */
                   2309:        jsval           val;
                   2310: 
                   2311:        /* Return existing object if it's already been created */
                   2312:        if(JS_GetProperty(cx,parent,"http_request",&val) && val!=JSVAL_VOID)  {
                   2313:                session->js_request=JSVAL_TO_OBJECT(val);
                   2314:        }
                   2315:        else
                   2316:                session->js_request = JS_DefineObject(cx, parent, "http_request", NULL
                   2317:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
                   2318: 
                   2319:        js_add_request_prop(session,"path_info",session->req.extra_path_info);
                   2320:        js_add_request_prop(session,"method",methods[session->req.method]);
                   2321:        js_add_request_prop(session,"virtual_path",session->req.virtual_path);
                   2322: 
                   2323:        /* Return existing object if it's already been created */
                   2324:        if(JS_GetProperty(cx,session->js_request,"query",&val) && val!=JSVAL_VOID)  {
                   2325:                session->js_query = JSVAL_TO_OBJECT(val);
                   2326:                JS_ClearScope(cx,session->js_query);
                   2327:        }
                   2328:        else
                   2329:                session->js_query = JS_DefineObject(cx, session->js_request, "query", NULL
                   2330:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
                   2331: 
                   2332:        
                   2333:        /* Return existing object if it's already been created */
                   2334:        if(JS_GetProperty(cx,session->js_request,"header",&val) && val!=JSVAL_VOID)  {
                   2335:                session->js_header = JSVAL_TO_OBJECT(val);
                   2336:                JS_ClearScope(cx,session->js_header);
                   2337:        }
                   2338:        else
                   2339:                session->js_header = JS_DefineObject(cx, session->js_request, "header", NULL
                   2340:                                                                        , NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
                   2341: 
                   2342:        return(session->js_request);
                   2343: }
                   2344: 
                   2345: static void
                   2346: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
                   2347: {
                   2348:        char    line[64];
                   2349:        char    file[MAX_PATH+1];
                   2350:        char*   warning;
                   2351:        http_session_t* session;
                   2352: 
                   2353:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
                   2354:                return;
                   2355:        
                   2356:        if(report==NULL) {
                   2357:                lprintf(LOG_ERR,"%04d !JavaScript: %s", session->socket, message);
                   2358:                if(session->req.fp!=NULL)
                   2359:                        fprintf(session->req.fp,"!JavaScript: %s", message);
                   2360:                return;
                   2361:     }
                   2362: 
                   2363:        if(report->filename)
                   2364:                sprintf(file," %s",report->filename);
                   2365:        else
                   2366:                file[0]=0;
                   2367: 
                   2368:        if(report->lineno)
                   2369:                sprintf(line," line %u",report->lineno);
                   2370:        else
                   2371:                line[0]=0;
                   2372: 
                   2373:        if(JSREPORT_IS_WARNING(report->flags)) {
                   2374:                if(JSREPORT_IS_STRICT(report->flags))
                   2375:                        warning="strict warning";
                   2376:                else
                   2377:                        warning="warning";
                   2378:        } else
                   2379:                warning="";
                   2380: 
                   2381:        lprintf(LOG_ERR,"%04d !JavaScript %s%s%s: %s",session->socket,warning,file,line,message);
                   2382:        if(session->req.fp!=NULL)
                   2383:                fprintf(session->req.fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
                   2384: }
                   2385: 
                   2386: static JSBool
                   2387: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
                   2388: {
                   2389:     uintN              i;
                   2390:     JSString * str;
                   2391:        http_session_t* session;
                   2392: 
                   2393:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
                   2394:                return(JS_FALSE);
                   2395: 
                   2396:        if(session->req.fp==NULL)
                   2397:                return(JS_FALSE);
                   2398: 
                   2399:     for(i=0; i<argc; i++) {
                   2400: #if 0
                   2401:                if((str=JS_ValueToString(cx, argv[i]))==NULL)
                   2402:                        continue;
                   2403:                fprintf(session->req.fp,"%s",JS_GetStringBytes(str));
                   2404: #else
                   2405:                if((str=JS_ValueToString(cx, argv[i]))==NULL)
                   2406:                        continue;
                   2407:                fwrite(JS_GetStringBytes(str),1,JS_GetStringLength(str),session->req.fp);
                   2408: #endif
                   2409:        }
                   2410: 
                   2411:        return(JS_TRUE);
                   2412: }
                   2413: 
                   2414: static JSBool
                   2415: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
                   2416: {
                   2417:     uintN              i;
                   2418:     JSString * str;
                   2419:        http_session_t* session;
                   2420: 
                   2421:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
                   2422:                return(JS_FALSE);
                   2423: 
                   2424:        if(session->req.fp==NULL)
                   2425:                return(JS_FALSE);
                   2426: 
                   2427:     for (i=0; i<argc;i++) {
                   2428: #if 0
                   2429:                if((str=JS_ValueToString(cx, argv[i]))==NULL)
                   2430:                        continue;
                   2431:                fprintf(session->req.fp,"%s",JS_GetStringBytes(str));
                   2432: #else
                   2433:                if((str=JS_ValueToString(cx, argv[i]))==NULL)
                   2434:                        continue;
                   2435:                fwrite(JS_GetStringBytes(str),1,JS_GetStringLength(str),session->req.fp);
                   2436: #endif
                   2437:        }
                   2438: 
                   2439:        fprintf(session->req.fp,"\n");
                   2440: 
                   2441:        return(JS_TRUE);
                   2442: }
                   2443: 
                   2444: static JSFunctionSpec js_global_functions[] = {
                   2445:        {"write",           js_write,           1},             /* write to HTML file */
                   2446:        {"writeln",         js_writeln,         1},             /* write line to HTML file */
                   2447:        {0}
                   2448: };
                   2449: 
                   2450: static JSBool
                   2451: js_BranchCallback(JSContext *cx, JSScript *script)
                   2452: {
                   2453:        http_session_t* session;
                   2454: 
                   2455:        if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
                   2456:                return(JS_FALSE);
                   2457: 
                   2458:     return(js_CommonBranchCallback(cx,&session->js_branch));
                   2459: }
                   2460: 
                   2461: static JSContext* 
                   2462: js_initcx(http_session_t *session)
                   2463: {
                   2464:        JSContext*      js_cx;
                   2465: 
                   2466:        lprintf(LOG_INFO,"%04d JavaScript: Initializing context (stack: %lu bytes)"
                   2467:                ,session->socket,startup->js_cx_stack);
                   2468: 
                   2469:     if((js_cx = JS_NewContext(session->js_runtime, startup->js_cx_stack))==NULL)
                   2470:                return(NULL);
                   2471: 
                   2472:        lprintf(LOG_INFO,"%04d JavaScript: Context created",session->socket);
                   2473: 
                   2474:     JS_SetErrorReporter(js_cx, js_ErrorReporter);
                   2475: 
                   2476:        JS_SetBranchCallback(js_cx, js_BranchCallback);
                   2477: 
                   2478:        lprintf(LOG_INFO,"%04d JavaScript: Creating Global Objects and Classes",session->socket);
                   2479:        if((session->js_glob=js_CreateCommonObjects(js_cx, &scfg, NULL
                   2480:                                                                        ,NULL                                           /* global */
                   2481:                                                                        ,uptime                                         /* system */
                   2482:                                                                        ,startup->host_name                     /* system */
                   2483:                                                                        ,SOCKLIB_DESC                           /* system */
                   2484:                                                                        ,&session->js_branch            /* js */
                   2485:                                                                        ,&session->client                       /* client */
                   2486:                                                                        ,session->socket                        /* client */
                   2487:                                                                        ,&js_server_props                       /* server */
                   2488:                ))==NULL
                   2489:                || !JS_DefineFunctions(js_cx, session->js_glob, js_global_functions)) {
                   2490:                JS_DestroyContext(js_cx);
                   2491:                return(NULL);
                   2492:        }
                   2493: 
                   2494:        return(js_cx);
                   2495: }
                   2496: 
                   2497: static BOOL js_setup(http_session_t* session)
                   2498: {
                   2499:        JSObject*       argv;
                   2500: 
                   2501:        if(session->js_runtime == NULL) {
                   2502:                lprintf(LOG_INFO,"%04d JavaScript: Creating runtime: %lu bytes"
                   2503:                        ,session->socket,startup->js_max_bytes);
                   2504: 
                   2505:                if((session->js_runtime=JS_NewRuntime(startup->js_max_bytes))==NULL) {
                   2506:                        lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",session->socket);
                   2507:                        send_error(session,"500 Error creating JavaScript runtime");
                   2508:                        return(FALSE);
                   2509:                }
                   2510:        }
                   2511: 
                   2512:        if(session->js_cx==NULL) {      /* Context not yet created, create it now */
                   2513:                if(((session->js_cx=js_initcx(session))==NULL)) {
                   2514:                        lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript context",session->socket);
                   2515:                        send_error(session,"500 Error initializing JavaScript context");
                   2516:                        return(FALSE);
                   2517:                }
                   2518:                argv=JS_NewArrayObject(session->js_cx, 0, NULL);
                   2519: 
                   2520:                JS_DefineProperty(session->js_cx, session->js_glob, "argv", OBJECT_TO_JSVAL(argv)
                   2521:                        ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
                   2522:                JS_DefineProperty(session->js_cx, session->js_glob, "argc", INT_TO_JSVAL(0)
                   2523:                        ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
                   2524:        }
                   2525: 
                   2526:        lprintf(LOG_INFO,"%04d JavaScript: Initializing HttpRequest object",session->socket);
                   2527:        if(js_CreateHttpRequestObject(session->js_cx, session->js_glob, session)==NULL) {
                   2528:                lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript HttpRequest object",session->socket);
                   2529:                send_error(session,"500 Error initializing JavaScript HttpRequest object");
                   2530:                return(FALSE);
                   2531:        }
                   2532: 
                   2533:        lprintf(LOG_INFO,"%04d JavaScript: Initializing HttpReply object",session->socket);
                   2534:        if(js_CreateHttpReplyObject(session->js_cx, session->js_glob, session)==NULL) {
                   2535:                lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript HttpReply object",session->socket);
                   2536:                send_error(session,"500 Error initializing JavaScript HttpReply object");
                   2537:                return(FALSE);
                   2538:        }
                   2539: 
                   2540:        JS_SetContextPrivate(session->js_cx, session);
                   2541: 
                   2542:        return(TRUE);
                   2543: }
                   2544: 
                   2545: static BOOL exec_ssjs(http_session_t* session)  {
                   2546:        JSString*       js_str;
                   2547:        JSScript*       js_script;
                   2548:        jsval           rval;
                   2549:        jsval           val;
                   2550:        JSObject*       reply;
                   2551:        JSIdArray*      heads;
                   2552:        JSObject*       headers;
                   2553:        char            path[MAX_PATH+1];
                   2554:        char            str[MAX_REQUEST_LINE+1];
                   2555:        int                     i;
                   2556: 
                   2557:        js_add_request_prop(session,"real_path",session->req.physical_path);
                   2558:        js_add_request_prop(session,"ars",session->req.ars);
                   2559:        js_add_request_prop(session,"request_string",session->req.request_line);
                   2560:        js_add_request_prop(session,"host",session->req.host);
                   2561:        js_add_request_prop(session,"http_ver",http_vers[session->http_ver]);
                   2562:        js_add_request_prop(session,"remote_ip",session->host_ip);
                   2563:        js_add_request_prop(session,"remote_host",session->host_name);
                   2564: 
                   2565:        do {
                   2566:                /* RUN SCRIPT */
                   2567:                JS_ClearPendingException(session->js_cx);
                   2568: 
                   2569:                session->js_branch.counter=0;
                   2570: 
                   2571:                if((js_script=JS_CompileFile(session->js_cx, session->js_glob
                   2572:                        ,session->req.physical_path))==NULL) {
                   2573:                        lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)"
                   2574:                                ,session->socket,session->req.physical_path);
                   2575:                        return(FALSE);
                   2576:                }
                   2577: 
                   2578:                JS_ExecuteScript(session->js_cx, session->js_glob, js_script, &rval);
                   2579: 
                   2580:        } while(0);
                   2581: 
                   2582:        /* Read http_reply object */
                   2583:        JS_GetProperty(session->js_cx,session->js_glob,"http_reply",&val);
                   2584:        reply = JSVAL_TO_OBJECT(val);
                   2585:        JS_GetProperty(session->js_cx,reply,"status",&val);
                   2586:        SAFECOPY(session->req.status,JS_GetStringBytes(JSVAL_TO_STRING(val)));
                   2587:        JS_GetProperty(session->js_cx,reply,"header",&val);
                   2588:        headers = JSVAL_TO_OBJECT(val);
                   2589:        heads=JS_Enumerate(session->js_cx,headers);
                   2590:        for(i=0;i<heads->length;i++)  {
                   2591:                JS_IdToValue(session->js_cx,heads->vector[i],&val);
                   2592:                js_str=JSVAL_TO_STRING(val);
                   2593:                JS_GetProperty(session->js_cx,headers,JS_GetStringBytes(js_str),&val);
                   2594:                safe_snprintf(str,sizeof(str),"%s: %s"
                   2595:                        ,JS_GetStringBytes(js_str),JS_GetStringBytes(JSVAL_TO_STRING(val)));
                   2596:                listPushNodeString(&session->req.dynamic_heads,str);
                   2597:        }
                   2598:        JS_DestroyIdArray(session->js_cx, heads);
                   2599: 
                   2600:        /* Free up temporary resources here */
                   2601: 
                   2602:        if(js_script!=NULL) 
                   2603:                JS_DestroyScript(session->js_cx, js_script);
                   2604:        if(session->req.fp!=NULL)
                   2605:                fclose(session->req.fp);
                   2606: 
                   2607:        SAFECOPY(session->req.physical_path, path);
                   2608:        session->req.dynamic=IS_SSJS;
                   2609:        
                   2610:        return(TRUE);
                   2611: }
                   2612: 
                   2613: static void respond(http_session_t * session)
                   2614: {
                   2615:        BOOL            send_file=TRUE;
                   2616: 
                   2617:        if(session->req.dynamic==IS_CGI)  {
                   2618:                if(!exec_cgi(session))  {
                   2619:                        send_error(session,error_500);
                   2620:                        return;
                   2621:                }
                   2622:                close_request(session);
                   2623:                return;
                   2624:        }
                   2625: 
                   2626:        if(session->req.dynamic==IS_SSJS) {     /* Server-Side JavaScript */
                   2627:                if(!exec_ssjs(session))  {
                   2628:                        send_error(session,error_500);
                   2629:                        return;
                   2630:                }
                   2631:                sprintf(session->req.physical_path
                   2632:                        ,"%s/SBBS_SSJS.%d.html",startup->cgi_temp_dir,session->socket);
                   2633:        }
                   2634: 
                   2635:        session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
                   2636:        send_file=send_headers(session,session->req.status);
                   2637:        if(session->req.method==HTTP_HEAD)
                   2638:                send_file=FALSE;
                   2639:        if(send_file)  {
                   2640:                int snt=0;
                   2641:                lprintf(LOG_INFO,"%04d Sending file: %s",session->socket, session->req.physical_path);
                   2642:                snt=sock_sendfile(session->socket,session->req.physical_path);
                   2643:                if(session->req.ld!=NULL) {
                   2644:                        if(snt<0)
                   2645:                                snt=0;
                   2646:                        session->req.ld->size=snt;
                   2647:                }
                   2648:                if(snt>0)
                   2649:                        lprintf(LOG_INFO,"%04d Sent file: %s",session->socket, session->req.physical_path);
                   2650:        }
                   2651:        close_request(session);
                   2652: }
                   2653: 
                   2654: void http_session_thread(void* arg)
                   2655: {
                   2656:        int                             i;
                   2657:        char*                   host_name;
                   2658:        HOSTENT*                host;
                   2659:        SOCKET                  socket;
                   2660:        char                    redir_req[MAX_REQUEST_LINE+1];
                   2661:        char                    *redirp;
                   2662:        http_session_t  session=*(http_session_t*)arg;  /* copies arg BEFORE it's freed */
                   2663:        int                             loop_count;
                   2664: 
                   2665:        FREE_AND_NULL(arg);
                   2666: 
                   2667:        socket=session.socket;
                   2668:        lprintf(LOG_DEBUG,"%04d Session thread started", session.socket);
                   2669: 
                   2670: #ifdef _WIN32
                   2671:        if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
                   2672:                PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
                   2673: #endif
                   2674: 
                   2675:        thread_up(TRUE /* setuid */);
                   2676:        session.finished=FALSE;
                   2677: 
                   2678:        srand(time(NULL));      /* Seed random number generator */
                   2679:        sbbs_random(10);        /* Throw away first number */
                   2680: 
                   2681:        if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
                   2682:                host=NULL;
                   2683:        else
                   2684:                host=gethostbyaddr ((char *)&session.addr.sin_addr
                   2685:                        ,sizeof(session.addr.sin_addr),AF_INET);
                   2686: 
                   2687:        if(host!=NULL && host->h_name!=NULL)
                   2688:                host_name=host->h_name;
                   2689:        else
                   2690:                host_name=session.host_ip;
                   2691: 
                   2692:        SAFECOPY(session.host_name,host_name);
                   2693: 
                   2694:        if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP))  {
                   2695:                lprintf(LOG_INFO,"%04d Hostname: %s", session.socket, host_name);
                   2696:                for(i=0;host!=NULL && host->h_aliases!=NULL 
                   2697:                        && host->h_aliases[i]!=NULL;i++)
                   2698:                        lprintf(LOG_INFO,"%04d HostAlias: %s", session.socket, host->h_aliases[i]);
                   2699:                if(trashcan(&scfg,host_name,"host")) {
                   2700:                        close_socket(session.socket);
                   2701:                        lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", session.socket, host_name);
                   2702:                        thread_down();
                   2703:                        return;
                   2704:                }
                   2705:        }
                   2706: 
                   2707:        /* host_ip wasn't defined in http_session_thread */
                   2708:        if(trashcan(&scfg,session.host_ip,"ip")) {
                   2709:                close_socket(session.socket);
                   2710:                lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip);
                   2711:                thread_down();
                   2712:                return;
                   2713:        }
                   2714: 
                   2715:        active_clients++;
                   2716:        update_clients();
                   2717:        SAFECOPY(session.username,unknown);
                   2718: 
                   2719:        SAFECOPY(session.client.addr,session.host_ip);
                   2720:        SAFECOPY(session.client.host,session.host_name);
                   2721:        session.client.port=ntohs(session.addr.sin_port);
                   2722:        session.client.time=time(NULL);
                   2723:        session.client.protocol="HTTP";
                   2724:        session.client.user=session.username;
                   2725:        session.client.size=sizeof(session.client);
                   2726:        client_on(session.socket, &session.client, /* update existing client record? */FALSE);
                   2727: 
                   2728:        session.last_user_num=-1;
                   2729:        session.last_js_user_num=-1;
                   2730:        session.logon_time=0;
                   2731: 
                   2732:        session.subscan=(subscan_t*)malloc(sizeof(subscan_t)*scfg.total_subs);
                   2733: 
                   2734:        while(!session.finished && server_socket!=INVALID_SOCKET) {
                   2735:            memset(&(session.req), 0, sizeof(session.req));
                   2736:                SAFECOPY(session.req.status,"200 OK");
                   2737:                session.req.send_location=NO_LOCATION;
                   2738:                redirp=NULL;
                   2739:                loop_count=0;
                   2740:                while((redirp==NULL || session.req.send_location >= MOVED_TEMP)
                   2741:                                 && !session.finished && server_socket!=INVALID_SOCKET) {
                   2742:                        session.req.send_location=NO_LOCATION;
                   2743:                        session.req.ld=NULL;
                   2744:                        if(startup->options&WEB_OPT_HTTP_LOGGING) {
                   2745:                                if((session.req.ld=(struct log_data*)malloc(sizeof(struct log_data)))==NULL)
                   2746:                                        lprintf(LOG_ERR,"%04d Cannot allocate memory for log data!",session.socket);
                   2747:                        }
                   2748:                        if(session.req.ld!=NULL) {
                   2749:                                memset(session.req.ld,0,sizeof(struct log_data));
                   2750:                                session.req.ld->hostname=strdup(session.host_name);
                   2751:                        }
                   2752:                        listInit(&session.req.headers,0);
                   2753:                        listInit(&session.req.cgi_env,0);
                   2754:                        listInit(&session.req.dynamic_heads,0);
                   2755:                        if(get_req(&session,redirp)) {
                   2756:                                /* At this point, if redirp is non-NULL then the headers have already been parsed */
                   2757:                                if((session.http_ver<HTTP_1_0)||redirp!=NULL||parse_headers(&session)) {
                   2758:                                        if(check_request(&session)) {
                   2759:                                                if(session.req.send_location < MOVED_TEMP || session.req.virtual_path[0]!='/' || loop_count++ >= MAX_REDIR_LOOPS) {
                   2760:                                                        respond(&session);
                   2761:                                                }
                   2762:                                                else {
                   2763:                                                        safe_snprintf(redir_req,sizeof(redir_req),"%s %s%s%s",methods[session.req.method]
                   2764:                                                                ,session.req.virtual_path,session.http_ver<HTTP_1_0?"":" ",http_vers[session.http_ver]);
                   2765:                                                        lprintf(LOG_DEBUG,"%04d Internal Redirect to: %s",socket,redir_req);
                   2766:                                                        redirp=redir_req;
                   2767:                                                }
                   2768:                                        }
                   2769:                                }
                   2770:                        }
                   2771:                }
                   2772:        }
                   2773: 
                   2774:        http_logoff(&session);
                   2775: 
                   2776:        if(session.js_cx!=NULL) {
                   2777:                lprintf(LOG_INFO,"%04d JavaScript: Destroying context",socket);
                   2778:                JS_DestroyContext(session.js_cx);       /* Free Context */
                   2779:                session.js_cx=NULL;
                   2780:        }
                   2781: 
                   2782:        if(session.js_runtime!=NULL) {
                   2783:                lprintf(LOG_INFO,"%04d JavaScript: Destroying runtime",socket);
                   2784:                JS_DestroyRuntime(session.js_runtime);
                   2785:                session.js_runtime=NULL;
                   2786:        }
                   2787: 
                   2788:        FREE_AND_NULL(session.subscan);
                   2789: 
                   2790: #ifdef _WIN32
                   2791:        if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
                   2792:                PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
                   2793: #endif
                   2794: 
                   2795:        close_socket(session.socket);
                   2796: 
                   2797:        active_clients--;
                   2798:        update_clients();
                   2799:        client_off(socket);
                   2800: 
                   2801:        thread_down();
                   2802:        lprintf(LOG_INFO,"%04d Session thread terminated (%u clients, %u threads remain, %lu served)"
                   2803:                ,socket, active_clients, thread_count, served);
                   2804: 
                   2805: }
                   2806: 
                   2807: void DLLCALL web_terminate(void)
                   2808: {
                   2809:        lprintf(LOG_INFO,"%04d Web Server terminate",server_socket);
                   2810:        terminate_server=TRUE;
                   2811: }
                   2812: 
                   2813: static void cleanup(int code)
                   2814: {
                   2815:        free_cfg(&scfg);
                   2816: 
                   2817:        listFree(&log_list);
                   2818: 
                   2819:        mime_types=iniFreeNamedStringList(mime_types);
                   2820: 
                   2821:        semfile_list_free(&recycle_semfiles);
                   2822:        semfile_list_free(&shutdown_semfiles);
                   2823: 
                   2824:        if(server_socket!=INVALID_SOCKET) {
                   2825:                close_socket(server_socket);
                   2826:                server_socket=INVALID_SOCKET;
                   2827:        }
                   2828: 
                   2829:        update_clients();
                   2830: 
                   2831: #ifdef _WINSOCKAPI_
                   2832:        if(WSAInitialized && WSACleanup()!=0) 
                   2833:                lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
                   2834: #endif
                   2835: 
                   2836:        thread_down();
                   2837:        status("Down");
                   2838:        if(terminate_server || code)
                   2839:                lprintf(LOG_INFO,"#### Web Server thread terminated (%u threads remain, %lu clients served)"
                   2840:                        ,thread_count, served);
                   2841:        if(startup!=NULL && startup->terminated!=NULL)
                   2842:                startup->terminated(startup->cbdata,code);
                   2843: }
                   2844: 
                   2845: const char* DLLCALL web_ver(void)
                   2846: {
                   2847:        static char ver[256];
                   2848:        char compiler[32];
                   2849: 
                   2850:        DESCRIBE_COMPILER(compiler);
                   2851: 
                   2852:        sscanf("$Revision: 1.247 $", "%*s %s", revision);
                   2853: 
                   2854:        sprintf(ver,"%s %s%s  "
                   2855:                "Compiled %s %s with %s"
                   2856:                ,server_name
                   2857:                ,revision
                   2858: #ifdef _DEBUG
                   2859:                ," Debug"
                   2860: #else
                   2861:                ,""
                   2862: #endif
                   2863:                ,__DATE__, __TIME__, compiler);
                   2864: 
                   2865:        return(ver);
                   2866: }
                   2867: 
                   2868: void http_logging_thread(void* arg)
                   2869: {
                   2870:        char    base[MAX_PATH+1];
                   2871:        char    filename[MAX_PATH+1];
                   2872:        char    newfilename[MAX_PATH+1];
                   2873:        FILE*   logfile=NULL;
                   2874: 
                   2875:        http_logging_thread_running=TRUE;
                   2876:        terminate_http_logging_thread=FALSE;
                   2877: 
                   2878:        SAFECOPY(base,arg);
                   2879:        if(!base[0])
                   2880:                SAFEPRINTF(base,"%slogs/http-",scfg.logs_dir);
                   2881: 
                   2882:        filename[0]=0;
                   2883:        newfilename[0]=0;
                   2884: 
                   2885:        thread_up(TRUE /* setuid */);
                   2886: 
                   2887:        lprintf(LOG_DEBUG,"%04d http logging thread started", server_socket);
                   2888: 
                   2889:        for(;!terminate_http_logging_thread;) {
                   2890:                struct log_data *ld;
                   2891:                char    timestr[128];
                   2892:                char    sizestr[100];
                   2893: 
                   2894:                sem_wait(&log_sem);
                   2895:                if(terminate_http_logging_thread)
                   2896:                        break;
                   2897: 
                   2898:                pthread_mutex_lock(&log_mutex);
                   2899:                ld=listShiftNode(&log_list);
                   2900:                pthread_mutex_unlock(&log_mutex);
                   2901:                if(ld==NULL) {
                   2902:                        lprintf(LOG_ERR,"%04d http logging thread received NULL linked list log entry"
                   2903:                                ,server_socket);
                   2904:                        continue;
                   2905:                }
                   2906:                SAFECOPY(newfilename,base);
                   2907:                strftime(strchr(newfilename,0),15,"%Y-%m-%d.log",&ld->completed);
                   2908:                if(strcmp(newfilename,filename)) {
                   2909:                        if(logfile!=NULL)
                   2910:                                fclose(logfile);
                   2911:                        SAFECOPY(filename,newfilename);
                   2912:                        logfile=fopen(filename,"ab");
                   2913:                        lprintf(LOG_INFO,"%04d http logfile is now: %s",server_socket,filename);
                   2914:                }
                   2915:                if(logfile!=NULL) {
                   2916:                        sprintf(sizestr,"%d",ld->size);
                   2917:                        strftime(timestr,sizeof(timestr),"%d/%b/%Y:%H:%M:%S %z",&ld->completed);
                   2918:                        while(lock(fileno(logfile),0,1) && !terminate_http_logging_thread) {
                   2919:                                SLEEP(10);
                   2920:                        }
                   2921:                        fprintf(logfile,"%s %s %s [%s] \"%s\" %d %s \"%s\" \"%s\"\n"
                   2922:                                        ,ld->hostname?(ld->hostname[0]?ld->hostname:"-"):"-"
                   2923:                                        ,ld->ident?(ld->ident[0]?ld->ident:"-"):"-"
                   2924:                                        ,ld->user?(ld->user[0]?ld->user:"-"):"-"
                   2925:                                        ,timestr
                   2926:                                        ,ld->request?(ld->request[0]?ld->request:"-"):"-"
                   2927:                                        ,ld->status
                   2928:                                        ,ld->size?sizestr:"-"
                   2929:                                        ,ld->referrer?(ld->referrer[0]?ld->referrer:"-"):"-"
                   2930:                                        ,ld->agent?(ld->agent[0]?ld->agent:"-"):"-");
                   2931:                        fflush(logfile);
                   2932:                        unlock(fileno(logfile),0,1);
                   2933:                        FREE_AND_NULL(ld->hostname);
                   2934:                        FREE_AND_NULL(ld->ident);
                   2935:                        FREE_AND_NULL(ld->user);
                   2936:                        FREE_AND_NULL(ld->request);
                   2937:                        FREE_AND_NULL(ld->referrer);
                   2938:                        FREE_AND_NULL(ld->agent);
                   2939:                        FREE_AND_NULL(ld);
                   2940:                }
                   2941:                else {
                   2942:                        logfile=fopen(filename,"ab");
                   2943:                        lprintf(LOG_ERR,"%04d http logfile %s was not open!",server_socket,filename);
                   2944:                }
                   2945:        }
                   2946:        if(logfile!=NULL) {
                   2947:                fclose(logfile);
                   2948:                logfile=NULL;
                   2949:        }
                   2950:        thread_down();
                   2951:        lprintf(LOG_DEBUG,"%04d http logging thread terminated",server_socket);
                   2952: 
                   2953:        http_logging_thread_running=FALSE;
                   2954: }
                   2955: 
                   2956: void DLLCALL web_server(void* arg)
                   2957: {
                   2958:        int                             i;
                   2959:        int                             result;
                   2960:        time_t                  start;
                   2961:        WORD                    host_port;
                   2962:        char                    host_ip[32];
                   2963:        char                    path[MAX_PATH+1];
                   2964:        char                    logstr[256];
                   2965:        SOCKADDR_IN             server_addr={0};
                   2966:        SOCKADDR_IN             client_addr;
                   2967:        socklen_t               client_addr_len;
                   2968:        SOCKET                  client_socket;
                   2969:        SOCKET                  high_socket_set;
                   2970:        fd_set                  socket_set;
                   2971:        time_t                  t;
                   2972:        time_t                  initialized=0;
                   2973:        char*                   p;
                   2974:        char                    compiler[32];
                   2975:        http_session_t *        session;
                   2976:        struct timeval tv;
                   2977:        startup=(web_startup_t*)arg;
                   2978: 
                   2979:        web_ver();      /* get CVS revision */
                   2980: 
                   2981:     if(startup==NULL) {
                   2982:        sbbs_beep(100,500);
                   2983:        fprintf(stderr, "No startup structure passed!\n");
                   2984:        return;
                   2985:     }
                   2986: 
                   2987:        if(startup->size!=sizeof(web_startup_t)) {      /* verify size */
                   2988:                sbbs_beep(100,500);
                   2989:                sbbs_beep(300,500);
                   2990:                sbbs_beep(100,500);
                   2991:                fprintf(stderr, "Invalid startup structure!\n");
                   2992:                return;
                   2993:        }
                   2994: 
                   2995: #ifdef _THREAD_SUID_BROKEN
                   2996:        startup->seteuid(TRUE);
                   2997: #endif
                   2998: 
                   2999:        /* Setup intelligent defaults */
                   3000:        if(startup->port==0)                                    startup->port=IPPORT_HTTP;
                   3001:        if(startup->root_dir[0]==0)                             SAFECOPY(startup->root_dir,WEB_DEFAULT_ROOT_DIR);
                   3002:        if(startup->error_dir[0]==0)                    SAFECOPY(startup->error_dir,WEB_DEFAULT_ERROR_DIR);
                   3003:        if(startup->cgi_dir[0]==0)                              SAFECOPY(startup->cgi_dir,WEB_DEFAULT_CGI_DIR);
                   3004:        if(startup->max_inactivity==0)                  startup->max_inactivity=120; /* seconds */
                   3005:        if(startup->sem_chk_freq==0)                    startup->sem_chk_freq=2; /* seconds */
                   3006:        if(startup->js_max_bytes==0)                    startup->js_max_bytes=JAVASCRIPT_MAX_BYTES;
                   3007:        if(startup->js_cx_stack==0)                             startup->js_cx_stack=JAVASCRIPT_CONTEXT_STACK;
                   3008:        if(startup->ssjs_ext[0]==0)                             SAFECOPY(startup->ssjs_ext,"ssjs");
                   3009:        if(startup->js_ext[0]==0)                               SAFECOPY(startup->js_ext,"bbs");
                   3010: 
                   3011:        sprintf(js_server_props.version,"%s %s",server_name,revision);
                   3012:        js_server_props.version_detail=web_ver();
                   3013:        js_server_props.clients=&active_clients;
                   3014:        js_server_props.options=&startup->options;
                   3015:        js_server_props.interface_addr=&startup->interface_addr;
                   3016: 
                   3017:        /* Copy html directories */
                   3018:        SAFECOPY(root_dir,startup->root_dir);
                   3019:        SAFECOPY(error_dir,startup->error_dir);
                   3020:        SAFECOPY(cgi_dir,startup->cgi_dir);
                   3021: 
                   3022:        /* Change to absolute path */
                   3023:        prep_dir(startup->ctrl_dir, root_dir, sizeof(root_dir));
                   3024:        prep_dir(root_dir, error_dir, sizeof(error_dir));
                   3025:        prep_dir(root_dir, cgi_dir, sizeof(cgi_dir));
                   3026: 
                   3027:        /* Trim off trailing slash/backslash */
                   3028:        if(IS_PATH_DELIM(*(p=lastchar(root_dir))))      *p=0;
                   3029: 
                   3030:        uptime=0;
                   3031:        served=0;
                   3032:        startup->recycle_now=FALSE;
                   3033:        startup->shutdown_now=FALSE;
                   3034:        terminate_server=FALSE;
                   3035: 
                   3036:        do {
                   3037: 
                   3038:                thread_up(FALSE /* setuid */);
                   3039: 
                   3040:                status("Initializing");
                   3041: 
                   3042:                memset(&scfg, 0, sizeof(scfg));
                   3043: 
                   3044:                lprintf(LOG_INFO,"%s Revision %s%s"
                   3045:                        ,server_name
                   3046:                        ,revision
                   3047: #ifdef _DEBUG
                   3048:                        ," Debug"
                   3049: #else
                   3050:                        ,""
                   3051: #endif
                   3052:                        );
                   3053: 
                   3054:                DESCRIBE_COMPILER(compiler);
                   3055: 
                   3056:                lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
                   3057: 
                   3058:                if(!winsock_startup()) {
                   3059:                        cleanup(1);
                   3060:                        return;
                   3061:                }
                   3062: 
                   3063:                t=time(NULL);
                   3064:                lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
                   3065:                        ,CTIME_R(&t,logstr),startup->options);
                   3066: 
                   3067:                lprintf(LOG_DEBUG,"Root HTML directory: %s", root_dir);
                   3068:                lprintf(LOG_DEBUG,"Error HTML directory: %s", error_dir);
                   3069: 
                   3070:                /* Initial configuration and load from CNF files */
                   3071:                SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
                   3072:                lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
                   3073:                scfg.size=sizeof(scfg);
                   3074:                SAFECOPY(logstr,UNKNOWN_LOAD_ERROR);
                   3075:                if(!load_cfg(&scfg, NULL, TRUE, logstr)) {
                   3076:                        lprintf(LOG_ERR,"!ERROR %s",logstr);
                   3077:                        lprintf(LOG_ERR,"!FAILED to load configuration files");
                   3078:                        cleanup(1);
                   3079:                        return;
                   3080:                }
                   3081:                scfg_reloaded=TRUE;
                   3082: 
                   3083:                iniFileName(path,sizeof(path),scfg.ctrl_dir,"mime_types.ini");
                   3084:                if(!read_mime_types(path)) {
                   3085:                        cleanup(1);
                   3086:                        return;
                   3087:                }
                   3088: 
                   3089:                if(startup->host_name[0]==0)
                   3090:                        SAFECOPY(startup->host_name,scfg.sys_inetaddr);
                   3091: 
                   3092:                if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&BBS_OPT_LOCAL_TIMEZONE)) {
                   3093:                        if(putenv("TZ=UTC0"))
                   3094:                                lprintf(LOG_WARNING,"!putenv() FAILED");
                   3095:                        tzset();
                   3096:                }
                   3097: 
                   3098:                if(uptime==0)
                   3099:                        uptime=time(NULL);      /* this must be done *after* setting the timezone */
                   3100: 
                   3101:                active_clients=0;
                   3102:                update_clients();
                   3103: 
                   3104:                /* open a socket and wait for a client */
                   3105: 
                   3106:                server_socket = open_socket(SOCK_STREAM);
                   3107: 
                   3108:                if(server_socket == INVALID_SOCKET) {
                   3109:                        lprintf(LOG_ERR,"!ERROR %d creating HTTP socket", ERROR_VALUE);
                   3110:                        cleanup(1);
                   3111:                        return;
                   3112:                }
                   3113:                
                   3114: /*
                   3115:  *             i=1;
                   3116:  *             if(setsockopt(server_socket, IPPROTO_TCP, TCP_NOPUSH, &i, sizeof(i)))
                   3117:  *                     lprintf("Cannot set TCP_NOPUSH socket option");
                   3118:  */
                   3119: 
                   3120:                lprintf(LOG_INFO,"%04d Web Server socket opened",server_socket);
                   3121: 
                   3122:                /*****************************/
                   3123:                /* Listen for incoming calls */
                   3124:                /*****************************/
                   3125:                memset(&server_addr, 0, sizeof(server_addr));
                   3126: 
                   3127:                server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
                   3128:                server_addr.sin_family = AF_INET;
                   3129:                server_addr.sin_port   = htons(startup->port);
                   3130: 
                   3131:                if(startup->seteuid!=NULL)
                   3132:                        startup->seteuid(FALSE);
                   3133:                result = retry_bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
                   3134:                        ,startup->bind_retry_count,startup->bind_retry_delay,"Web Server",lprintf);
                   3135:                if(startup->seteuid!=NULL)
                   3136:                        startup->seteuid(TRUE);
                   3137:                if(result != 0) {
                   3138:                        lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
                   3139:                        cleanup(1);
                   3140:                        return;
                   3141:                }
                   3142: 
                   3143:                result = listen(server_socket, 64);
                   3144: 
                   3145:                if(result != 0) {
                   3146:                        lprintf(LOG_ERR,"%04d !ERROR %d (%d) listening on socket"
                   3147:                                ,server_socket, result, ERROR_VALUE);
                   3148:                        cleanup(1);
                   3149:                        return;
                   3150:                }
                   3151:                lprintf(LOG_INFO,"%04d Web Server listening on port %d"
                   3152:                        ,server_socket, startup->port);
                   3153:                status("Listening");
                   3154: 
                   3155:                lprintf(LOG_INFO,"%04d Web Server thread started", server_socket);
                   3156: 
                   3157:                listInit(&log_list,/* flags */ 0);
                   3158:                if(startup->options&WEB_OPT_HTTP_LOGGING) {
                   3159:                        /********************/
                   3160:                        /* Start log thread */
                   3161:                        /********************/
                   3162:                        sem_init(&log_sem,0,0);
                   3163:                        pthread_mutex_init(&log_mutex,NULL);
                   3164:                        _beginthread(http_logging_thread, 0, startup->logfile_base);
                   3165:                }
                   3166: 
                   3167:                /* Setup recycle/shutdown semaphore file lists */
                   3168:                semfile_list_init(&shutdown_semfiles,scfg.ctrl_dir,"shutdown","web");
                   3169:                semfile_list_init(&recycle_semfiles,scfg.ctrl_dir,"recycle","web");
                   3170:                SAFEPRINTF(path,"%swebsrvr.rec",scfg.ctrl_dir); /* legacy */
                   3171:                semfile_list_add(&recycle_semfiles,path);
                   3172:                if(!initialized) {
                   3173:                        initialized=time(NULL);
                   3174:                        semfile_list_check(&initialized,&recycle_semfiles);
                   3175:                        semfile_list_check(&initialized,&shutdown_semfiles);
                   3176:                }
                   3177: 
                   3178:                /* signal caller that we've started up successfully */
                   3179:                if(startup->started!=NULL)
                   3180:                startup->started(startup->cbdata);
                   3181: 
                   3182:                while(server_socket!=INVALID_SOCKET && !terminate_server) {
                   3183: 
                   3184:                        /* check for re-cycle/shutdown semaphores */
                   3185:                        if(active_clients==0) {
                   3186:                                if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
                   3187:                                        if((p=semfile_list_check(&initialized,&recycle_semfiles))!=NULL) {
                   3188:                                                lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected"
                   3189:                                                        ,server_socket,p);
                   3190:                                                break;
                   3191:                                        }
                   3192: #if 0  /* unused */
                   3193:                                        if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0)
                   3194:                                                startup->recycle_now=TRUE;
                   3195: #endif
                   3196:                                        if(startup->recycle_now==TRUE) {
                   3197:                                                lprintf(LOG_INFO,"%04d Recycle semaphore signaled",server_socket);
                   3198:                                                startup->recycle_now=FALSE;
                   3199:                                                break;
                   3200:                                        }
                   3201:                                }
                   3202:                                if(((p=semfile_list_check(&initialized,&shutdown_semfiles))!=NULL
                   3203:                                                && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected"
                   3204:                                                        ,server_socket,p))
                   3205:                                        || (startup->shutdown_now==TRUE
                   3206:                                                && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled"
                   3207:                                                        ,server_socket))) {
                   3208:                                        startup->shutdown_now=FALSE;
                   3209:                                        terminate_server=TRUE;
                   3210:                                        break;
                   3211:                                }
                   3212:                        }       
                   3213: 
                   3214:                        /* now wait for connection */
                   3215: 
                   3216:                        FD_ZERO(&socket_set);
                   3217:                        FD_SET(server_socket,&socket_set);
                   3218:                        high_socket_set=server_socket+1;
                   3219: 
                   3220:                        tv.tv_sec=startup->sem_chk_freq;
                   3221:                        tv.tv_usec=0;
                   3222: 
                   3223:                        if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
                   3224:                                if(i==0)
                   3225:                                        continue;
                   3226:                                if(ERROR_VALUE==EINTR)
                   3227:                                        lprintf(LOG_INFO,"Web Server listening interrupted");
                   3228:                                else if(ERROR_VALUE == ENOTSOCK)
                   3229:                        lprintf(LOG_INFO,"Web Server socket closed");
                   3230:                                else
                   3231:                                        lprintf(LOG_WARNING,"!ERROR %d selecting socket",ERROR_VALUE);
                   3232:                                continue;
                   3233:                        }
                   3234: 
                   3235:                        if(server_socket==INVALID_SOCKET)       /* terminated */
                   3236:                                break;
                   3237: 
                   3238:                        client_addr_len = sizeof(client_addr);
                   3239: 
                   3240:                        if(server_socket!=INVALID_SOCKET
                   3241:                                && FD_ISSET(server_socket,&socket_set)) {
                   3242:                                client_socket = accept(server_socket, (struct sockaddr *)&client_addr
                   3243:                                ,&client_addr_len);
                   3244:                        }
                   3245:                        else {
                   3246:                                lprintf(LOG_NOTICE,"!NO SOCKETS set by select");
                   3247:                                continue;
                   3248:                        }
                   3249: 
                   3250:                        if(client_socket == INVALID_SOCKET)     {
                   3251:                                lprintf(LOG_WARNING,"!ERROR %d accepting connection", ERROR_VALUE);
                   3252: #ifdef _WIN32
                   3253:                                if(WSAGetLastError()==WSAENOBUFS)       /* recycle (re-init WinSock) on this error */
                   3254:                                        break;
                   3255: #endif
                   3256:                                continue;
                   3257:                        }
                   3258: 
                   3259:                        if(startup->socket_open!=NULL)
                   3260:                                startup->socket_open(startup->cbdata,TRUE);
                   3261: 
                   3262:                        SAFECOPY(host_ip,inet_ntoa(client_addr.sin_addr));
                   3263: 
                   3264:                        if(trashcan(&scfg,host_ip,"ip-silent")) {
                   3265:                                close_socket(client_socket);
                   3266:                                continue;
                   3267:                        }
                   3268: 
                   3269:                        if(startup->max_clients && active_clients>=startup->max_clients) {
                   3270:                                lprintf(LOG_WARNING,"%04d !MAXMIMUM CLIENTS (%d) reached, access denied"
                   3271:                                        ,client_socket, startup->max_clients);
                   3272:                                mswait(3000);
                   3273:                                close_socket(client_socket);
                   3274:                                continue;
                   3275:                        }
                   3276: 
                   3277:                        host_port=ntohs(client_addr.sin_port);
                   3278: 
                   3279:                        lprintf(LOG_INFO,"%04d HTTP connection accepted from: %s port %u"
                   3280:                                ,client_socket
                   3281:                                ,host_ip, host_port);
                   3282: 
                   3283:                        if((session=malloc(sizeof(http_session_t)))==NULL) {
                   3284:                                lprintf(LOG_CRIT,"%04d !ERROR allocating %u bytes of memory for http_session_t"
                   3285:                                        ,client_socket, sizeof(http_session_t));
                   3286:                                mswait(3000);
                   3287:                                close_socket(client_socket);
                   3288:                                continue;
                   3289:                        }
                   3290: 
                   3291:                        memset(session, 0, sizeof(http_session_t));
                   3292:                        SAFECOPY(session->host_ip,host_ip);
                   3293:                        session->addr=client_addr;
                   3294:                        session->socket=client_socket;
                   3295:                        session->js_branch.auto_terminate=TRUE;
                   3296:                        session->js_branch.terminated=&terminate_server;
                   3297:                        session->js_branch.limit=startup->js_branch_limit;
                   3298:                        session->js_branch.gc_interval=startup->js_gc_interval;
                   3299:                        session->js_branch.yield_interval=startup->js_yield_interval;
                   3300: 
                   3301:                        _beginthread(http_session_thread, 0, session);
                   3302:                        served++;
                   3303:                }
                   3304: 
                   3305:                /* Wait for active clients to terminate */
                   3306:                if(active_clients) {
                   3307:                        lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..."
                   3308:                                ,server_socket, active_clients);
                   3309:                        start=time(NULL);
                   3310:                        while(active_clients) {
                   3311:                                if(time(NULL)-start>startup->max_inactivity) {
                   3312:                                        lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients"
                   3313:                                                ,server_socket, active_clients);
                   3314:                                        break;
                   3315:                                }
                   3316:                                mswait(100);
                   3317:                        }
                   3318:                }
                   3319: 
                   3320:                if(http_logging_thread_running) {
                   3321:                        terminate_http_logging_thread=TRUE;
                   3322:                        sem_post(&log_sem);
                   3323:                        mswait(100);
                   3324:                }
                   3325:                if(http_logging_thread_running) {
                   3326:                        lprintf(LOG_DEBUG,"%04d Waiting for HTTP logging thread to terminate..."
                   3327:                                ,server_socket);
                   3328:                        start=time(NULL);
                   3329:                        while(http_logging_thread_running) {
                   3330:                                if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
                   3331:                                        lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for HTTP logging thread to "
                   3332:                                "terminate", server_socket);
                   3333:                                        break;
                   3334:                                }
                   3335:                                mswait(100);
                   3336:                        }
                   3337:                }
                   3338: 
                   3339:                cleanup(0);
                   3340: 
                   3341:                if(!terminate_server) {
                   3342:                        lprintf(LOG_INFO,"Recycling server...");
                   3343:                        mswait(2000);
                   3344:                        if(startup->recycle!=NULL)
                   3345:                                startup->recycle(startup->cbdata);
                   3346:                }
                   3347: 
                   3348:        } while(!terminate_server);
                   3349: }

unix.superglobalmegacorp.com

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