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