|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.