Skip to content
Snippets Groups Projects
websrvr.c 87.8 KiB
Newer Older
		lprintf(LOG_ERR,"%04d !FAILED! execl()",session->socket);
		exit(EXIT_FAILURE); /* Should never happen */
	}
deuce's avatar
deuce committed

	if(child==-1)  {
		lprintf(LOG_ERR,"%04d !FAILED! fork() errno=%d",session->socket,errno);
		close(in_pipe[1]);		/* close write-end of pipe */
		close(out_pipe[0]);		/* close read-end of pipe */
		close(err_pipe[0]);		/* close read-end of pipe */
	}

	close(in_pipe[0]);		/* close excess file descriptor */
	close(out_pipe[1]);		/* close excess file descriptor */
	close(err_pipe[1]);		/* close excess file descriptor */
deuce's avatar
deuce committed
	if(child==-1)
		return(FALSE);

	start=time(NULL);
	post_offset+=write(in_pipe[1],
		session->req.post_data+post_offset,
		session->req.post_len-post_offset);
	high_fd=out_pipe[0];
	if(err_pipe[0]>high_fd)
		high_fd=err_pipe[0];
	if(in_pipe[1]>high_fd)
		high_fd=in_pipe[1];
	if(session->socket>high_fd)
		high_fd=session->socket;
	/* ToDo: Magically set done_parsing_headers for nph-* scripts */
	cgi_status[0]=0;
	while(!done_reading)  {
		tv.tv_sec=startup->max_cgi_inactivity;
		tv.tv_usec=0;
		FD_ZERO(&read_set);
		FD_SET(out_pipe[0],&read_set);
		FD_SET(err_pipe[0],&read_set);
		FD_SET(session->socket,&read_set);
		FD_ZERO(&write_set);
		if(post_offset < session->req.post_len)
			FD_SET(in_pipe[1],&write_set);

		if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0)  {
			if(FD_ISSET(session->socket,&read_set))  {
				if(recv(session->socket,&ch,1,MSG_PEEK) < 1) /* Is there no data waiting? */
					done_reading=TRUE;
			}
			if(FD_ISSET(in_pipe[1],&write_set))  {
				if(post_offset < session->req.post_len)  {
					i=write(in_pipe[1],
						session->req.post_data+post_offset,
						session->req.post_len-post_offset);
					if(post_offset>=session->req.post_len || done_reading)
					else if(i!=post_offset)
			}
			if(FD_ISSET(out_pipe[0],&read_set))  {
				if(done_parsing_headers && got_valid_headers)  {
					i=read(out_pipe[0],buf,sizeof(buf));
						if(session->req.method!=HTTP_HEAD) {
							snt=write(session->socket,buf,i);
							if(session->req.ld!=NULL && snt>0) {
								session->req.ld->size+=snt;
					i=pipereadline(out_pipe[0],buf,sizeof(buf));
					if(i<0)  {
						done_reading=TRUE;
						got_valid_headers=FALSE;
					}
					if(!done_parsing_headers && *buf)  {
						SAFECOPY(header,buf);
						directive=strtok(header,":");
						if(directive != NULL)  {
							value=strtok(NULL,"");
							i=get_header_type(directive);
							switch (i)  {
								case HEAD_LOCATION:
									got_valid_headers=TRUE;
									if(*value=='/')  {
										unescape(value);
										SAFECOPY(session->req.virtual_path,value);
										session->req.send_location=MOVED_STAT;
										if(cgi_status[0]==0)
											SAFECOPY(cgi_status,"302 Moved Temporarily");
									} else  {
										SAFECOPY(session->req.virtual_path,value);
										session->req.send_location=MOVED_STAT;
										if(cgi_status[0]==0)
											SAFECOPY(cgi_status,"302 Moved Temporarily");
									}
									break;
								case HEAD_STATUS:
									SAFECOPY(cgi_status,value);
									break;
								case HEAD_TYPE:
									got_valid_headers=TRUE;
								default:
									session->req.dynamic_heads
										=add_list(session->req.dynamic_heads,buf);
							session->req.dynamic=IS_CGI;
							if(cgi_status[0]==0)
								SAFECOPY(cgi_status,"200 OK");
							send_headers(session,cgi_status);
			}
			if(FD_ISSET(err_pipe[0],&read_set))  {
				i=read(err_pipe[0],buf,sizeof(buf));
		}
		else  {
			if((time(NULL)-start) >= startup->max_cgi_inactivity)  {
				lprintf(LOG_ERR,"%04d CGI Script %s Timed out",session->socket,cmdline);
	/* Drain STDERR */	
	tv.tv_sec=1;
	tv.tv_usec=0;
	FD_ZERO(&read_set);
	FD_SET(err_pipe[0],&read_set);
	if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0)
	if(FD_ISSET(err_pipe[0],&read_set)) {
		while(pipereadline(err_pipe[0],buf,sizeof(buf))!=-1)
			lprintf(LOG_ERR,"%s",buf);
		if(start)
			lprintf(LOG_NOTICE,"%04d CGI Script %s still alive on client exit",session->socket,cmdline);
		kill(child,SIGTERM);
		mswait(1000);
		done_wait = (waitpid(child,&status,WNOHANG)==child);
		if(!done_wait)  {
			kill(child,SIGKILL);
			done_wait = (waitpid(child,&status,0)==child);
	close(in_pipe[1]);		/* close write-end of pipe */
	close(out_pipe[0]);		/* close read-end of pipe */
	close(err_pipe[0]);		/* close read-end of pipe */
	if(!got_valid_headers) {
		lprintf(LOG_ERR,"%04d CGI Script %s did not generate valid headers",session->socket,cmdline);
		return(FALSE);
	}

	if(!done_parsing_headers) {
		lprintf(LOG_ERR,"%04d CGI Script %s did not send data header termination",session->socket,cmdline);
	}

	if(!done_parsing_headers || !got_valid_headers) {
		return(FALSE);
	}
/********************/
/* JavaScript stuff */
/********************/

JSObject* DLLCALL js_CreateHttpReplyObject(JSContext* cx
										   ,JSObject* parent, http_session_t *session)
{
	JSObject*	reply;
	JSObject*	headers;
	jsval		val;
	JSString*	js_str;
	
	/* Return existing object if it's already been created */
	if(JS_GetProperty(cx,parent,"http_reply",&val) && val!=JSVAL_VOID)  {
		reply = JSVAL_TO_OBJECT(val);
		JS_ClearScope(cx,reply);
	}
	else
		reply = JS_DefineObject(cx, parent, "http_reply", NULL
									, NULL, JSPROP_ENUMERATE|JSPROP_READONLY);

	if((js_str=JS_NewStringCopyZ(cx, "200 OK"))==NULL)
		return(FALSE);
	JS_DefineProperty(cx, reply, "status", STRING_TO_JSVAL(js_str)
		,NULL,NULL,JSPROP_ENUMERATE);
	/* Return existing object if it's already been created */
	if(JS_GetProperty(cx,reply,"header",&val) && val!=JSVAL_VOID)  {
		headers = JSVAL_TO_OBJECT(val);
		JS_ClearScope(cx,headers);
	}
	else
		headers = JS_DefineObject(cx, reply, "header", NULL
									, NULL, JSPROP_ENUMERATE|JSPROP_READONLY);

	if((js_str=JS_NewStringCopyZ(cx, "text/html"))==NULL)
		return(FALSE);
	JS_DefineProperty(cx, headers, "Content-Type", STRING_TO_JSVAL(js_str)
		,NULL,NULL,JSPROP_ENUMERATE);

	return(reply);
}

JSObject* DLLCALL js_CreateHttpRequestObject(JSContext* cx
											 ,JSObject* parent, http_session_t *session)
deuce's avatar
deuce committed
{
	JSObject*	request;
	JSObject*	query;
/*	JSObject*	cookie; */
	JSObject*	headers;
deuce's avatar
deuce committed
	JSString*	js_str;
	jsval		val;

	/* Return existing object if it's already been created */
	if(JS_GetProperty(cx,parent,"http_request",&val) && val!=JSVAL_VOID)  {
deuce's avatar
deuce committed
		request = JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
	else
		request = JS_DefineObject(cx, parent, "http_request", NULL
									, NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
	if((js_str=JS_NewStringCopyZ(session->js_cx, methods[session->req.method]))==NULL)
deuce's avatar
deuce committed
		return(FALSE);
	JS_DefineProperty(session->js_cx, request, "method", STRING_TO_JSVAL(js_str)
deuce's avatar
deuce committed
		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

	if((js_str=JS_NewStringCopyZ(session->js_cx, session->req.virtual_path))==NULL)
deuce's avatar
deuce committed
		return(FALSE);
	JS_DefineProperty(session->js_cx, request, "virtual_path", STRING_TO_JSVAL(js_str)
deuce's avatar
deuce committed
		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);

	/* Return existing object if it's already been created */
	if(JS_GetProperty(cx,request,"query",&val) && val!=JSVAL_VOID)  {
deuce's avatar
deuce committed
		query = JSVAL_TO_OBJECT(val);
		JS_ClearScope(cx,query);
	}
deuce's avatar
deuce committed
	else
		query = JS_DefineObject(cx, request, "query", NULL
									, NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
	/* Return existing object if it's already been created */
	if(JS_GetProperty(cx,request,"header",&val) && val!=JSVAL_VOID)  {
		headers = JSVAL_TO_OBJECT(val);
		JS_ClearScope(cx,headers);
	}
	else
		headers = JS_DefineObject(cx, request, "header", NULL
									, NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
	session->js_query=query;
	session->js_header=headers;
	session->js_request=request;
deuce's avatar
deuce committed
	return(request);
}

static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
	char*	warning;
	http_session_t* session;

	if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
		return;
	
	if(report==NULL) {
		lprintf(LOG_ERR,"%04d !JavaScript: %s", session->socket, message);
		if(session->req.fp!=NULL)
			fprintf(session->req.fp,"!JavaScript: %s", message);
		return;
    }

	if(report->filename)
		sprintf(file," %s",report->filename);
	else
		file[0]=0;

	if(report->lineno)
		sprintf(line," line %u",report->lineno);
	else
		line[0]=0;

	if(JSREPORT_IS_WARNING(report->flags)) {
		if(JSREPORT_IS_STRICT(report->flags))
			warning="strict warning";
		else
			warning="warning";
	} else
		warning="";

	lprintf(LOG_ERR,"%04d !JavaScript %s%s%s: %s",session->socket,warning,file,line,message);
	if(session->req.fp!=NULL)
		fprintf(session->req.fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
}

static JSBool
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN		i;
    JSString *	str;
	http_session_t* session;

	if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	if(session->req.fp==NULL)
		return(JS_FALSE);

    for(i=0; i<argc; i++) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			continue;
		fprintf(session->req.fp,"%s",JS_GetStringBytes(str));
	}

	return(JS_TRUE);
}

static JSBool
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN		i;
    JSString *	str;
	http_session_t* session;

	if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	if(session->req.fp==NULL)
		return(JS_FALSE);

    for (i=0; i<argc;i++) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			continue;
		fprintf(session->req.fp,"%s",JS_GetStringBytes(str));
	}

	fprintf(session->req.fp,"\n");

	return(JS_TRUE);
}

static JSFunctionSpec js_global_functions[] = {
	{"write",           js_write,           1},		/* write to HTML file */
	{"writeln",         js_writeln,         1},		/* write line to HTML file */
	{0}
};

static JSContext* 
deuce's avatar
deuce committed
js_initcx(JSRuntime* runtime, SOCKET sock, JSObject** glob, http_session_t *session)
{
	JSContext*	js_cx;
	JSObject*	js_glob;
	BOOL		success=FALSE;

	lprintf(LOG_INFO,"%04d JavaScript: Initializing context (stack: %lu bytes)"
    if((js_cx = JS_NewContext(runtime, startup->js_cx_stack))==NULL)
	lprintf(LOG_INFO,"%04d JavaScript: Context created",sock);

    JS_SetErrorReporter(js_cx, js_ErrorReporter);

	do {

		lprintf(LOG_INFO,"%04d JavaScript: Initializing Global object",sock);
		if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL) 
			break;

		if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions)) 
			break;

		lprintf(LOG_INFO,"%04d JavaScript: Initializing System object",sock);
		if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) 
		if(js_CreateServerObject(js_cx,js_glob,&js_server_props)==NULL)
			break;

		if(glob!=NULL)
			*glob=js_glob;

		success=TRUE;

	} while(0);

	if(!success) {
		JS_DestroyContext(js_cx);
static BOOL js_setup(http_session_t* session)
	if(session->js_runtime == NULL) {
		lprintf(LOG_INFO,"%04d JavaScript: Creating runtime: %lu bytes"
			,session->socket,startup->js_max_bytes);

		if((session->js_runtime=JS_NewRuntime(startup->js_max_bytes))==NULL) {
			lprintf(LOG_ERR,"%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
deuce's avatar
deuce committed
			,&session->js_glob, session))==NULL)) {
			lprintf(LOG_ERR,"%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(LOG_ERR,"%04d !JavaScript ERROR creating user class",session->socket);

		if(js_CreateFileClass(session->js_cx, session->js_glob)==NULL) 
			lprintf(LOG_ERR,"%04d !JavaScript ERROR creating File class",session->socket);
		if(js_CreateSocketClass(session->js_cx, session->js_glob)==NULL)
			lprintf(LOG_ERR,"%04d !JavaScript ERROR creating Socket class",session->socket);

		if(js_CreateMsgBaseClass(session->js_cx, session->js_glob, &scfg)==NULL)
			lprintf(LOG_ERR,"%04d !JavaScript ERROR creating MsgBase class",session->socket);
		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(LOG_INFO,"%04d JavaScript: Initializing HttpRequest object",session->socket);
	if(js_CreateHttpRequestObject(session->js_cx, session->js_glob, session)==NULL) {
		lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript HttpRequest object",session->socket);
		send_error(session,"500 Error initializing JavaScript HttpRequest object");
		return(FALSE);
	}

	lprintf(LOG_INFO,"%04d JavaScript: Initializing HttpReply object",session->socket);
	if(js_CreateHttpReplyObject(session->js_cx, session->js_glob, session)==NULL) {
		lprintf(LOG_ERR,"%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)
	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)
	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);
	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(LOG_ERR,"%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);
		safe_snprintf(str,sizeof(str),"%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)
{
	if(session->req.dynamic==IS_CGI)  {
		if(!exec_cgi(session))  {
			send_error(session,error_500);
			return;
		}
		close_request(session);
		return;
	}
	if(session->req.dynamic==IS_SSJS) {	/* Server-Side JavaScript */
		if(!exec_ssjs(session))  {
			send_error(session,error_500);
		sprintf(session->req.physical_path
			,"%s/SBBS_SSJS.%d.html",startup->cgi_temp_dir,session->socket);
deuce's avatar
deuce committed
	session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
	send_file=send_headers(session,session->req.status);
	if(session->req.method==HTTP_HEAD)
		send_file=FALSE;
		lprintf(LOG_INFO,"%04d Sending file: %s",session->socket, session->req.physical_path);
		snt=sock_sendfile(session->socket,session->req.physical_path);
		if(session->req.ld!=NULL) {
			if(snt<0)
				snt=0;
			session->req.ld->size=snt;
		}
		lprintf(LOG_INFO,"%04d Sent file: %s",session->socket, session->req.physical_path);
	close_request(session);
}

void http_session_thread(void* arg)
{
	char			redir_req[MAX_REQUEST_LINE+1];
	char			*redirp;
	http_session_t	session=*(http_session_t*)arg;	/* copies arg BEFORE it's freed */
deuce's avatar
deuce committed
	FREE_AND_NULL(arg);
	lprintf(LOG_DEBUG,"%04d Session thread started", session.socket);
	thread_up(TRUE /* 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=session.host_ip;

	SAFECOPY(session.host_name,host_name);
	if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP))  {
		lprintf(LOG_INFO,"%04d Hostname: %s", session.socket, host_name);
		for(i=0;host!=NULL && host->h_aliases!=NULL 
			&& host->h_aliases[i]!=NULL;i++)
			lprintf(LOG_INFO,"%04d HostAlias: %s", session.socket, host->h_aliases[i]);
		if(trashcan(&scfg,host_name,"host")) {
			close_socket(session.socket);
			lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", session.socket, host_name);
			thread_down();
			return;
		}
	/* host_ip wasn't defined in http_session_thread */
	if(trashcan(&scfg,session.host_ip,"ip")) {
		lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip);
	active_clients++;
	update_clients();
	SAFECOPY(session.username,unknown);

	SAFECOPY(session.client.addr,session.host_ip);
	SAFECOPY(session.client.host,session.host_name);
	session.client.port=ntohs(session.addr.sin_port);
	session.client.time=time(NULL);
	session.client.protocol="http";
	session.client.user=session.username;
	session.client.size=sizeof(session.client);
	client_on(session.socket, &session.client, TRUE);
	if(session.socket!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,TRUE);
	while(!session.finished && server_socket!=INVALID_SOCKET) {
	    memset(&(session.req), 0, sizeof(session.req));
		SAFECOPY(session.req.status,"200 OK");
		session.req.send_location=NO_LOCATION;
		redirp=NULL;
		loop_count=0;
		while((redirp==NULL || session.req.send_location >= MOVED_TEMP)
				 && !session.finished && server_socket!=INVALID_SOCKET) {
			session.req.send_location=NO_LOCATION;
			session.req.ld=NULL;
			if(startup->options&WEB_OPT_HTTP_LOGGING) {
				if((session.req.ld=(struct log_data*)malloc(sizeof(struct log_data)))==NULL)
					lprintf(LOG_ERR,"%04d Cannot allocate memory for log data!",session.socket);
deuce's avatar
deuce committed
			}
				memset(session.req.ld,0,sizeof(struct log_data));
				session.req.ld->hostname=strdup(session.host_name);
			}
			if(get_req(&session,redirp)) {
				/* At this point, if redirp is non-NULL then the headers have already been parsed */
				if((session.http_ver<HTTP_1_0)||redirp!=NULL||parse_headers(&session)) {
					if(check_request(&session)) {
						if(session.req.send_location < MOVED_TEMP || session.req.virtual_path[0]!='/' || loop_count++ >= MAX_REDIR_LOOPS) {
							safe_snprintf(redir_req,sizeof(redir_req),"%s %s%s%s",methods[session.req.method]
								,session.req.virtual_path,session.http_ver<HTTP_1_0?"":" ",http_vers[session.http_ver]);
							lprintf(LOG_DEBUG,"%04d Internal Redirect to: %s",socket,redir_req);
							redirp=redir_req;
						}
					}
		lprintf(LOG_INFO,"%04d JavaScript: Destroying context",socket);
		JS_DestroyContext(session.js_cx);	/* Free Context */
		lprintf(LOG_INFO,"%04d JavaScript: Destroying runtime",socket);
		JS_DestroyRuntime(session.js_runtime);
	}
	client_off(session.socket);
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,FALSE);
	lprintf(LOG_INFO,"%04d Session thread terminated (%u clients, %u threads remain, %lu served)"
		,socket, active_clients, thread_count, served);
   	lprintf(LOG_INFO,"%04d Web Server terminate",server_socket);
	terminate_server=TRUE;
}

static void cleanup(int code)
{
	free_cfg(&scfg);
	mime_types=iniFreeNamedStringList(mime_types);

	semfile_list_free(&recycle_semfiles);
	semfile_list_free(&shutdown_semfiles);

	if(server_socket!=INVALID_SOCKET) {
		close_socket(server_socket);
		server_socket=INVALID_SOCKET;
	}

	update_clients();

#ifdef _WINSOCKAPI_
	if(WSAInitialized && WSACleanup()!=0) 
		lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
#endif

	thread_down();
	status("Down");
	if(terminate_server || code)
		lprintf(LOG_INFO,"#### Web Server thread terminated (%u threads remain, %lu clients served)"
			,thread_count, served);
	if(startup!=NULL && startup->terminated!=NULL)
		startup->terminated(startup->cbdata,code);
}

const char* DLLCALL web_ver(void)
{
	static char ver[256];
	char compiler[32];

	DESCRIBE_COMPILER(compiler);

	sscanf("$Revision$", "%*s %s", revision);

	sprintf(ver,"%s %s%s  "
		"Compiled %s %s with %s"
		,server_name
		,revision
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		,__DATE__, __TIME__, compiler);

	return(ver);
}

	char	base[MAX_PATH+1];
	char	filename[MAX_PATH+1];
	char	newfilename[MAX_PATH+1];
	http_logging_thread_running=TRUE;
	terminate_http_logging_thread=FALSE;

		SAFEPRINTF(base,"%slogs/http-",scfg.logs_dir);
	filename[0]=0;
	newfilename[0]=0;

	thread_up(TRUE /* setuid */);

	lprintf(LOG_INFO,"0000 http logging thread started");
	for(;!terminate_http_logging_thread;) {
		char	timestr[128];
		if(terminate_http_logging_thread)
			break;
		ld=listRemoveNode(&log_list, FIRST_NODE);
			lprintf(LOG_ERR,"0000 http logging thread received NULL linked list log entry");
		if(ld==NULL)
			continue;
		strftime(strchr(newfilename,0),15,"%Y-%m-%d.log",&ld->completed);
		if(strcmp(newfilename,filename)) {
deuce's avatar
deuce committed
			if(logfile!=NULL)
				fclose(logfile);
			SAFECOPY(filename,newfilename);
			logfile=fopen(filename,"ab");
			lprintf(LOG_INFO,"0000 http logfile is now: %s",filename);
		}
		if(logfile!=NULL) {
			sprintf(sizestr,"%d",ld->size);
			strftime(timestr,sizeof(timestr),"%d/%b/%Y:%H:%M:%S %z",&ld->completed);
			while(lock(fileno(logfile),0,1) && !terminate_http_logging_thread) {
			fprintf(logfile,"%s %s %s [%s] \"%s\" %d %s \"%s\" \"%s\"\n"
					,ld->hostname?(ld->hostname[0]?ld->hostname:"-"):"-"
					,ld->ident?(ld->ident[0]?ld->ident:"-"):"-"
					,ld->user?(ld->user[0]?ld->user:"-"):"-"
					,timestr
					,ld->request?(ld->request[0]?ld->request:"-"):"-"
					,ld->status
					,ld->size?sizestr:"-"
					,ld->referrer?(ld->referrer[0]?ld->referrer:"-"):"-"
					,ld->agent?(ld->agent[0]?ld->agent:"-"):"-");
			fflush(logfile);
			unlock(fileno(logfile),0,1);
deuce's avatar
deuce committed
			FREE_AND_NULL(ld->hostname);
			FREE_AND_NULL(ld->ident);
			FREE_AND_NULL(ld->user);
			FREE_AND_NULL(ld->request);
			FREE_AND_NULL(ld->referrer);
			FREE_AND_NULL(ld->agent);
			FREE_AND_NULL(ld);
			lprintf(LOG_ERR,"0000 http logfile %s was not open!",filename);
deuce's avatar
deuce committed
	if(logfile!=NULL) {
deuce's avatar
deuce committed
		logfile=NULL;
	}
	lprintf(LOG_INFO,"0000 http logging thread terminated");

	http_logging_thread_running=FALSE;
void DLLCALL web_server(void* arg)
{
	int				i;
	int				result;
	time_t			start;
	WORD			host_port;
	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;

rswindell's avatar
rswindell committed
	web_ver();	/* get CVS revision */

    if(startup==NULL) {
    	sbbs_beep(100,500);
    	fprintf(stderr, "No startup structure passed!\n");
    	return;
    }

rswindell's avatar
rswindell committed
	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;
	}

#ifdef _THREAD_SUID_BROKEN
	startup->seteuid(TRUE);
#endif

	/* Setup intelligent defaults */
	if(startup->port==0)					startup->port=IPPORT_HTTP;
	if(startup->root_dir[0]==0)				SAFECOPY(startup->root_dir,WEB_DEFAULT_ROOT_DIR);
	if(startup->error_dir[0]==0)			SAFECOPY(startup->error_dir,WEB_DEFAULT_ERROR_DIR);
	if(startup->cgi_dir[0]==0)				SAFECOPY(startup->error_dir,WEB_DEFAULT_CGI_DIR);
	if(startup->max_inactivity==0) 			startup->max_inactivity=120; /* seconds */
	if(startup->sem_chk_freq==0)			startup->sem_chk_freq=2; /* seconds */
	if(startup->js_max_bytes==0)			startup->js_max_bytes=JAVASCRIPT_MAX_BYTES;
	if(startup->js_cx_stack==0)				startup->js_cx_stack=JAVASCRIPT_CONTEXT_STACK;
	if(startup->ssjs_ext[0]==0)				SAFECOPY(startup->ssjs_ext,"ssjs");
	sprintf(js_server_props.version,"%s %s",server_name,revision);
	js_server_props.version_detail=web_ver();
	js_server_props.clients=&active_clients;
	js_server_props.options=&startup->options;
	js_server_props.interface_addr=&startup->interface_addr;

	/* Copy html directories */
	SAFECOPY(root_dir,startup->root_dir);
	SAFECOPY(error_dir,startup->error_dir);
	SAFECOPY(cgi_dir,startup->cgi_dir);
	prep_dir(startup->ctrl_dir, root_dir, sizeof(root_dir));
	prep_dir(root_dir, error_dir, sizeof(error_dir));

	/* Trim off trailing slash/backslash */
	if(IS_PATH_DELIM(*(p=lastchar(root_dir))))	*p=0;
	startup->recycle_now=FALSE;
	do {

		thread_up(FALSE /* setuid */);

		status("Initializing");

		memset(&scfg, 0, sizeof(scfg));

rswindell's avatar
rswindell committed
			,server_name
			,revision
#ifdef _DEBUG
			," Debug"
#else