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