Skip to content
Snippets Groups Projects
websrvr.c 65.3 KiB
Newer Older
	dst=p;
	for(;*p;p++) {
		if(*p=='%' && isxdigit(*(p+1)) && isxdigit(*(p+2))) {
			sprintf(code,"%.2s",p+1);
			*(dst++)=(char)strtol(code,NULL,16);
			p+=2;
		}
		else  {
			if(*p=='+')  {
				*(dst++)=' ';
			}
			else  {
				*(dst++)=*p;
			}
		}
	}
	*(dst)=0;
}

static void js_parse_post(http_session_t * session)  {
	char		*p;
	char		*key;
	char		*value;
	JSString*	js_str;

	if(session->req.post_data != NULL)  {
		p=session->req.post_data;
		while((key=strtok(p,"="))!=NULL)  {
			p=NULL;
			if(key != NULL)  {
				value=strtok(NULL,"&");
				if(value != NULL)  {
					unescape(value);
					unescape(key);
					lprintf("Setting %s to %s",key,value);
					if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL)
						return;
					JS_DefineProperty(session->js_cx, session->js_query, key, STRING_TO_JSVAL(js_str)
						,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
				}
			}
		}
	}
}

static void js_add_header(http_session_t * session, char *key, char *value)  {
	JSString*	js_str;

	if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL)
		return;
	JS_DefineProperty(session->js_cx, session->js_header, key, STRING_TO_JSVAL(js_str)
		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
}

static BOOL parse_headers(http_session_t * session)
{
	char	req_line[MAX_REQUEST_LINE];
	char	next_char[2];
	char	*value;
	char	*p;
	int		i;
	char	env_name[128];
	while(sockreadline(session,req_line,sizeof(req_line))>0) {
		while((recvfrom(session->socket,next_char,1,MSG_PEEK,NULL,0)>0) 
			&& (next_char[0]=='\t' || next_char[0]==' ')) {
			sockreadline(session,req_line+i,sizeof(req_line)-i);
		if((value=strtok(NULL,""))!=NULL) {
			i=get_header_type(req_line);
			while(*value && *value<=' ') value++;
			if(session->req.dynamic==IS_SSJS)
				js_add_header(session,req_line,value);
			switch(i) {
				case HEAD_AUTH:
					strtok(value," ");
					p=strtok(NULL," ");
					if(p==NULL)
						break;
					while(*p && *p<' ') p++;
					b64_decode(p);
					SAFECOPY(session->req.auth,p);
					break;
				case HEAD_LENGTH:
					if(session->req.dynamic==IS_CGI)				
						add_env(session,"CONTENT_LENGTH",value);
					content_len=atoi(value);
					if(session->req.dynamic==IS_CGI)				
						add_env(session,"CONTENT_TYPE",value);
					break;
				case HEAD_IFMODIFIED:
					session->req.if_modified_since=decode_date(value);
					break;
				case HEAD_CONNECTION:
					if(!stricmp(value,"Keep-Alive")) {
						session->req.keep_alive=TRUE;
					}
					if(!stricmp(value,"Close")) {
						session->req.keep_alive=FALSE;
					}
					if(session->req.host[0]==0) {
						SAFECOPY(session->req.host,value);
						if(startup->options&WEB_OPT_DEBUG_RX)
							lprintf("%04d Grabbing from virtual host: %s"
								,session->socket,value);
			if(session->req.dynamic==IS_CGI)  {
				sprintf(env_name,"HTTP_%s",req_line);
				add_env(session,env_name,value);
			}
		if((session->req.post_data=malloc(content_len+1)) != NULL)  {
			recvbufsocket(session->socket,session->req.post_data,content_len);
			if(session->req.dynamic==IS_SSJS)  {
				js_parse_post(session);
			}
		}
		else  {
			lprintf("%04d !ERROR Allocating %d bytes of memory",session->socket,content_len);
			return(FALSE);
	if(session->req.dynamic==IS_CGI)				
		add_env(session,"SERVER_NAME",session->req.host[0] ? session->req.host : startup->host_name );
	return TRUE;
}

static int get_version(char *p)
{
	int		i;
	if(p==NULL)
		return(0);
	while(*p && *p<' ') p++;
	if(*p==0)
		return(0);
	for(i=1;http_vers[i]!=NULL;i++) {
		if(!stricmp(p,http_vers[i])) {
			return(i);
		}
	}
	return(i-1);
}

static void js_parse_query(http_session_t * session, char *p)  {
	char	*key;
	char	*value;
	JSString*	js_str;
	
	while((key=strtok(p,"="))!=NULL)  {
		p=NULL;
		if(key != NULL)  {
			value=strtok(NULL,"&");
			if(value != NULL)  {
				unescape(value);
				unescape(key);
				lprintf("Setting %s to %s",key,value);
				if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL)
					return;
				JS_DefineProperty(session->js_cx, session->js_query, key, STRING_TO_JSVAL(js_str)
					,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
			}
		}
	}
}

static int is_dynamic(http_session_t * session,char *req)  {
	char*	p;
	int		i=0;
	char	path[MAX_PATH+1];

	if((p=strrchr(req,'.'))==NULL)  {
		lprintf("No dot... is static");
		return(IS_STATIC);
	}

	if(!(startup->options&BBS_OPT_NO_JAVASCRIPT) && stricmp(p,startup->ssjs_ext)==0)  {
		lprintf("Setting up JavaScript support");
	
		if(!js_setup(session)) {
			lprintf("%04d !ERROR setting up JavaScript support", session->socket);
			send_error(session,"500 Internal Server Error");
			return(IS_STATIC);
		}
	
		sprintf(path,"%s/SBBS_SSJS.%d.html",startup->cgi_temp_dir,session->socket);
		if((session->req.fp=fopen(path,"w"))==NULL) {
			lprintf("%04d !ERROR %d opening/creating %s", session->socket, errno, path);
			send_error(session,"500 Internal Server Error");
			return(IS_STATIC);
		}
		lprintf("is ssjs");
		return(IS_SSJS);
	}

	if(!(startup->options&BBS_OPT_NO_JAVASCRIPT) && stricmp(p,startup->js_ext)==0)
		return(IS_JS);

	if(!(startup->options&WEB_OPT_NO_CGI)) {
		for(i=0;i<10&&startup->cgi_ext[i][0];i++)  {
			if(!(startup->options&BBS_OPT_NO_JAVASCRIPT) && stricmp(p,startup->cgi_ext[i])==0)  {
				lprintf("is ssjs");
				init_enviro(session);
				return(IS_CGI);
			}
		}
	}

	return(IS_STATIC);
}

static char *get_request(http_session_t * session, char *req_line)
{
	char *	p;
	char *  retval;
	int		offset;

	while(*req_line && *req_line<' ') req_line++;
	SAFECOPY(session->req.virtual_path,req_line);
	strtok(session->req.virtual_path," \t");
	retval=strtok(NULL," \t");
	strtok(session->req.virtual_path,"?");
	p=strtok(NULL,"");
	session->req.dynamic=is_dynamic(session,session->req.virtual_path);
deuce's avatar
deuce committed
	SAFECOPY(session->req.query_str,p);
	if(p!=NULL)  {
		switch(session->req.dynamic) {
			case IS_CGI:
				add_env(session,"QUERY_STRING",p);
				break;
			case IS_SSJS:
				js_parse_query(session,p);
				break;
		}
	}
	
	unescape(session->req.virtual_path);
	SAFECOPY(session->req.physical_path,session->req.virtual_path);
	if(!strnicmp(session->req.physical_path,http_scheme,http_scheme_len)) {
		/* Set HOST value... ignore HOST header */
		SAFECOPY(session->req.host,session->req.physical_path+http_scheme_len);
		strtok(session->req.physical_path,"/");
		p=strtok(NULL,"/");
		if(p==NULL) {
			/* Do not allow host values larger than 128 bytes */
			session->req.host[0]=0;
			p=session->req.physical_path+http_scheme_len;
		offset=p-session->req.physical_path;
		memmove(session->req.physical_path
			,session->req.physical_path+offset
			,strlen(session->req.physical_path+offset)+1	/* move '\0' terminator too */
			);
static char *get_method(http_session_t * session, char *req_line)
{
	int i;

	for(i=0;methods[i]!=NULL;i++) {
		if(!strnicmp(req_line,methods[i],strlen(methods[i]))) {
			session->req.method=i;
			if(strlen(req_line)<strlen(methods[i])+2) {
				send_error(session,"400 Bad Request");
			add_env(session,"REQUEST_METHOD",methods[i]);
			return(req_line+strlen(methods[i])+1);
		}
	}
rswindell's avatar
rswindell committed
	if(req_line!=NULL && *req_line>=' ')
		send_error(session,"501 Not Implemented");
	return(NULL);
}

static BOOL get_req(http_session_t * session)
{
	char	req_line[MAX_REQUEST_LINE];
	char *	p;
rswindell's avatar
rswindell committed
	struct timeval tv;
	
	tv.tv_sec=startup->max_inactivity;
	tv.tv_usec=0;
	FD_ZERO(&sockset);
	FD_SET(session->socket,&sockset);
	if(select(session->socket+1,&sockset,NULL,NULL,&tv)<=0)  {
		session->req.keep_alive=FALSE;
	} else  {
		if(sockreadline(session,req_line,sizeof(req_line))>0) {
			if(startup->options&WEB_OPT_DEBUG_RX)
				lprintf("%04d Got request line: %s",session->socket,req_line);
			p=NULL;
			p=get_method(session,req_line);
			if(p!=NULL) {
				p=get_request(session,p);
				session->http_ver=get_version(p);
				if(session->http_ver>=HTTP_1_1)
					session->req.keep_alive=TRUE;
				if(session->req.dynamic==IS_CGI)
					add_env(session,"SERVER_PROTOCOL",session->http_ver ? 
						http_vers[session->http_ver] : "HTTP/0.9");
		}
	}
	close_request(session);
	return FALSE;
}

static BOOL check_request(http_session_t * session)
{
	char	path[MAX_PATH+1];
	char	str[MAX_PATH+1];
	if(!(startup->options&WEB_OPT_VIRTUAL_HOSTS))
		session->req.host[0]=0;
	if(session->req.host[0]) {
		sprintf(str,"%s/%s",root_dir,session->req.host);
		if(isdir(str))
			sprintf(str,"%s/%s%s",root_dir,session->req.host,session->req.physical_path);
		sprintf(str,"%s%s",root_dir,session->req.physical_path);
	if(FULLPATH(path,str,sizeof(session->req.physical_path))==NULL) {
		send_error(session,"404 Not Found");
		return(FALSE);
	}
	if(!fexist(path)) {
		lprintf("%04d Requested file doesn't exist: %s", session->socket, path);
		last_ch=*lastchar(path);
		if(last_ch!='/' && last_ch!='\\')  {
			strcat(path,"/");
		last_ch=*lastchar(session->req.virtual_path);
		if(last_ch!='/' && last_ch!='\\')  {
			strcat(session->req.virtual_path,"/");
		}
		last_slash=strrchr(str,'/');
		last_slash++;
		for(i=0;i<4 && startup->index_file_name[i][0] && !fexist(path);i++)  {
			*last_slash=0;
			strcat(path,startup->index_file_name[i]);
		}
		strcat(session->req.virtual_path,startup->index_file_name[i-1]);
		session->req.send_location=MOVED_PERM;
	if(strnicmp(path,root_dir,strlen(root_dir))) {
		send_error(session,"400 Bad Request");
		session->req.keep_alive=FALSE;
		return(FALSE);
	}
		send_error(session,"404 Not Found");
	SAFECOPY(session->req.physical_path,path);
	if(session->req.dynamic==IS_CGI)  {
		add_env(session,"PATH_TRANSLATED",path);
		add_env(session,"SCRIPT_NAME",session->req.virtual_path);
	}
	SAFECOPY(str,session->req.virtual_path);
	last_slash=strrchr(str,'/');
	if(last_slash!=NULL)
		*(last_slash+1)=0;
	if(session->req.dynamic==IS_CGI)
		add_env(session,"PATH_INFO",str);

	/* Set default ARS to a 0-length string */
	session->req.ars[0]=0;
	/* Walk up from root_dir checking for access.ars */
	SAFECOPY(str,path);
	last_slash=str+strlen(root_dir)-1;
	/* Loop while there's more /s in path*/
	while((last_slash=strchr(last_slash+1,'/'))!=NULL) {
		/* Terminate the path after the slash */
		*(last_slash+1)=0;
		strcat(str,"access.ars");
		if(fexist(str)) {
			if(!strcmp(path,str)) {
				send_error(session,"403 Forbidden");
				return(FALSE);
			}
			/* Read access.ars file */
			if((file=fopen(str,"r"))!=NULL) {
rswindell's avatar
rswindell committed
				fgets(session->req.ars,sizeof(session->req.ars),file);
				fclose(file);
			}
			else  {
				/* If cannot open access.ars, only allow sysop access */
				SAFECOPY(session->req.ars,"LEVEL 90");
				break;
			}
			/* Truncate at \r or \n - can use last_slash since I'm done with it.*/
			truncsp(session->req.ars);
		}
		SAFECOPY(str,path);
	}
	
	if(session->req.ars[0] && !(check_ars(session))) {
		/* No authentication provided */
rswindell's avatar
rswindell committed
		sprintf(str,"401 Unauthorized%s%s: Basic realm=\"%s\""
			,newline,get_header(HEAD_WWWAUTH),scfg.sys_name);
	lprintf("%04d Validated as %s",session->socket,session->req.physical_path);
static BOOL exec_cgi(http_session_t *session)  
{
	char	cmdline[MAX_PATH+256];
#ifdef __unix__
	/* ToDo: Damn, that's WAY too many variables */
	int		i=0;
	int		status=0;
	pid_t	child=0;
	int		in_pipe[2];
	int		out_pipe[2];
	int		err_pipe[2];
	struct timeval tv={0,0};
	int		high_fd=0;
	char	buf[MAX_REQUEST_LINE+1];
	size_t	post_offset=0;
	BOOL	done_parsing_headers=FALSE;
	BOOL	done_reading=FALSE;
	char	cgi_status[MAX_REQUEST_LINE+1];
	char	header[MAX_REQUEST_LINE+1];
	char	*directive=NULL;
	char	*value=NULL;
	BOOL	got_valid_headers=FALSE;
	time_t	start;
	SAFECOPY(cmdline,session->req.physical_path);
	/* ToDo: Should only do this if the Content-Length header was NOT sent */
	session->req.keep_alive=FALSE;

	/* Set up I/O pipes */
	if(pipe(in_pipe)!=0) {
		lprintf("%04d Can't create in_pipe",session->socket,buf);
		return(FALSE);
	}
    fcntl(in_pipe[0],F_SETFL,fcntl(in_pipe[0],F_GETFL)|O_NONBLOCK);
    fcntl(in_pipe[1],F_SETFL,fcntl(in_pipe[1],F_GETFL)|O_NONBLOCK);
	if(pipe(out_pipe)!=0) {
		lprintf("%04d Can't create out_pipe",session->socket,buf);
		return(FALSE);
	}
    fcntl(out_pipe[0],F_SETFL,fcntl(out_pipe[0],F_GETFL)|O_NONBLOCK);
    fcntl(out_pipe[1],F_SETFL,fcntl(out_pipe[1],F_GETFL)|O_NONBLOCK);

	if(pipe(err_pipe)!=0) {
		lprintf("%04d Can't create err_pipe",session->socket,buf);
    fcntl(err_pipe[0],F_SETFL,fcntl(err_pipe[0],F_GETFL)|O_NONBLOCK);
    fcntl(err_pipe[1],F_SETFL,fcntl(err_pipe[1],F_GETFL)|O_NONBLOCK);

	if((child=fork())==0)  {
		/* Set up environment */
		while(session->req.cgi_env != NULL)  {
			putenv(session->req.cgi_env->val);
			session->req.cgi_env=session->req.cgi_env->next;
		}
		
		/* Set up STDIO */
		close(in_pipe[1]);		/* close write-end of pipe */
		dup2(in_pipe[0],0);		/* redirect stdin */
		close(in_pipe[0]);		/* close excess file descriptor */
		close(out_pipe[0]);		/* close read-end of pipe */
		dup2(out_pipe[1],1);	/* stdout */
		close(out_pipe[1]);		/* close excess file descriptor */
		close(err_pipe[0]);		/* close read-end of pipe */
		dup2(err_pipe[1],2);	/* stderr */
		close(err_pipe[1]);		/* close excess file descriptor */

		/* Execute command */
		execl(cmdline,cmdline,NULL);
		lprintf("FAILED! execl()");
		exit(EXIT_FAILURE); /* Should never happen */
	}
	close(in_pipe[0]);		/* close excess file descriptor */
	close(out_pipe[1]);		/* close excess file descriptor */
	close(err_pipe[1]);		/* close excess file descriptor */
	start=time(NULL);
	if(child==0)  {
		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 */
		return(FALSE);
	}
	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)  {
		if(!done_wait)
			done_wait = (waitpid(child,&status,WNOHANG)==child);
		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(!done_reading && (select(high_fd+1,&read_set,&write_set,NULL,&tv)>0))  {
			if(FD_ISSET(session->socket,&read_set))  {
				done_reading=TRUE;
			}
			if(FD_ISSET(in_pipe[1],&write_set))  {
				if(post_offset < session->req.post_len)  {
					i=post_offset;
					post_offset+=write(in_pipe[1],
						session->req.post_data+post_offset,
						session->req.post_len-post_offset);
					if(post_offset>=session->req.post_len)
						close(in_pipe[1]);
					if(i!=post_offset)  {
						start=time(NULL);
					}
					if(i<0)  {
						done_reading=TRUE;
					}
			}
			if(FD_ISSET(out_pipe[0],&read_set))  {
				if(done_parsing_headers && got_valid_headers)  {
					i=read(out_pipe[0],buf,MAX_REQUEST_LINE);
					if(i>0)  {
						start=time(NULL);
						write(session->socket,buf,i);
					}
					if(i<=0)  {
						done_reading=TRUE;
				else  {
					/* This is the tricky part */
					i=pipereadline(out_pipe[0],buf,MAX_REQUEST_LINE);
					if(i<0)  {
						done_reading=TRUE;
						got_valid_headers=FALSE;
					}
					if(i>0)  {
						start=time(NULL);
					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,MAX_REQUEST_LINE);
				buf[i]=0;
				if(i>0)  {
					start=time(NULL);
				}
				if(i<0)  {
					done_reading=TRUE;
		}
		else  {
			if(!done_wait)
				done_wait = (waitpid(child,&status,WNOHANG)==child);
			if((time(NULL)-start) >= startup->max_cgi_inactivity)  {
				/* timeout */
				lprintf("%04d CGI Script %s Timed out",session->socket,cmdline);
				if(!done_wait)  {
					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);
	}
	if(!done_wait)  {
		lprintf("%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(!done_parsing_headers || !got_valid_headers)
		return(FALSE);
	return(TRUE);
/********************/
/* 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);

	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);

	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);
}

deuce's avatar
deuce committed
JSObject* DLLCALL js_CreateHttpRequestObject(JSContext* cx, JSObject* parent, http_session_t *session)
{
	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);

	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);

	/* 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);

	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("!JavaScript: %s", 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("!JavaScript %s%s%s: %s",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)
{
	char		ver[256];
	JSContext*	js_cx;
	JSObject*	js_glob;
	JSObject*	server;
	JSString*	js_str;
	jsval		val;
	BOOL		success=FALSE;

	lprintf("%04d JavaScript: Initializing context (stack: %lu bytes)"
		,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);
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
deuce's avatar
deuce committed
			,&session->js_glob, session))==NULL)) {
			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_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user
			,NULL /* ftp index file */, NULL /* subscan */)) 
			lprintf("%04d !JavaScript ERROR creating user objects",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);