Skip to content
Snippets Groups Projects
websrvr.c 207 KiB
Newer Older
	BOOL	no_chunked=FALSE;
	char*	env_block;
	char	startup_dir[MAX_PATH+1];
	HANDLE	rdpipe=INVALID_HANDLE_VALUE;
	HANDLE	wrpipe=INVALID_HANDLE_VALUE;
	HANDLE	rdoutpipe;
	HANDLE	wrinpipe;
	DWORD	retval;
    PROCESS_INFORMATION process_info;
	SECURITY_ATTRIBUTES sa;
    STARTUPINFO startup_info={0};

    startup_info.cb=sizeof(startup_info);
	startup_info.dwFlags|=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
    startup_info.wShowWindow=SW_HIDE;

	SAFECOPY(startup_dir,session->req.physical_path);
	if((p=strrchr(startup_dir,'/'))!=NULL || (p=strrchr(startup_dir,'\\'))!=NULL)
		*p=0;
	else
		SAFECOPY(startup_dir,session->req.cgi_dir?session->req.cgi_dir:cgi_dir);

	lprintf(LOG_DEBUG,"%04d CGI startup dir: %s", session->socket, startup_dir);

	if((p=get_cgi_handler(session->req.physical_path))!=NULL)
		SAFEPRINTF2(cmdline,"%s %s",p,session->req.physical_path);
	else
		SAFECOPY(cmdline,session->req.physical_path);
	lprintf(LOG_INFO,"%04d Executing CGI: %s",session->socket,cmdline);

	orig_keep=session->req.keep_alive;
	session->req.keep_alive=FALSE;

	memset(&sa,0,sizeof(sa));
	sa.nLength= sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	/* Create the child output pipe (override default 4K buffer size) */
	if(!CreatePipe(&rdoutpipe,&startup_info.hStdOutput,&sa,sizeof(buf))) {
		lprintf(LOG_ERR,"%04d !ERROR %d creating stdout pipe",session->socket,GetLastError());
		return(FALSE);
	}
	startup_info.hStdError=startup_info.hStdOutput;

	/* Create the child input pipe. */
	if(!CreatePipe(&startup_info.hStdInput,&wrinpipe,&sa,0 /* default buffer size */)) {
		lprintf(LOG_ERR,"%04d !ERROR %d creating stdin pipe",session->socket,GetLastError());
		CloseHandle(rdoutpipe);
		return(FALSE);
	}

	DuplicateHandle(
		GetCurrentProcess(), rdoutpipe,
		GetCurrentProcess(), &rdpipe, 0, FALSE, DUPLICATE_SAME_ACCESS);

	DuplicateHandle(
		GetCurrentProcess(), wrinpipe,
		GetCurrentProcess(), &wrpipe, 0, FALSE, DUPLICATE_SAME_ACCESS);

	CloseHandle(rdoutpipe);
	CloseHandle(wrinpipe);

	env_list=get_cgi_env(session);
	env_block = strListCreateBlock(env_list);
	strListFree(&env_list);

    success=CreateProcess(
		NULL,			/* pointer to name of executable module */
		cmdline,  		/* pointer to command line string */
		NULL,  			/* process security attributes */
		NULL,   		/* thread security attributes */
		TRUE,	 		/* handle inheritance flag */
		CREATE_NEW_CONSOLE, /* creation flags */
        env_block,  	/* pointer to new environment block */
		startup_dir,	/* pointer to current directory name */
		&startup_info,  /* pointer to STARTUPINFO */
		&process_info  	/* pointer to PROCESS_INFORMATION */
		);

	strListFreeBlock(env_block);
	if(!success) {
		lprintf(LOG_ERR,"%04d !ERROR %d running %s",session->socket,GetLastError(),cmdline);
		return(FALSE);
    }

	cd.wrpipe = wrpipe;
	cd.rdpipe = rdpipe;
	cd.child = process_info.hProcess;
	cd.session = session;
	int ret = do_cgi_stuff(session, &cgi, orig_keep);
	if (ret & CGI_STUFF_DONE_PARSING)
		done_parsing_headers = TRUE;
	if (ret & CGI_STUFF_VALID_HEADERS)
		got_valid_headers = TRUE;

    if(GetExitCodeProcess(process_info.hProcess, &retval)==FALSE)
	    lprintf(LOG_ERR,"%04d !ERROR GetExitCodeProcess(%s) returned %d"
			,session->socket,getfname(cmdline),GetLastError());
		lprintf(LOG_WARNING,"%04d Terminating CGI process: %s"
			,session->socket,getfname(cmdline));
		TerminateProcess(process_info.hProcess, GetLastError());

	if(rdpipe!=INVALID_HANDLE_VALUE)
		CloseHandle(rdpipe);
	if(wrpipe!=INVALID_HANDLE_VALUE)
		CloseHandle(wrpipe);
	CloseHandle(process_info.hProcess);

		lprintf(LOG_WARNING,"%04d !CGI Process %s did not generate valid headers"
			,session->socket,getfname(cmdline));
		lprintf(LOG_WARNING,"%04d !CGI Process %s did not send data header termination"
			,session->socket,getfname(cmdline));
/********************/
/* JavaScript stuff */
/********************/

JSObject* 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, session->req.status))==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* js_CreateHttpRequestObject(JSContext* cx
											 ,JSObject* parent, http_session_t *session)
deuce's avatar
deuce committed
{
/*	JSObject*	cookie; */
deuce's avatar
deuce committed
	jsval		val;

	/* Return existing object if it's already been created */
	if(JS_GetProperty(cx,parent,"http_request",&val) && val!=JSVAL_VOID)  {
		session->js_request=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
	else
		session->js_request = JS_DefineObject(cx, parent, "http_request", NULL
									, NULL, JSPROP_ENUMERATE|JSPROP_READONLY);
	js_add_request_prop(session,"path_info",session->req.extra_path_info);
	js_add_request_prop(session,"method",methods[session->req.method]);
	js_add_request_prop(session,"virtual_path",session->req.virtual_path);
deuce's avatar
deuce committed

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


	return(session->js_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;
		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)
		SAFEPRINTF(file," %s",report->filename);
		SAFEPRINTF(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";
		log_level=LOG_WARNING;
	} else {
		log_level=LOG_ERR;
	lprintf(log_level,"%04d !JavaScript %s%s%s: %s, Request: %s"
		,session->socket,warning,file,line,message, session->req.request_line);
	if(session->req.fp!=NULL)
		fprintf(session->req.fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
}

static void js_writebuf(http_session_t *session, const char *buf, size_t buflen)
		if(session->req.send_content)
			writebuf(session,buf,buflen);
	}
	else
		fwrite(buf,1,buflen,session->req.fp);
}

deuce's avatar
deuce committed
js_writefunc(JSContext *cx, uintN argc, jsval *arglist, BOOL writeln)
deuce's avatar
deuce committed
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
	jsrefcount	rc;
deuce's avatar
deuce committed
	char		*cstr=NULL;
	size_t		cstr_sz=0;
deuce's avatar
deuce committed
	size_t		len;

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

	if((!session->req.prev_write) && (!session->req.sent_headers)) {
		if(session->http_ver>=HTTP_1_1 && session->req.keep_alive) {
deuce's avatar
deuce committed
			if(!ssjs_send_headers(session,TRUE)) {
		else {
			/* "Fast Mode" requested? */
			jsval		val;
			JSObject*	reply = NULL;
			if(JS_GetProperty(cx, session->js_glob, "http_reply", &val))
				reply=JSVAL_TO_OBJECT(val);
			if(reply != NULL
				&& JS_GetProperty(cx, reply, "fast", &val)
				&& JSVAL_IS_BOOLEAN(val) && JSVAL_TO_BOOLEAN(val)) {
deuce's avatar
deuce committed
				if(!ssjs_send_headers(session,FALSE)) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			continue;
deuce's avatar
deuce committed
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, &len);
		HANDLE_PENDING(cx, cstr);
deuce's avatar
deuce committed
		js_writebuf(session, cstr, len);
		if(writeln)
			js_writebuf(session, newline, 2);
deuce's avatar
deuce committed
	if(cstr)
		free(cstr);
deuce's avatar
deuce committed
		JS_SET_RVAL(cx,arglist,JSVAL_VOID);
deuce's avatar
deuce committed
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
js_write(JSContext *cx, uintN argc, jsval *arglist)
deuce's avatar
deuce committed
	js_writefunc(cx, argc, arglist, FALSE);
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
deuce's avatar
deuce committed
	js_writefunc(cx, argc, arglist,TRUE);
deuce's avatar
deuce committed
static JSBool
js_set_cookie(JSContext *cx, uintN argc, jsval *arglist)
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
	char	header_buf[8192];
	char	*header;
deuce's avatar
deuce committed
	int32	i;
rswindell's avatar
rswindell committed
	JSBool	b;
deuce's avatar
deuce committed
	struct tm tm;
	http_session_t* session;
deuce's avatar
deuce committed
	time_t	tt;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
	if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	if(argc<2)
		return(JS_FALSE);

	header=header_buf;
deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
deuce's avatar
deuce committed
	if(!p)
		return(JS_FALSE);
	header+=sprintf(header,"Set-Cookie: %s=",p);
deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[1], p, NULL);
deuce's avatar
deuce committed
	if(!p)
		return(JS_FALSE);
	header+=sprintf(header,"%s",p);
	FREE_AND_NULL(p);
deuce's avatar
deuce committed
	if(argc>2) {
		if(!JS_ValueToInt32(cx,argv[2],&i))
			return JS_FALSE;
deuce's avatar
deuce committed
		tt=i;
		if(i && gmtime_r(&tt,&tm)!=NULL)
deuce's avatar
deuce committed
			header += strftime(header,50,"; expires=%a, %d-%b-%Y %H:%M:%S GMT",&tm);
	}
	if(argc>3) {
deuce's avatar
deuce committed
		JSVALUE_TO_MSTRING(cx, argv[3], p, NULL);
		if(p!=NULL && *p) {
deuce's avatar
deuce committed
			header += sprintf(header,"; domain=%s",p);
		FREE_AND_NULL(p);
deuce's avatar
deuce committed
	}
	if(argc>4) {
deuce's avatar
deuce committed
		JSVALUE_TO_MSTRING(cx, argv[4], p, NULL);
		if(p!=NULL && *p) {
deuce's avatar
deuce committed
			header += sprintf(header,"; path=%s",p);
		FREE_AND_NULL(p);
deuce's avatar
deuce committed
	}
	if(argc>5) {
rswindell's avatar
rswindell committed
		JS_ValueToBoolean(cx, argv[5], &b);
		if(b)
deuce's avatar
deuce committed
			header += sprintf(header,"; secure");
	}
	strListPush(&session->req.dynamic_heads,header_buf);

	return(JS_TRUE);
}

js_log(JSContext *cx, uintN argc, jsval *arglist)
	jsval *argv=JS_ARGV(cx, arglist);
	char		str[512];
    uintN		i=0;
	int32		level=LOG_INFO;
	http_session_t* session;
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

	if(startup==NULL || startup->lputs==NULL)
		return(JS_FALSE);
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}

	str[0]=0;
    for(;i<argc && strlen(str)<(sizeof(str)/2);i++) {
		char* tp=strchr(str, 0);
		JSVALUE_TO_STRBUF(cx, argv[i], tp, sizeof(str)/2, NULL);
		SAFECAT(str," ");
	lprintf(level,"%04d %s",session->socket,str);
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)));
js_login(JSContext *cx, uintN argc, jsval *arglist)
	jsval *argv=JS_ARGV(cx, arglist);
	char*		username;
	char*		password;
	JSBool		inc_logons=JS_FALSE;
	user_t		user;
	http_session_t*	session;
deuce's avatar
deuce committed
	jsrefcount	rc;
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE));

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

	/* User name */
	JSVALUE_TO_ASTRING(cx, argv[0], username, (LEN_ALIAS > LEN_NAME) ? LEN_ALIAS+2 : LEN_NAME+2, NULL);
	if(username==NULL)
		user.number=atoi(username);
	else if(*username)
		user.number=matchuser(&scfg,username,FALSE);

	if(getuserdat(&scfg,&user)!=0) {
		lprintf(LOG_NOTICE,"%04d !USER NOT FOUND: '%s'"
			,session->socket, username);
		return(JS_TRUE);
	}

	if(user.misc&(DELETED|INACTIVE)) {
		lprintf(LOG_WARNING,"%04d !DELETED OR INACTIVE USER #%d: %s"
			,session->socket,user.number, username);
		JSVALUE_TO_ASTRING(cx, argv[1], password, LEN_PASS+2, NULL);
		if(password==NULL)
		if(stricmp(user.pass, password)) { /* Wrong password */
			lprintf(LOG_WARNING,"%04d !INVALID PASSWORD ATTEMPT FOR USER: '%s'"
			badlogin(session->socket,session->client.protocol, username, password, session->host_name, &session->addr);
			return(JS_TRUE);
		}
	}

	if(argc>2)
		JS_ValueToBoolean(cx,argv[2],&inc_logons);

	if(inc_logons) {
		user.logons++;
		user.ltoday++;
	}

	http_logon(session, &user);

rswindell's avatar
rswindell committed
	if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user, &session->client
		,startup->file_vpath_prefix, session->subscan /* subscan */)) {
		lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user objects",session->socket);
		send_error(session,__LINE__,"500 Error initializing JavaScript User Objects");
	JS_SET_RVAL(cx, arglist,BOOLEAN_TO_JSVAL(JS_TRUE));
	if(startup->sound.login[0] && !sound_muted(&scfg))
		PlaySound(startup->sound.login, NULL, SND_ASYNC|SND_FILENAME);
#endif

#if 0
static char *find_next_pair(char *buffer, size_t buflen, char find)
{
	char	*p;
	char	*search;
	char	*end;
	size_t	buflen2;
	char	chars[5]="@%^<";

	end=buffer+buflen;
	search=buffer;
	buflen2=buflen;

	for(;search<end;) {
		p=memchr(search, chars[i], buflen2);
		/* Can't even find one... there's definatly no pair */
		if(p==NULL)
			return(NULL);

		if(*(p+1)==find)
			return(p);

		/* Next search pos is at the char after the match */
		search=p+1;
		buflen2=end-search;
	}
}

static void js_write_escaped(JSContext *cx, JSObject *obj, char *pos, size_t len, char *name_end, char *repeat_section)
{
	char	*name=pos+2;

}

enum {
	 T_AT
	,T_PERCENT
	,T_CARET
	,T_LT
};

static int js_write_template_part(JSContext *cx, JSObject *obj, char *template, size_t len, char *repeat_section)
{
	size_t		len2;
	char		*pos;
	char		*end;
	char		*p;
	char		*p2;
	char		*send_end;
	int			no_more[4];
	char		*next[4];
	int			i,j;
	char		chars[5]="@%^<";

	end=template+len;
	pos=template;
	memset(&next,0,sizeof(next));
	memset(&no_more,0,sizeof(no_more));

	while(pos<end) {
		send_end=NULL;

		/* Find next seperator */
		for(i=0; i<4; i++) {
			if(!no_more[i]) {
				if(next[i] < pos)
					next[i]=NULL;
				if(next[i] == NULL) {
					if((next[i]=find_next_pair(pos, len, chars[i]))==NULL) {
						no_more[i]=TRUE;
						continue;
					}
				}
				if(!send_end || next[i] < send_end)
					send_end=next[i];
			}
		}
		if(send_end==NULL) {
			/* Nothing else matched... we're good now! */
			js_writebuf(session, pos, len);
			pos=end;
			len=0;
			continue;
		}
		if(send_end > pos) {
			i=send_end-pos;
			js_writebuf(session, pos, i);
			pos+=i;
			len-=i;
		}

		/*
		 * At this point, pos points to a matched introducer.
		 * If it's not a repeat section, we can just output it here.
		 */
		if(*pos != '<') {
			/*
			 * If there is no corresponding terminator to this introdcer,
			 * force it to be output unchanged.
			 */
			if((p=find_next_pair(pos, len, *pos))==NULL) {
				no_more[strchr(chars,*pos)-char]=TRUE;
				continue;
			}
			js_write_escaped(cx, obj, pos, len, p, repeat_section);
			continue;
		}

		/*
		 * Pos is the start of a repeat section now... this is where things
		 * start to get tricky.  Set up RepeatObj object, then call self
		 * once for each repeat.
		 */
	}
}

static JSBool
js_write_template(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	JSString*	js_str;
	char		*filename;
	char		*template;
	FILE		*tfile;
	size_t		len;
	http_session_t* session;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[0], filename, NULL);
	if(filename==NULL)
		return(JS_FALSE);

	if(!fexist(filename)) {
deuce's avatar
deuce committed
		free(filename);
		JS_ReportError(cx, "Template file %s does not exist.", filename);
		return(JS_FALSE);
	}
	len=flength(filename);

	if((tfile=fopen(filename,"r"))==NULL) {
deuce's avatar
deuce committed
		free(filename);
		JS_ReportError(cx, "Unable to open template %s for read.", filename);
		return(JS_FALSE);
	}
deuce's avatar
deuce committed
	free(filename);
deuce's avatar
deuce committed
	if((template=(char *)malloc(len))==NULL) {
		JS_ReportError(cx, "Unable to allocate %u bytes for template.", len);
		return(JS_FALSE);
	}

	if(fread(template, 1, len, tfile) != len) {
deuce's avatar
deuce committed
		free(template);
		fclose(tfile);
		JS_ReportError(cx, "Unable to read %u bytes from template %s.", len, filename);
		return(JS_FALSE);
	}
	fclose(tfile);

	if((!session->req.prev_write) && (!session->req.sent_headers)) {
		if(session->http_ver>=HTTP_1_1 && session->req.keep_alive) {
deuce's avatar
deuce committed
			if(!ssjs_send_headers(session,TRUE)) {
				free(template);
		}
		else {
			/* "Fast Mode" requested? */
			jsval		val;
			JSObject*	reply;
			if(JS_GetProperty(cx, session->js_glob, "http_reply", &val))
				reply=JSVAL_TO_OBJECT(val);
			if(JS_GetProperty(cx, reply, "fast", &val)
				&& JSVAL_IS_BOOLEAN(val) && JSVAL_TO_BOOLEAN(val)) {
deuce's avatar
deuce committed
				if(!ssjs_send_headers(session,FALSE)) {
					free(template);
			}
		}
	}

	session->req.prev_write=TRUE;
	js_write_template_part(cx, obj, template, len, NULL);
deuce's avatar
deuce committed
	free(template);
static JSFunctionSpec js_global_functions[] = {
	{"write",           js_write,           1},		/* write to HTML file */
	{"writeln",         js_writeln,         1},		/* write line to HTML file */
	{"print",			js_writeln,			1},		/* write line to HTML file (alias) */
	{"log",				js_log,				0},		/* Log a string */
	{"login",           js_login,           2},		/* log in as a different user */
deuce's avatar
deuce committed
	{"set_cookie",		js_set_cookie,		2},		/* Set a cookie */
js_OperationCallback(JSContext *cx)
	JS_SetOperationCallback(cx, NULL);
	if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL) {
		JS_SetOperationCallback(cx, js_OperationCallback);
    ret=js_CommonOperationCallback(cx,&session->js_callback);
	JS_SetOperationCallback(cx, js_OperationCallback);
js_initcx(http_session_t *session)
    if((js_cx = JS_NewContext(session->js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL)
	JS_SetOptions(js_cx, startup->js.options);
	lprintf(LOG_DEBUG,"%04d JavaScript: Context created with options: %lx"
		,session->socket, (long)startup->js.options);

    JS_SetErrorReporter(js_cx, js_ErrorReporter);

	JS_SetOperationCallback(js_cx, js_OperationCallback);
	lprintf(LOG_DEBUG,"%04d JavaScript: Creating Global Objects and Classes",session->socket);
	if(!js_CreateCommonObjects(js_cx, &scfg, NULL
									,NULL						/* global */
									,uptime						/* system */
									,&startup->js				/* js */
									,&session->client			/* client */
									,session->socket			/* client */
									,session->tls_sess			/* client */
		|| !JS_DefineFunctions(js_cx, session->js_glob, js_global_functions)) {
		JS_RemoveObjectRoot(js_cx, &session->js_glob);
static BOOL js_setup_cx(http_session_t* session)
	if(session->js_runtime == NULL) {
		lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes"
			,session->socket,startup->js.max_bytes);
		if((session->js_runtime=jsrt_GetNew(startup->js.max_bytes, 5000, __FILE__, __LINE__))==NULL) {
			lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",session->socket);
			return(FALSE);
		}
	}

	if(session->js_cx==NULL) {	/* Context not yet created, create it now */
		/* js_initcx() begins a context */
		if(((session->js_cx=js_initcx(session))==NULL)) {
			lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript context",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);

		JS_DefineProperty(session->js_cx, session->js_glob, "web_root_dir",
			STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx, root_dir))
			,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
		JS_DefineProperty(session->js_cx, session->js_glob, "web_error_dir",
			STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx, session->req.error_dir?session->req.error_dir:error_dir))
			,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
		JS_ENDREQUEST(session->js_cx);
		JS_BEGINREQUEST(session->js_cx);
	lprintf(LOG_DEBUG,"%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);
		JS_ENDREQUEST(session->js_cx);
	JS_SetContextPrivate(session->js_cx, session);

	return TRUE;
}

static BOOL js_setup(http_session_t* session)
{
	if(!js_setup_cx(session))
		return FALSE;

	lprintf(LOG_DEBUG,"%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);
		JS_ENDREQUEST(session->js_cx);
static BOOL ssjs_send_headers(http_session_t* session,int chunked)
	JSObject*	reply = NULL;
	JSIdArray*	heads = NULL;
	JSObject*	headers = NULL;
deuce's avatar
deuce committed
	char		*p=NULL,*p2=NULL;
	size_t		p_sz=0, p2_sz=0;
	JS_BEGINREQUEST(session->js_cx);
	if(JS_GetProperty(session->js_cx,session->js_glob,"http_reply",&val)) {
		reply = JSVAL_TO_OBJECT(val);
		if(JS_GetProperty(session->js_cx,reply,"status",&val))
			JSVALUE_TO_STRBUF(session->js_cx, val, session->req.status, sizeof(session->req.status), NULL);
		if(JS_GetProperty(session->js_cx,reply,"header",&val)) {
			headers = JSVAL_TO_OBJECT(val);
			heads=JS_Enumerate(session->js_cx,headers);
		}
deuce's avatar
deuce committed
	if(heads != NULL) {
		for(i=0;i<heads->length;i++)  {
			JS_IdToValue(session->js_cx,heads->vector[i],&val);
deuce's avatar
deuce committed
			JSVALUE_TO_RASTRING(session->js_cx, val, p, &p_sz, NULL);
			if(p==NULL) {
				if(p2)
					free(p2);
				JS_DestroyIdArray(session->js_cx, heads);
deuce's avatar
deuce committed
				return FALSE;
			}
			if(JS_GetProperty(session->js_cx,headers,p,&val))
				JSVALUE_TO_RASTRING(session->js_cx, val, p2, &p2_sz, NULL);
deuce's avatar
deuce committed
			if(JS_IsExceptionPending(session->js_cx)) {
				if(p)
					free(p);
				if(p2)
					free(p2);
				JS_DestroyIdArray(session->js_cx, heads);
deuce's avatar
deuce committed
				return FALSE;
			}
			if (p2 != NULL && !session->req.sent_headers) {
				h = get_header_type(p);
				switch(h) {
				case HEAD_LOCATION:
					if (*p2 == '/') {
						unescape(p2);
						SAFECOPY(session->req.virtual_path,p2);
						session->req.send_location=MOVED_STAT;
					}
					else {
						SAFECOPY(session->req.virtual_path,p2);
						session->req.send_location=MOVED_TEMP;
					}
					if (atoi(session->req.status) == 200)
						SAFECOPY(session->req.status, error_302);
					break;
				case HEAD_LENGTH:
				case HEAD_TRANSFER_ENCODING:
					/* If either of these are manually set, point
					 * the gun at the script writers foot for them */
					chunked = false;
					session->req.manual_length = TRUE;
					// fall-through
				default:
					safe_snprintf(str,sizeof(str),"%s: %s",p,p2);
					strListPush(&session->req.dynamic_heads,str);
				safe_snprintf(str,sizeof(str),"%s: %s",p,p2);
				strListPush(&session->req.dynamic_heads,str);
			}
deuce's avatar
deuce committed
		}
deuce's avatar
deuce committed
		if(p)
			free(p);
		if(p2)
			free(p2);
		JS_ClearScope(session->js_cx, headers);
		JS_DestroyIdArray(session->js_cx, heads);
	JS_ENDREQUEST(session->js_cx);
	return(send_headers(session,session->req.status,chunked));
static BOOL exec_ssjs(http_session_t* session, char* script)  {
	jsval		rval;
	char		path[MAX_PATH+1];
	BOOL		retval=TRUE;
	/* External JavaScript handler? */
	if(script == session->req.physical_path && session->req.xjs_handler[0])
		script = session->req.xjs_handler;

	sprintf(path,"%sSBBS_SSJS.%u.%u.html",scfg.temp_dir,getpid(),session->socket);
	if((session->req.fp=fopen(path,"wb"))==NULL) {
		lprintf(LOG_ERR,"%04d !ERROR %d opening/creating %s", session->socket, errno, path);
		return(FALSE);