Skip to content
Snippets Groups Projects
websrvr.c 161 KiB
Newer Older
				if(iniReadString(file, NULL, "CGIDirectory", cgi_dir,str)==str) {
					prep_dir(root_dir, str, sizeof(str));
deuce's avatar
deuce committed
					FREE_AND_NULL(session->req.cgi_dir);
					/* FREE()d in close_request() */
					session->req.cgi_dir=strdup(str);
				if(iniReadString(file, NULL, "Authentication", default_auth_list,str)==str) {
					FREE_AND_NULL(session->req.auth_list);
					/* FREE()d in close_request() */
					session->req.auth_list=strdup(str);
				}
				session->req.path_info_index=iniReadBool(file, NULL, "PathInfoIndex", FALSE);
				/* Read in per-filespec */
				while((spec=strListPop(&specs))!=NULL) {
					if(wildmatch(filename,spec,TRUE)) {
						if(iniReadString(file, spec, "AccessRequirements", session->req.ars,str)==str)
							SAFECOPY(session->req.ars,str);
						if(iniReadString(file, spec, "Realm", scfg.sys_name,str)==str) {
							FREE_AND_NULL(session->req.realm);
							/* FREE()d in close_request() */
							session->req.realm=strdup(str);
						if(iniReadString(file, spec, "DigestRealm", scfg.sys_name,str)==str) {
							FREE_AND_NULL(session->req.digest_realm);
							/* FREE()d in close_request() */
							session->req.digest_realm=strdup(str);
						}
						if(iniReadString(file, spec, "ErrorDirectory", error_dir,str)==str) {
							FREE_AND_NULL(session->req.error_dir);
							prep_dir(root_dir, str, sizeof(str));
							/* FREE()d in close_request() */
							session->req.error_dir=strdup(str);
						}
						if(iniReadString(file, spec, "CGIDirectory", cgi_dir,str)==str) {
							FREE_AND_NULL(session->req.cgi_dir);
							prep_dir(root_dir, str, sizeof(str));
							/* FREE()d in close_request() */
							session->req.cgi_dir=strdup(str);
						if(iniReadString(file, spec, "Authentication", default_auth_list,str)==str) {
							FREE_AND_NULL(session->req.auth_list);
							/* FREE()d in close_request() */
							session->req.auth_list=strdup(str);
						}
						session->req.path_info_index=iniReadBool(file, spec, "PathInfoIndex", FALSE);
				iniFreeStringList(specs);
				if(session->req.path_info_index)
					recheck_dynamic=TRUE;
				/* If cannot open webctrl.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(curdir,path);
		session->req.dynamic=is_dynamic_req(session);
		if(session->req.dynamic)	/* Need to re-copy path here in case of re-checked PathInfoIndex change */
			SAFECOPY(path,session->req.physical_path);
	}
	if(!session->req.dynamic && session->req.extra_path_info[0])
		unsigned *auth_list;
		unsigned auth_list_len;
	
		strcpy(str,"401 Unauthorized");
		auth_list=parseEnumList(session->req.auth_list?session->req.auth_list:default_auth_list, ",", auth_type_names, &auth_list_len);
deuce's avatar
deuce committed
		for(i=0; ((unsigned)i)<auth_list_len; i++) {
			p=strchr(str,0);
			switch(auth_list[i]) {
				case AUTHENTICATION_BASIC:
					snprintf(p,sizeof(str)-(p-str),"%s%s: Basic realm=\"%s\""
							,newline,get_header(HEAD_WWWAUTH),session->req.realm?session->req.realm:scfg.sys_name);
					str[sizeof(str)-1]=0;
					break;
				case AUTHENTICATION_DIGEST:
					snprintf(p,sizeof(str)-(p-str),"%s%s: Digest realm=\"%s\", nonce=\"%s@%u\", qop=\"auth\"%s"
							,newline,get_header(HEAD_WWWAUTH),session->req.digest_realm?session->req.digest_realm:(session->req.realm?session->req.realm:scfg.sys_name),session->client.addr,time(NULL),session->req.auth.stale?", stale=true":"");
					str[sizeof(str)-1]=0;
					break;
			}
		}
		if(auth_list)
			free(auth_list);
		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);
			strcat(session->req.physical_path,session->req.extra_path_info);
			strcat(session->req.virtual_path,session->req.extra_path_info);
			send_error(session,error_404);
			return(FALSE);
		}
	if(session->req.range_start || session->req.range_end) {
		if(session->req.range_start < 0)
			session->req.range_start=sb.st_size-session->req.range_start;
		if(session->req.range_end < 0)
			session->req.range_end=sb.st_size-session->req.range_end;
		if(session->req.range_end >= sb.st_size)
deuce's avatar
deuce committed
			session->req.range_end=sb.st_size-1;
		if(session->req.range_end < session->req.range_start || session->req.dynamic) {
			send_error(session,error_416);
			return(FALSE);
		}
		if(session->req.range_start < 0 || session->req.range_end < 0) {
			send_error(session,error_416);
			return(FALSE);
		}
		if(session->req.range_start >= sb.st_size) {
			send_error(session,error_416);
			return(FALSE);
		}
		SAFECOPY(session->req.status,"206 Partial Content");
	}
	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		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];
	size_t		i;
	str_list_t	env_list;
	str_list_t	add_list;

deuce's avatar
deuce committed
	/* Return value */
	if((env_list=strListInit())==NULL)
		return(NULL);

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

deuce's avatar
deuce committed
	/* FREE()d in this block */
	if((add_list=iniGetSectionList(cgi_env,NULL))!=NULL) {

		for(i=0; add_list[i]!=NULL; i++) {
			if((deflt=getenv(add_list[i]))==NULL)
				deflt=iniGetString(cgi_env,add_list[i],"default",NULL,defltbuf);
			if(iniGetString(cgi_env,add_list[i],"value",deflt,value)==NULL)
			iniGetString(cgi_env,add_list[i],"append","",append);
			iniGetString(cgi_env,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);
		}
deuce's avatar
deuce committed
		iniFreeStringList(add_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;
	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((p=get_cgi_handler(cmdline))!=NULL) {
			lprintf(LOG_INFO,"%04d Using handler %s to execute %s",session->socket,p,cmdline);
			execle(shell,shell,"-c",p,cmdline,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
	/* FREE()d following this block */
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(i!=-1 && i!=0)  {
						if(session->req.method!=HTTP_HEAD) {
							if(session->req.ld!=NULL) {
								session->req.ld->size+=snt;
deuce's avatar
deuce committed
					i=pipereadline(out_pipe[0],buf,sizeof(buf), fbuf, sizeof(fbuf));
						done_reading=TRUE;
						got_valid_headers=FALSE;
					}
					if(!done_parsing_headers && *buf)  {
deuce's avatar
deuce committed
						if(tmpbuf != NULL)
							strListPush(&tmpbuf, fbuf);
						directive=strtok_r(header,":",&last);
							value=strtok_r(NULL,"",&last);
							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_TEMP;
									}
									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;
						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,set_chunked);
						}
						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,FALSE);
deuce's avatar
deuce committed

							/* Now send the tmpbuf */
							for(i=0; tmpbuf != NULL && tmpbuf[i] != NULL; i++) {
								if(strlen(tmpbuf[i])>0) {
									snt=writebuf(session,tmpbuf[i],strlen(tmpbuf[i]));
									if(session->req.ld!=NULL) {
										session->req.ld->size+=snt;
									}
								}
							}
							if(strlen(fbuf)>0) {
								snt=writebuf(session,fbuf,strlen(fbuf));
deuce's avatar
deuce committed
								if(session->req.ld!=NULL && snt>0) {
									session->req.ld->size+=snt;
								}
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);

	if(!done_wait)
		done_wait = (waitpid(child,&status,WNOHANG)==child);
	if(!done_wait)  {
		if(start)
			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);
		}
	}

	/* Drain STDERR & STDOUT */	
	tv.tv_sec=1;
	tv.tv_usec=0;
	FD_ZERO(&read_set);
	FD_SET(err_pipe[0],&read_set);
	FD_SET(out_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!=-1 && i!=0) {
				buf[i]=0;
				lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
				start=time(NULL);
			}
		}

		if(FD_ISSET(out_pipe[0],&read_set))  {
			i=read(out_pipe[0],buf,sizeof(buf));
			if(i!=-1 && i!=0)  {
				int snt=0;
				start=time(NULL);
				if(session->req.method!=HTTP_HEAD) {
					if(session->req.ld!=NULL) {
						session->req.ld->size+=snt;
					}
				}
			}
		}

		if(i==0 || i==-1)
			break;

		tv.tv_sec=1;
		tv.tv_usec=0;
		FD_ZERO(&read_set);
		FD_SET(err_pipe[0],&read_set);
		FD_SET(out_pipe[0],&read_set);
	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;
	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(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());
		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);
		/* Check socket for received POST Data */
		if(!socket_check(session->socket, &rd, NULL, /* timeout: */0)) {
			lprintf(LOG_WARNING,"%04d CGI Socket disconnected", session->socket);
			break;
		}
		if(rd) {
			/* Send received POST Data to stdin of CGI process */
			if((i=recv(session->socket, buf, sizeof(buf), 0)) > 0)  {
				lprintf(LOG_DEBUG,"%04d CGI Received %d bytes of POST data"
					,session->socket, i);
				WriteFile(wrpipe, buf, i, &wr, /* Overlapped: */NULL);
			}
		}

			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) {
rswindell's avatar
rswindell committed
				if((directive=strtok_r(header,":",&last))!=NULL)
					value=strtok_r(NULL,"",&last);
				else
					value="";
				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_TEMP;
							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;
			strListPush(&session->req.dynamic_heads,content_type);
			send_headers(session,cgi_status,set_chunked);
		}
		if(msglen) {
			lprintf(LOG_DEBUG,"%04d Sending %d bytes: %.*s"
				,session->socket,msglen,msglen,buf);
			wr=writebuf(session,buf,msglen);
			/* log actual bytes sent */
			if(session->req.ld!=NULL && wr>0)
				session->req.ld->size+=wr;	
		}
	}

    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 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;
	
	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)
		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.sent_headers) {
		if(session->req.method!=HTTP_HEAD && session->req.method!=HTTP_OPTIONS)
			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;
	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;
			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;
deuce's avatar
deuce committed
				if(!ssjs_send_headers(session,FALSE)) {