Newer
Older
,sock,JAVASCRIPT_CONTEXT_STACK);
if((js_cx = JS_NewContext(runtime, JAVASCRIPT_CONTEXT_STACK))==NULL)
return(NULL);
lprintf("%04d JavaScript: Context created",sock);
JS_SetErrorReporter(js_cx, js_ErrorReporter);
do {
lprintf("%04d JavaScript: Initializing Global object",sock);
if((js_glob=js_CreateGlobalObject(js_cx, &scfg))==NULL)
break;
if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions))
break;
lprintf("%04d JavaScript: Initializing System object",sock);
if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name)==NULL)
break;
if((server=JS_DefineObject(js_cx, js_glob, "server", NULL,NULL,0))==NULL)
break;
sprintf(ver,"%s %s",server_name,revision);
if((js_str=JS_NewStringCopyZ(js_cx, ver))==NULL)
break;
val = STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, server, "version", &val))
break;
if((js_str=JS_NewStringCopyZ(js_cx, web_ver()))==NULL)
break;
val = STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, server, "version_detail", &val))
break;
if(glob!=NULL)
*glob=js_glob;
success=TRUE;
} while(0);
if(!success) {
JS_DestroyContext(js_cx);
session->js_cx=NULL;
return(NULL);
}
return(js_cx);
}
static BOOL js_setup(http_session_t* session)
if(session->js_runtime == NULL) {
lprintf("%04d JavaScript: Creating runtime: %lu bytes"
,session->socket,startup->js_max_bytes);
if((session->js_runtime=JS_NewRuntime(startup->js_max_bytes))==NULL) {
lprintf("%04d !ERROR creating JavaScript runtime",session->socket);
send_error(session,"500 Error creating JavaScript runtime");
return(FALSE);
}
}
if(session->js_cx==NULL) { /* Context not yet created, create it now */
if(((session->js_cx=js_initcx(session->js_runtime, session->socket
lprintf("%04d !ERROR initializing JavaScript context",session->socket);
send_error(session,"500 Error initializing JavaScript context");
return(FALSE);
}
if(js_CreateUserClass(session->js_cx, session->js_glob, &scfg)==NULL)
lprintf("%04d !JavaScript ERROR creating user class",session->socket);
if(js_CreateFileClass(session->js_cx, session->js_glob)==NULL)
lprintf("%04d !JavaScript ERROR creating File class",session->socket);
if(js_CreateSocketClass(session->js_cx, session->js_glob)==NULL)
lprintf("%04d !JavaScript ERROR creating Socket class",session->socket);
if(js_CreateMsgBaseClass(session->js_cx, session->js_glob, &scfg)==NULL)
lprintf("%04d !JavaScript ERROR creating MsgBase class",session->socket);
if(js_CreateClientObject(session->js_cx, session->js_glob, "client", &client
,session->socket)==NULL)
lprintf("%04d !JavaScript ERROR creating client object",session->socket);
#endif
argv=JS_NewArrayObject(session->js_cx, 0, NULL);
JS_DefineProperty(session->js_cx, session->js_glob, "argv", OBJECT_TO_JSVAL(argv)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
JS_DefineProperty(session->js_cx, session->js_glob, "argc", INT_TO_JSVAL(0)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
}
lprintf(" JavaScript: Initializing HttpRequest object");
if(js_CreateHttpRequestObject(session->js_cx, session->js_glob, session)==NULL) {
lprintf("%04d !ERROR initializing JavaScript HttpRequest object",session->socket);
send_error(session,"500 Error initializing JavaScript HttpRequest object");
return(FALSE);
}
lprintf(" JavaScript: Initializing HttpReply object");
if(js_CreateHttpReplyObject(session->js_cx, session->js_glob, session)==NULL) {
lprintf("%04d !ERROR initializing JavaScript HttpReply object",session->socket);
send_error(session,"500 Error initializing JavaScript HttpReply object");
return(FALSE);
}
JS_SetContextPrivate(session->js_cx, session);
return(TRUE);
}
static BOOL exec_ssjs(http_session_t* session) {
JSString* js_str;
JSScript* js_script;
jsval rval;
jsval val;
JSObject* reply;
JSIdArray* heads;
JSObject* headers;
char path[MAX_PATH+1];
char str[MAX_REQUEST_LINE+1];
int i;
if((js_str=JS_NewStringCopyZ(session->js_cx, session->req.physical_path))==NULL)
return(FALSE);
JS_DefineProperty(session->js_cx, session->js_request, "real_path", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
if((js_str=JS_NewStringCopyZ(session->js_cx, session->req.ars))==NULL)
return(FALSE);
JS_DefineProperty(session->js_cx, session->js_request, "ars", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
if((js_str=JS_NewStringCopyZ(session->js_cx, session->req.host))==NULL)
return(FALSE);
JS_DefineProperty(session->js_cx, session->js_request, "host", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
if((js_str=JS_NewStringCopyZ(session->js_cx, http_vers[session->http_ver]))==NULL)
return(FALSE);
JS_DefineProperty(session->js_cx, session->js_request, "http_ver", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
if((js_str=JS_NewStringCopyZ(session->js_cx, session->host_ip))==NULL)
return(FALSE);
JS_DefineProperty(session->js_cx, session->js_request, "remote_ip", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
if((js_str=JS_NewStringCopyZ(session->js_cx, session->host_name))==NULL)
return(FALSE);
JS_DefineProperty(session->js_cx, session->js_request, "remote_host", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
do {
/* RUN SCRIPT */
JS_ClearPendingException(session->js_cx);
if((js_script=JS_CompileFile(session->js_cx, session->js_glob
,session->req.physical_path))==NULL) {
lprintf("%04d !JavaScript FAILED to compile script (%s)"
,session->socket,session->req.physical_path);
return(FALSE);
}
JS_ExecuteScript(session->js_cx, session->js_glob, js_script, &rval);
} while(0);
/* Read http_reply object */
JS_GetProperty(session->js_cx,session->js_glob,"http_reply",&val);
reply = JSVAL_TO_OBJECT(val);
JS_GetProperty(session->js_cx,reply,"status",&val);
SAFECOPY(session->req.status,JS_GetStringBytes(JSVAL_TO_STRING(val)));
JS_GetProperty(session->js_cx,reply,"header",&val);
headers = JSVAL_TO_OBJECT(val);
heads=JS_Enumerate(session->js_cx,headers);
for(i=0;i<heads->length;i++) {
JS_IdToValue(session->js_cx,heads->vector[i],&val);
js_str=JSVAL_TO_STRING(val);
JS_GetProperty(session->js_cx,headers,JS_GetStringBytes(js_str),&val);
snprintf(str,MAX_REQUEST_LINE+1,"%s: %s",JS_GetStringBytes(js_str),JS_GetStringBytes(JSVAL_TO_STRING(val)));
session->req.dynamic_heads=add_list(session->req.dynamic_heads,str);
}
JS_DestroyIdArray(session->js_cx, heads);
/* Free up temporary resources here */
if(js_script!=NULL)
JS_DestroyScript(session->js_cx, js_script);
if(session->req.fp!=NULL)
fclose(session->req.fp);
SAFECOPY(session->req.physical_path, path);
session->req.dynamic=IS_SSJS;
return(TRUE);
}
static void respond(http_session_t * session)
{
BOOL send_file=TRUE;
if(session->req.dynamic==IS_CGI) {
if(!exec_cgi(session)) {
send_error(session,"500 Internal Server Error");
return;
}
close_request(session);
return;
}
if(session->req.dynamic==IS_SSJS) { /* Server-Side JavaScript */
if(!exec_ssjs(session)) {
send_error(session,"500 Internal Server Error");
return;
}
sprintf(session->req.physical_path,"%s/SBBS_SSJS.%d.html",startup->cgi_temp_dir,session->socket);
}
if(session->http_ver > HTTP_0_9) {
session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
send_file=send_headers(session,session->req.status);
if(send_file) {
lprintf("%04d Sending file",session->socket);
sock_sendfile(session->socket,session->req.physical_path);
lprintf("%04d Sent file",session->socket);
}
close_request(session);
}
void http_session_thread(void* arg)
{
char* host_name;
HOSTENT* host;
SOCKET socket;
http_session_t session=*(http_session_t*)arg; /* copies arg BEFORE it's freed */
free(arg);
socket=session.socket;
lprintf("%04d Session thread started", session.socket);
thread_up(FALSE /* setuid */);
session.finished=FALSE;
if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
host=NULL;
else
host=gethostbyaddr ((char *)&session.addr.sin_addr
,sizeof(session.addr.sin_addr),AF_INET);
if(host!=NULL && host->h_name!=NULL)
host_name=host->h_name;
else
host_name="<no name>";
if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP)) {
lprintf("%04d Hostname: %s", session.socket, host_name);
SAFECOPY(session.host_name,host_name);
for(i=0;host!=NULL && host->h_aliases!=NULL
&& host->h_aliases[i]!=NULL;i++)
lprintf("%04d HostAlias: %s", session.socket, host->h_aliases[i]);
if(trashcan(&scfg,host_name,"host")) {
close_socket(session.socket);
lprintf("%04d !CLIENT BLOCKED in host.can: %s", session.socket, host_name);
thread_down();
lprintf("%04d Free()ing session",socket);
return;
}
/* host_ip wasn't defined in http_session_thread */
if(trashcan(&scfg,session.host_ip,"ip")) {
close_socket(session.socket);
lprintf("%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip);
thread_down();
lprintf("%04d Free()ing session",socket);
return;
}
active_clients++;
update_clients();
while(!session.finished && server_socket!=INVALID_SOCKET) {
memset(&(session.req), 0, sizeof(session.req));
SAFECOPY(session.req.status,"200 OK");
if(get_req(&session)) {
if((session.http_ver<HTTP_1_0)||parse_headers(&session)) {
if(check_request(&session)) {
respond(&session);
}
}
}
}
if(session.js_cx!=NULL) {
lprintf("%04d JavaScript: Destroying context",socket);
JS_DestroyContext(session.js_cx); /* Free Context */
session.js_cx=NULL;
}
if(session.js_runtime!=NULL) {
lprintf("%04d JavaScript: Destroying runtime",socket);
JS_DestroyRuntime(session.js_runtime);
}
active_clients--;
update_clients();
/* client_off(session.socket); */
thread_down();
lprintf("%04d Session thread terminated (%u clients, %u threads remain, %lu served)"
,socket, active_clients, thread_count, served);
}
void DLLCALL web_terminate(void)
{
recycle_server=FALSE;
if(server_socket!=INVALID_SOCKET) {
lprintf("%04d Web Terminate: closing socket",server_socket);
close_socket(server_socket);
server_socket=INVALID_SOCKET;
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
}
}
static void cleanup(int code)
{
free_cfg(&scfg);
if(server_socket!=INVALID_SOCKET) {
close_socket(server_socket);
server_socket=INVALID_SOCKET;
}
update_clients();
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
#endif
thread_down();
status("Down");
lprintf("#### Web Server thread terminated (%u threads remain, %lu clients served)"
,thread_count, served);
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(code);
}
const char* DLLCALL web_ver(void)
{
static char ver[256];
char compiler[32];
DESCRIBE_COMPILER(compiler);
sscanf("$Revision$", "%*s %s", revision);
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
sprintf(ver,"%s %s%s "
"Compiled %s %s with %s"
,server_name
,revision
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,__DATE__, __TIME__, compiler);
return(ver);
}
void DLLCALL web_server(void* arg)
{
int i;
int result;
time_t start;
WORD host_port;
char host_ip[32];
char path[MAX_PATH+1];
char logstr[256];
SOCKADDR_IN server_addr={0};
SOCKADDR_IN client_addr;
socklen_t client_addr_len;
SOCKET client_socket;
SOCKET high_socket_set;
fd_set socket_set;
time_t t;
time_t initialized=0;
char compiler[32];
http_session_t * session;
struct timeval tv;
startup=(web_startup_t*)arg;
if(startup==NULL) {
sbbs_beep(100,500);
fprintf(stderr, "No startup structure passed!\n");
return;
}
if(startup->size!=sizeof(web_startup_t)) { /* verify size */
sbbs_beep(100,500);
sbbs_beep(300,500);
sbbs_beep(100,500);
fprintf(stderr, "Invalid startup structure!\n");
return;
}
/* Setup intelligent defaults */
if(startup->port==0) startup->port=IPPORT_HTTP;
if(startup->index_file_name[0][0]==0) SAFECOPY(startup->index_file_name[0],"index.html");
if(startup->root_dir[0]==0) SAFECOPY(startup->root_dir,"../html");
if(startup->error_dir[0]==0) SAFECOPY(startup->error_dir,"../html/error");
if(startup->max_inactivity==0) startup->max_inactivity=120; /* seconds */
if(startup->js_max_bytes==0) startup->js_max_bytes=JAVASCRIPT_MAX_BYTES;
if(startup->cgi_ext[0][0]==0) SAFECOPY(startup->cgi_ext[0],"cgi");
if(startup->ssjs_ext[0]==0) SAFECOPY(startup->ssjs_ext,"ssjs");
/* Copy html directories */
SAFECOPY(root_dir,startup->root_dir);
SAFECOPY(error_dir,startup->error_dir);
/* Change to absolute path */
prep_dir(startup->ctrl_dir, root_dir, sizeof(root_dir));
prep_dir(startup->ctrl_dir, error_dir, sizeof(error_dir));
/* Trim off trailing slash/backslash */
if(*(p=lastchar(root_dir))==BACKSLASH) *p=0;
if(*(p=lastchar(error_dir))==BACKSLASH) *p=0;
uptime=0;
served=0;
startup->recycle_now=FALSE;
recycle_server=TRUE;
do {
thread_up(FALSE /* setuid */);
status("Initializing");
memset(&scfg, 0, sizeof(scfg));
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
,revision
#ifdef _DEBUG
," Debug"
#else
,""
#endif
);
DESCRIBE_COMPILER(compiler);
lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);
srand(time(NULL)); /* Seed random number generator */
sbbs_random(10); /* Throw away first number */
if(!winsock_startup()) {
cleanup(1);
return;
}
t=time(NULL);
lprintf("Initializing on %.24s with options: %lx"
,CTIME_R(&t,logstr),startup->options);
lprintf("Root HTML directory: %s", root_dir);
lprintf("Error HTML directory: %s", error_dir);
/* Initial configuration and load from CNF files */
SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
lprintf("Loading configuration files from %s", scfg.ctrl_dir);
scfg.size=sizeof(scfg);
SAFECOPY(logstr,UNKNOWN_LOAD_ERROR);
if(!load_cfg(&scfg, NULL, TRUE, logstr)) {
lprintf("!ERROR %s",logstr);
lprintf("!FAILED to load configuration files");
cleanup(1);
return;
}
scfg_reloaded=TRUE;
sprintf(path,"%smime_types.cfg",scfg.ctrl_dir);
if(!read_mime_types(path)) {
lprintf("!ERROR %d reading %s", errno,path);
cleanup(1);
return;
}
if(startup->host_name[0]==0)
SAFECOPY(startup->host_name,scfg.sys_inetaddr);
if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&BBS_OPT_LOCAL_TIMEZONE)) {
if(putenv("TZ=UTC0"))
lprintf("!putenv() FAILED");
tzset();
}
if(uptime==0)
uptime=time(NULL); /* this must be done *after* setting the timezone */
active_clients=0;
update_clients();
/* open a socket and wait for a client */
server_socket = open_socket(SOCK_STREAM);
if(server_socket == INVALID_SOCKET) {
lprintf("!ERROR %d creating HTTP socket", ERROR_VALUE);
cleanup(1);
return;
}
lprintf("Web Server socket %d opened",server_socket);
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
/*****************************/
/* Listen for incoming calls */
/*****************************/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(startup->port);
if(startup->seteuid!=NULL)
startup->seteuid(FALSE);
result = bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr));
if(startup->seteuid!=NULL)
startup->seteuid(TRUE);
if(result != 0) {
lprintf("!ERROR %d (%d) binding HTTP socket to port %d"
,result, ERROR_VALUE,startup->port);
lprintf(BIND_FAILURE_HELP);
cleanup(1);
return;
}
result = listen(server_socket, 64);
if(result != 0) {
lprintf("!ERROR %d (%d) listening on HTTP socket", result, ERROR_VALUE);
cleanup(1);
return;
}
lprintf("Web Server listening on port %d",startup->port);
status("Listening");
/* signal caller that we've started up successfully */
if(startup->started!=NULL)
startup->started();
lprintf("Web Server thread started");
sprintf(path,"%swebsrvr.rec",scfg.ctrl_dir);
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
while(server_socket!=INVALID_SOCKET) {
/* check for re-cycle semaphores */
if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
t=fdate(path);
if(!active_clients && t!=-1 && t>initialized) {
lprintf("0000 Recycle semaphore file (%s) detected",path);
initialized=t;
break;
}
if(!active_clients && startup->recycle_now==TRUE) {
lprintf("0000 Recycle semaphore signaled");
startup->recycle_now=FALSE;
break;
}
}
/* now wait for connection */
FD_ZERO(&socket_set);
FD_SET(server_socket,&socket_set);
high_socket_set=server_socket+1;
tv.tv_sec=2;
tv.tv_usec=0;
if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
if(i==0) {
mswait(1);
continue;
}
if(ERROR_VALUE==EINTR)
lprintf("Web Server listening interrupted");
else if(ERROR_VALUE == ENOTSOCK)
lprintf("Web Server socket closed");
else
lprintf("!ERROR %d selecting socket",ERROR_VALUE);
break;
}
if(server_socket==INVALID_SOCKET) /* terminated */
break;
client_addr_len = sizeof(client_addr);
if(server_socket!=INVALID_SOCKET
&& FD_ISSET(server_socket,&socket_set)) {
client_socket = accept(server_socket, (struct sockaddr *)&client_addr
,&client_addr_len);
else {
lprintf("!NO SOCKETS set by select");
continue;
}
if(client_socket == INVALID_SOCKET) {
if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR || ERROR_VALUE == EINVAL) {
lprintf("Web Server socket closed");
break;
}
lprintf("!ERROR %d accepting connection", ERROR_VALUE);
SAFECOPY(host_ip,inet_ntoa(client_addr.sin_addr));
if(trashcan(&scfg,host_ip,"ip-silent")) {
close_socket(client_socket);
continue;
}
host_port=ntohs(client_addr.sin_port);
lprintf("%04d HTTP connection accepted from: %s port %u"
,client_socket
,host_ip, host_port);
if(startup->socket_open!=NULL)
startup->socket_open(TRUE);
if((session=malloc(sizeof(http_session_t)))==NULL) {
lprintf("%04d !ERROR allocating %u bytes of memory for service_client"
,client_socket, sizeof(http_session_t));
mswait(3000);
close_socket(client_socket);
continue;
}
memset(session, 0, sizeof(http_session_t));
SAFECOPY(session->host_ip,host_ip);
session->addr=client_addr;
session->socket=client_socket;
_beginthread(http_session_thread, 0, session);
served++;
lprintf("Closing Server Socket %d", server_socket);
close_socket(server_socket);
server_socket=INVALID_SOCKET;
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
if(http_threads_running) {
lprintf("Waiting for %d connection threads to terminate...", http_threads_running);
start=time(NULL);
while(http_threads_running) {
if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
lprintf("!TIMEOUT waiting for %d http thread(s) to "
"terminate", http_threads_running);
break;
}
mswait(100);
}
}
cleanup(0);
if(recycle_server) {
lprintf("Recycling server...");
mswait(2000);
}
} while(recycle_server);
}