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