Skip to content
Snippets Groups Projects
websrvr.c 114 KiB
Newer Older
	SAFECOPY(path,session->req.physical_path);
deuce's avatar
deuce committed
	if(startup->options&WEB_OPT_DEBUG_TX)
		lprintf(LOG_DEBUG,"%04d Path is: %s",session->socket,path);
		if(!IS_PATH_DELIM(last_ch))  {
deuce's avatar
deuce committed
			session->req.send_location=MOVED_PERM;
			strcat(path,"/");
		last_ch=*lastchar(session->req.virtual_path);
		if(!IS_PATH_DELIM(last_ch))  {
deuce's avatar
deuce committed
			session->req.send_location=MOVED_PERM;
			strcat(session->req.virtual_path,"/");
		}
		last_slash=find_last_slash(path);
deuce's avatar
deuce committed
		if(last_slash==NULL) {
			send_error(session,error_500);
deuce's avatar
deuce committed
			return(FALSE);
		}
		for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++)  {
			*last_slash=0;
			strcat(path,startup->index_file_name[i]);
deuce's avatar
deuce committed
			if(startup->options&WEB_OPT_DEBUG_TX)
				lprintf(LOG_DEBUG,"%04d Checking for %s",session->socket,path);
			if(!stat(path,&sb))
deuce's avatar
deuce committed
				break;

		/* Don't send 404 unless authourized... prevent info leak */
		if(startup->index_file_name[i] == NULL)
			send404=1;
		else {
			strcat(session->req.virtual_path,startup->index_file_name[i]);
			if(session->req.send_location != MOVED_PERM)
				session->req.send_location=MOVED_STAT;
	if(strnicmp(path,root_dir,strlen(root_dir))) {
		send_error(session,"400 Bad Request");
		lprintf(LOG_NOTICE,"%04d !ERROR Request for %s is outside of web root %s"
			,session->socket,path,root_dir);

	/* 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*/
deuce's avatar
deuce committed
	p=last_slash;
	while((last_slash=find_first_slash(p+1))!=NULL) {
deuce's avatar
deuce committed
		p=last_slash;
		/* Terminate the path after the slash */
		*(last_slash+1)=0;
		strcat(str,"access.ars");
		if(!stat(str,&sb)) {
			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(!check_ars(session)) {
		/* No authentication provided */
		sprintf(str,"401 Unauthorized%s%s: Basic realm=\"%s\""
			,newline,get_header(HEAD_WWWAUTH),scfg.sys_name);
		send_error(session,str);
		return(FALSE);
	if(stat(path,&sb) || IS_PATH_DELIM(*(lastchar(path))) || send404) {
		/* OPTIONS requests never return 404 errors (ala Apache) */
		if(session->req.method!=HTTP_OPTIONS) {
			if(startup->options&WEB_OPT_DEBUG_TX)
				lprintf(LOG_DEBUG,"%04d 404 - %s does not exist",session->socket,path);
			send_error(session,error_404);
			return(FALSE);
		}
	}
	SAFECOPY(session->req.physical_path,path);
	add_env(session,"SCRIPT_NAME",session->req.virtual_path);
	add_env(session,"SCRIPT_FILENAME",session->req.physical_path);
	SAFECOPY(str,session->req.virtual_path);
	last_slash=find_last_slash(str);
	if(last_slash!=NULL)
		*(last_slash+1)=0;
	if(*(session->req.extra_path_info))
	{
		sprintf(str,"%s%s",startup->root_dir,session->req.extra_path_info);
		add_env(session,"PATH_TRANSLATED",str);
		add_env(session,"PATH_INFO",session->req.extra_path_info);
	}

static str_list_t get_cgi_env(http_session_t *session)
{
	char		path[MAX_PATH+1];
	char		value[INI_MAX_VALUE_LEN+1];
	char*		deflt;
	char		defltbuf[INI_MAX_VALUE_LEN+1];
	char		append[INI_MAX_VALUE_LEN+1];
	char		prepend[INI_MAX_VALUE_LEN+1];
	char		env_str[(INI_MAX_VALUE_LEN*4)+2];
	FILE*		fp;
	size_t		i;
	str_list_t	env_list;
	str_list_t	add_list;

	if((env_list=strListInit())==NULL)
		return(NULL);

	strListAppendList(&env_list, session->req.cgi_env);

	strListPush(&env_list,"REDIRECT_STATUS=200");	/* Kludge for php-cgi */

	if((fp=iniOpenFile(iniFileName(path,sizeof(path),scfg.ctrl_dir,"cgi_env.ini"),/* create? */FALSE))==NULL)
		return(env_list);

	if((add_list=iniReadSectionList(fp,NULL))!=NULL) {

		for(i=0; add_list[i]!=NULL; i++) {
			if((deflt=getenv(add_list[i]))==NULL)
				deflt=iniReadString(fp,add_list[i],"default",NULL,defltbuf);
			if(iniReadString(fp,add_list[i],"value",deflt,value)==NULL)
				continue;
			iniReadString(fp,add_list[i],"append","",append);
			iniReadString(fp,add_list[i],"prepend","",prepend);
			safe_snprintf(env_str,sizeof(env_str),"%s=%s%s%s"
				,add_list[i], prepend, value, append);
			strListPush(&env_list,env_str);
		}
		strListFree(&add_list);
	}

	fclose(fp);

	return(env_list);
}


static BOOL exec_cgi(http_session_t *session)
	/* ToDo: Damn, that's WAY too many variables */
	int		i=0;
	int		status=0;
	pid_t	child=0;
	struct timeval tv={0,0};
	int		high_fd=0;
	char	buf[1024];
deuce's avatar
deuce committed
	char	fbuf[1026];
	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;
	char	cgipath[MAX_PATH+1];
	char	*p;
	BOOL	orig_keep=FALSE;
deuce's avatar
deuce committed
	str_list_t	tmpbuf;
	size_t	tmpbuflen=0;
	BOOL	no_chunked=FALSE;
	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;

deuce's avatar
deuce committed
		lprintf(LOG_ERR,"%04d Can't create out_pipe",session->socket);
deuce's avatar
deuce committed
		lprintf(LOG_ERR,"%04d Can't create err_pipe",session->socket);
		return(FALSE);
	}

	if((child=fork())==0)  {
deuce's avatar
deuce committed
		str_list_t  env_list;

		/* Do a full suid thing. */
		if(startup->setuid!=NULL)
			startup->setuid(TRUE);

deuce's avatar
deuce committed
		env_list=get_cgi_env(session);
		dup2(session->socket,0);		/* redirect stdin */
		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 */

		SAFECOPY(cgipath,cmdline);
		if((p=strrchr(cgipath,'/'))!=NULL)
		{
			*p=0;
			chdir(cgipath);
		}

		if(get_cgi_handler(cgipath, sizeof(cgipath))) {
			char *comspec;
			comspec=getenv("SHELL");
			if(comspec==NULL)
#ifdef _PATH_BSHELL
				comspec=_PATH_BSHELL;
#else
				comspec="/bin/sh";
#endif
			lprintf(LOG_INFO,"%04d Using handler %s to execute %s",session->socket,cgipath,cmdline);
			execle(comspec,comspec,"-c",cgipath,NULL,env_list);
		}
		else {
			execle(cmdline,cmdline,NULL,env_list);
		}
deuce's avatar
deuce committed

		lprintf(LOG_ERR,"%04d !FAILED! execle() (%d)",session->socket,errno);
		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(out_pipe[0]);		/* close read-end of pipe */
		close(err_pipe[0]);		/* close read-end of pipe */
	}

	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);
	high_fd=out_pipe[0];
	if(err_pipe[0]>high_fd)
		high_fd=err_pipe[0];
	/* ToDo: Magically set done_parsing_headers for nph-* scripts */
	cgi_status[0]=0;
deuce's avatar
deuce committed
	tmpbuf=strListInit();
	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_ZERO(&write_set);

		if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0)  {
			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) {
							if(session->req.write_chunked) {
								sprintf(header,"%X\r\n",i);
								write(session->socket,header,strlen(header));
							}
							snt=write(session->socket,buf,i);
							if(session->req.ld!=NULL && snt>0) {
								session->req.ld->size+=snt;
							if(session->req.write_chunked)
								write(session->socket,newline,2);
deuce's avatar
deuce committed
					i=pipereadline(out_pipe[0],buf,sizeof(buf), fbuf, sizeof(fbuf));
					if(i<0)  {
						done_reading=TRUE;
						got_valid_headers=FALSE;
					}
					if(!done_parsing_headers && *buf)  {
deuce's avatar
deuce committed
						if(tmpbuf != NULL)
							strListPush(&tmpbuf, fbuf);
						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)
									} else  {
										SAFECOPY(session->req.virtual_path,value);
										session->req.send_location=MOVED_STAT;
										if(cgi_status[0]==0)
									}
									break;
								case HEAD_STATUS:
									SAFECOPY(cgi_status,value);
									break;
									session->req.keep_alive=orig_keep;
									strListPush(&session->req.dynamic_heads,buf);
									no_chunked=TRUE;
								case HEAD_TYPE:
									got_valid_headers=TRUE;
									strListPush(&session->req.dynamic_heads,buf);
								case HEAD_TRANSFER_ENCODING:
									no_chunked=TRUE;
									break;
									strListPush(&session->req.dynamic_heads,buf);
						if(directive == NULL || value == NULL) {
							/* Invalid header line */
							done_parsing_headers=TRUE;
						}
					}
					else  {
						if(!no_chunked && session->http_ver>=HTTP_1_1) {
							session->req.keep_alive=orig_keep;
							session->req.write_chunked=TRUE;
						}
						if(got_valid_headers)  {
							session->req.dynamic=IS_CGI;
							if(cgi_status[0]==0)
								SAFECOPY(cgi_status,session->req.status);
							send_headers(session,cgi_status);
						}
						else {
							/* Invalid headers... send 'er all as plain-text */
deuce's avatar
deuce committed
							char    content_type[MAX_REQUEST_LINE+1];
deuce's avatar
deuce committed

							lprintf(LOG_DEBUG,"%04d Recieved invalid CGI headers, sending result as plain-text",session->socket);

deuce's avatar
deuce committed
							/* free() the non-headers so they don't get sent, then recreate the list */
							strListFreeStrings(session->req.dynamic_heads);

							/* Copy current status */
							SAFECOPY(cgi_status,session->req.status);

							/* Add the content-type header (REQUIRED) */
							SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content);
							strListPush(&session->req.dynamic_heads,content_type);
							send_headers(session,cgi_status);

							/* Now send the tmpbuf */
							for(i=0; tmpbuf != NULL && tmpbuf[i] != NULL; i++) {
								if(strlen(tmpbuf[i])>0) {
									if(session->req.write_chunked) {
										sprintf(header,"%X\r\n",strlen(tmpbuf[i]));
										write(session->socket,header,strlen(header));
									}
									snt=write(session->socket,tmpbuf[i],strlen(tmpbuf[i]));
									if(session->req.write_chunked)
										write(session->socket,newline,2);
									if(session->req.ld!=NULL && snt>0) {
										session->req.ld->size+=snt;
									}
								}
							}
							if(strlen(fbuf)>0) {
								if(session->req.write_chunked) {
									sprintf(header,"%X\r\n",strlen(fbuf));
									write(session->socket,header,strlen(header));
								}
								snt=write(session->socket,fbuf,strlen(fbuf));
deuce's avatar
deuce committed
								if(session->req.ld!=NULL && snt>0) {
									session->req.ld->size+=snt;
								}
								if(session->req.write_chunked)
									write(session->socket,newline,2);
deuce's avatar
deuce committed
							got_valid_headers=TRUE;
						}
			}
			if(FD_ISSET(err_pipe[0],&read_set))  {
				i=read(err_pipe[0],buf,sizeof(buf)-1);
				if(i>0) {
					buf[i]=0;
					lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
			if(!done_wait)
				done_wait = (waitpid(child,&status,WNOHANG)==child);
			if(!FD_ISSET(err_pipe[0],&read_set) && !FD_ISSET(out_pipe[0],&read_set) && done_wait)
				done_reading=TRUE;
		}
		else  {
			if((time(NULL)-start) >= startup->max_cgi_inactivity)  {
				lprintf(LOG_ERR,"%04d CGI Process %s Timed out",session->socket,getfname(cmdline));
deuce's avatar
deuce committed
	if(tmpbuf != NULL)
		strListFree(&tmpbuf);

	/* Drain STDERR */	
	tv.tv_sec=1;
	tv.tv_usec=0;
	FD_ZERO(&read_set);
	FD_SET(err_pipe[0],&read_set);
	while(select(high_fd+1,&read_set,NULL,NULL,&tv)>0) {
		if(FD_ISSET(err_pipe[0],&read_set)) {
			i=read(err_pipe[0],buf,sizeof(buf)-1);
			if(i>0) {
				buf[i]=0;
				lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
				start=time(NULL);
			}
			else
				break;
		}
		tv.tv_sec=1;
		tv.tv_usec=0;
		FD_ZERO(&read_set);
		FD_SET(err_pipe[0],&read_set);
	if(!done_wait)
		done_wait = (waitpid(child,&status,WNOHANG)==child);
			lprintf(LOG_NOTICE,"%04d CGI Process %s still alive on client exit"
				,session->socket,getfname(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(out_pipe[0]);		/* close read-end of pipe */
	close(err_pipe[0]);		/* close read-end of pipe */
		lprintf(LOG_ERR,"%04d CGI Process %s did not generate valid headers"
			,session->socket,getfname(cmdline));
		return(FALSE);
	}

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

	/* These are (more or less) copied from the Unix version */
	char*	p;
	char	cmdline[MAX_PATH+256];
	char	buf[4096];
	int		i;
	BOOL	orig_keep;
	BOOL	done_parsing_headers=FALSE;
	BOOL	got_valid_headers=FALSE;
	char	cgi_status[MAX_REQUEST_LINE+1];
	char	content_type[MAX_REQUEST_LINE+1];
	char	header[MAX_REQUEST_LINE+1];
	char	*directive=NULL;
	char	*value=NULL;
	time_t	start;
	BOOL	no_chunked=FALSE;
	char*	env_block;
	char	startup_dir[MAX_PATH+1];
	int		wr;
	HANDLE	rdpipe=INVALID_HANDLE_VALUE;
	HANDLE	wrpipe=INVALID_HANDLE_VALUE;
	HANDLE	rdoutpipe;
	HANDLE	wrinpipe;
	DWORD	waiting;
	DWORD	msglen;
	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(cmdline,session->req.physical_path);

	SAFECOPY(startup_dir,cmdline);
	if((p=strrchr(startup_dir,'/'))!=NULL || (p=strrchr(startup_dir,'\\'))!=NULL)
		*p=0;
	else
		SAFECOPY(startup_dir,cgi_dir);

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

	get_cgi_handler(cmdline, sizeof(cmdline));

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

	start=time(NULL);

	SAFECOPY(cgi_status,session->req.status);
	SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content);
		if(WaitForSingleObject(process_info.hProcess,0)==WAIT_OBJECT_0)
			process_terminated=TRUE;	/* handle remaining data in pipe before breaking */

		if((time(NULL)-start) >= startup->max_cgi_inactivity)  {
			lprintf(LOG_WARNING,"%04d CGI Process %s timed out after %u seconds of inactivity"
				,session->socket,getfname(cmdline),startup->max_cgi_inactivity);
			rdpipe,             /* handle to pipe to copy from */
			NULL,               /* pointer to data buffer */
			0,					/* size, in bytes, of data buffer */
			NULL,				/* pointer to number of bytes read */
			&waiting,			/* pointer to total number of bytes available */
			NULL				/* pointer to unread bytes in this message */
		if(!waiting) {
			if(process_terminated)
				break;
			Sleep(1);
			continue;
		}
		/* reset inactivity timer */
		start=time(NULL);	

		msglen=0;
		if(done_parsing_headers) {
			if(ReadFile(rdpipe,buf,sizeof(buf),&msglen,NULL)==FALSE) {
				lprintf(LOG_ERR,"%04d !ERROR %d reading from pipe"
					,session->socket,GetLastError());
				break;
		}
		else  {
			/* This is the tricky part */
			buf[0]=0;
			i=pipereadline(rdpipe,buf,sizeof(buf),NULL,0);
			if(i<0)  {
				lprintf(LOG_WARNING,"%04d CGI pipereadline returned %d",session->socket,i);
				got_valid_headers=FALSE;
				break;
			}
			lprintf(LOG_DEBUG,"%04d CGI header line: %s"
				,session->socket, buf);
			SAFECOPY(header,buf);
			if(strchr(header,':')!=NULL) {
				directive=strtok(header,":");
				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,error_302);
						} else  {
							SAFECOPY(session->req.virtual_path,value);
							session->req.send_location=MOVED_STAT;
							if(cgi_status[0]==0)
								SAFECOPY(cgi_status,error_302);
						}
						break;
					case HEAD_STATUS:
						SAFECOPY(cgi_status,value);
						break;
					case HEAD_LENGTH:
						session->req.keep_alive=orig_keep;
						strListPush(&session->req.dynamic_heads,buf);
						no_chunked=TRUE;
						break;
					case HEAD_TYPE:
						got_valid_headers=TRUE;
						SAFECOPY(content_type,buf);
						break;
					case HEAD_TRANSFER_ENCODING:
						no_chunked=TRUE;
						break;
					default:
						strListPush(&session->req.dynamic_heads,buf);
			if(i) {
				strcat(buf,"\r\n");	/* Add back the missing line terminator */
				msglen=strlen(buf);	/* we will send this text later */
			done_parsing_headers = TRUE;	/* invalid header */
			session->req.dynamic=IS_CGI;
			if(!no_chunked && session->http_ver>=HTTP_1_1) {
				session->req.keep_alive=orig_keep;
				session->req.write_chunked=TRUE;
			}
			strListPush(&session->req.dynamic_heads,content_type);
			send_headers(session,cgi_status);
		}
		if(msglen) {
			if(session->req.write_chunked) {
				sprintf(header,"%X\r\n",msglen);
				sendsocket(session->socket,header,strlen(header));
			}
			lprintf(LOG_DEBUG,"%04d Sending %d bytes: %.*s"
				,session->socket,msglen,msglen,buf);
			wr=sendsocket(session->socket,buf,msglen);
			/* log actual bytes sent */
			if(session->req.ld!=NULL && wr>0)
				session->req.ld->size+=wr;	
			if(session->req.write_chunked)
				sendsocket(session->socket,newline,2);
		}
	}

    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* 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, 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* DLLCALL 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(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;
	
	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_writefunc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, BOOL writeln)
	http_session_t* session;

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

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

	if((!session->req.prev_write) && (!session->req.sent_headers)) {
		if(session->http_ver>=HTTP_1_1 && session->req.keep_alive) {
			session->req.write_chunked=TRUE;
			if(!ssjs_send_headers(session))
				return(JS_FALSE);
		}
		else {
			/* "Fast Mode" requested? */
			jsval		val;
			JSObject*	reply;
			JS_GetProperty(cx, session->js_glob, "http_reply", &val);
			reply=JSVAL_TO_OBJECT(val);
			JS_GetProperty(cx, reply, "fast", &val);
			if(JSVAL_IS_BOOLEAN(val) && JSVAL_TO_BOOLEAN(val)) {
				session->req.keep_alive=FALSE;
				if(!ssjs_send_headers(session))
					return(JS_FALSE);
			}
		}
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			continue;
		if(JS_GetStringLength(str)<1 && !writeln)
			continue;
		if(session->req.sent_headers) {
			if(session->req.method!=HTTP_HEAD && session->req.method!=HTTP_OPTIONS) {
				if(session->req.write_chunked) {
					char	chstr[12];
					sprintf(chstr,"%X\r\n", JS_GetStringLength(str)+(writeln?2:0));
					sendsocket(session->socket, chstr, strlen(chstr));
				}
				sendsocket(session->socket, JS_GetStringBytes(str), JS_GetStringLength(str));
				if(writeln)
					sendsocket(session->socket, newline, 2);
				if(session->req.write_chunked)
					sendsocket(session->socket, newline, 2);
			}
			fwrite(JS_GetStringBytes(str),1,JS_GetStringLength(str),session->req.fp);
			if(writeln)
				fwrite(newline,1,2,session->req.fp);
		}
	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);

js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	http_session_t* session;

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

	js_writefunc(cx, obj, argc, argv, rval,FALSE);
	return(JS_TRUE);
}

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

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

	js_writefunc(cx, obj, argc, argv, rval,TRUE);
static JSBool
js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char		str[512];
    uintN		i=0;