Newer
Older
case HEAD_COOKIE:
if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
char *key;
char *val;
p=value;
while((key=strtok_r(p,"=",&last))!=NULL) {
p=NULL;
if((val=strtok_r(p,";\t\n\v\f\r ",&last))!=NULL) { /* Whitespace */
js_add_cookieval(session,key,val);
}
}
}
break;
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
default:
break;
}
}
}
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 int is_dynamic_req(http_session_t* session)
{
char drive[4];
char cgidrive[4];
char dir[MAX_PATH+1];
char cgidir[MAX_PATH+1];
char fname[MAX_PATH+1];
char ext[MAX_PATH+1];
check_extra_path(session);
_splitpath(session->req.physical_path, drive, dir, fname, ext);
if(stricmp(ext,startup->ssjs_ext)==0)
i=IS_SSJS;
else if(get_xjs_handler(ext,session))
i=IS_SSJS;
else if(stricmp(ext,startup->js_ext)==0)
i=IS_JS;
if(!(startup->options&BBS_OPT_NO_JAVASCRIPT) && i) {
lprintf(LOG_INFO,"%04d Setting up JavaScript support", session->socket);
if(!js_setup(session)) {
lprintf(LOG_ERR,"%04d !ERROR setting up JavaScript support", session->socket);
return(IS_STATIC);
}
if(!(startup->options&WEB_OPT_NO_CGI)) {
for(i=0; startup->cgi_ext!=NULL && startup->cgi_ext[i]!=NULL; i++) {
if(stricmp(ext,startup->cgi_ext[i])==0) {
return(IS_CGI);
}
}
_splitpath(session->req.cgi_dir?session->req.cgi_dir:cgi_dir, cgidrive, cgidir, fname, ext);
if(stricmp(dir,cgidir)==0 && stricmp(drive,cgidrive)==0) {
}
}
return(IS_STATIC);
}
static char *get_request(http_session_t * session, char *req_line)
char* p;
char* retval;
SKIP_WHITESPACE(req_line);
SAFECOPY(session->req.virtual_path,req_line);
if(strtok_r(session->req.virtual_path," \t",&last))
retval=strtok_r(NULL," \t",&last);
else
retval=NULL;
SAFECOPY(session->req.request_line,session->req.virtual_path);
if(strtok_r(session->req.virtual_path,"?",&last))
query=strtok_r(NULL,"",&last);
else
query=NULL;
/* Must initialize physical_path before calling is_dynamic_req() */
SAFECOPY(session->req.physical_path,session->req.virtual_path);
unescape(session->req.physical_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);
SAFECOPY(session->req.vhost,session->req.host);
/* Remove port specification */
strtok_r(session->req.vhost,":",&last);
if(strtok_r(session->req.physical_path,"/",&last))
p=strtok_r(NULL,"/",&last);
else
p=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 */
);
}
SAFECOPY(session->req.query_str,query);
return(retval);
}
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");
return(NULL);
}
return(req_line+strlen(methods[i])+1);
}
}
if(req_line!=NULL && *req_line>=' ')
send_error(session,"501 Not Implemented");
return(NULL);
}
static BOOL get_request_headers(http_session_t * session)
{
char head_line[MAX_REQUEST_LINE+1];
char next_char;
char *value;
int i;
while(sockreadline(session,head_line,sizeof(head_line)-1)>0) {
/* Multi-line headers */
while((i=recv(session->socket,&next_char,1,MSG_PEEK))>0
&& (next_char=='\t' || next_char==' ')) {
if(i==-1 && ERROR_VALUE != EAGAIN)
close_socket(&session->socket);
i=strlen(head_line);
if(i>sizeof(head_line)-1) {
lprintf(LOG_ERR,"%04d !ERROR long multi-line header. The web server is broken!", session->socket);
i=sizeof(head_line)/2;
break;
}
sockreadline(session,head_line+i,sizeof(head_line)-i-1);
}
strListPush(&session->req.headers,head_line);
if((strtok_r(head_line,":",&last))!=NULL && (value=strtok_r(NULL,"",&last))!=NULL) {
i=get_header_type(head_line);
while(*value && *value<=' ') value++;
switch(i) {
case HEAD_HOST:
if(session->req.host[0]==0) {
SAFECOPY(session->req.host,value);
SAFECOPY(session->req.vhost,value);
/* Remove port part of host (Win32 doesn't allow : in dir names) */
/* Either an existing : will be replaced with a null, or nothing */
/* Will happen... the return value is not relevent here */
strtok_r(session->req.vhost,":",&last);
}
break;
default:
break;
}
}
}
if(!(session->req.vhost[0]))
SAFECOPY(session->req.vhost, startup->host_name);
if(!(session->req.host[0]))
SAFECOPY(session->req.host, startup->host_name);
return TRUE;
}
static BOOL get_fullpath(http_session_t * session)
{
char str[MAX_PATH+1];
if(session->req.vhost[0] && startup->options&WEB_OPT_VIRTUAL_HOSTS) {
safe_snprintf(str,sizeof(str),"%s/%s",root_dir,session->req.vhost);
safe_snprintf(str,sizeof(str),"%s/%s%s",root_dir,session->req.vhost,session->req.physical_path);
else
safe_snprintf(str,sizeof(str),"%s%s",root_dir,session->req.physical_path);
} else
safe_snprintf(str,sizeof(str),"%s%s",root_dir,session->req.physical_path);
if(FULLPATH(session->req.physical_path,str,sizeof(session->req.physical_path))==NULL) {
send_error(session,error_500);
return(FALSE);
}
return(TRUE);
}
static BOOL get_req(http_session_t * session, char *request_line)
req_line[0]=0;
if(request_line == NULL) {
/* Eat leaing blank lines... as apache does...
* "This is a legacy issue. The CERN webserver required POST data to have an extra
* CRLF following it. Thus many clients send an extra CRLF that is not included in the
* Content-Length of the request. Apache works around this problem by eating any empty
* lines which appear before a request."
* http://httpd.apache.org/docs/misc/known_client_problems.html
*/
while((len=sockreadline(session,req_line,sizeof(req_line)-1))==0);
if(len<0)
return(FALSE);
if(req_line[0])
lprintf(LOG_DEBUG,"%04d Request: %s",session->socket,req_line);
if(session->req.ld!=NULL && session->req.ld->request==NULL)
/* FREE()d in http_logging_thread() */
session->req.ld->request=strdup(req_line);
}
else {
lprintf(LOG_DEBUG,"%04d Handling Internal Redirect to: %s",session->socket,request_line);
SAFECOPY(req_line,request_line);
}
if(req_line[0]) {
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(!is_redir)
get_request_headers(session);
if(!get_fullpath(session)) {
send_error(session,error_500);
if(session->req.ld!=NULL && session->req.ld->vhost==NULL)
/* FREE()d in http_logging_thread() */
session->req.ld->vhost=strdup(session->req.vhost);
session->req.dynamic=is_dynamic_req(session);
if(session->req.query_str[0])
add_env(session,"QUERY_STRING",session->req.query_str);
add_env(session,"REQUEST_METHOD",methods[session->req.method]);
add_env(session,"SERVER_PROTOCOL",session->http_ver ?
http_vers[session->http_ver] : "HTTP/0.9");
send_error(session,"400 Bad Request");
return FALSE;
}
/* This may exist somewhere else - ToDo */
static char *find_last_slash(char *str)
{
#ifdef _WIN32
char * LastFSlash;
char * LastBSlash;
LastFSlash=strrchr(str,'/');
LastBSlash=strrchr(str,'\\');
if(LastFSlash==NULL)
return(LastBSlash);
if(LastBSlash==NULL)
return(LastFSlash);
if(LastBSlash < LastFSlash)
return(LastFSlash);
return(LastBSlash);
#else
return(strrchr(str,'/'));
#endif
}
/* This may exist somewhere else - ToDo */
static char *find_first_slash(char *str)
{
#ifdef _WIN32
char * FirstFSlash;
char * FirstBSlash;
FirstFSlash=strchr(str,'/');
FirstBSlash=strchr(str,'\\');
if(FirstFSlash==NULL)
return(FirstBSlash);
if(FirstBSlash==NULL)
return(FirstFSlash);
if(FirstBSlash > FirstFSlash)
return(FirstFSlash);
return(FirstBSlash);
#else
return(strchr(str,'/'));
#endif
}
static BOOL check_extra_path(http_session_t * session)
char *rp_slash;
char *vp_slash;
char rpath[MAX_PATH+1];
char vpath[MAX_PATH+1];
char epath[MAX_PATH+1];
char str[MAX_PATH+1];
struct stat sb;
int i;
char *end;
int use_epath=0;
epath[1]=0;
if(IS_PATH_DELIM(*lastchar(session->req.physical_path)) || stat(session->req.physical_path,&sb)==-1 /* && errno==ENOTDIR */)
{
SAFECOPY(vpath,session->req.virtual_path);
SAFECOPY(rpath,session->req.physical_path);
while((vp_slash=find_last_slash(vpath))!=NULL)
*vp_slash=0;
if((rp_slash=find_last_slash(rpath))==NULL)
return(FALSE);
SAFECOPY(str,epath);
if(*rp_slash)
sprintf(epath,"%s%s",rp_slash,str);
*(rp_slash+1)=0;
/* Check if this contains an index */
end=strchr(rpath,0);
if(use_epath || session->req.path_info_index || strchr(epath+1,'/')!=NULL) {
use_epath=1;
if(isdir(rpath) && !isdir(session->req.physical_path)) {
for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++) {
*end=0;
strcat(rpath,startup->index_file_name[i]);
if(!stat(rpath,&sb)) {
/* *end=0; /* Removed Wed, Aug 09, 2006 to allow is_dynamic_req to detect correctly */
SAFECOPY(session->req.extra_path_info,epath);
SAFECOPY(session->req.virtual_path,vpath);
strcat(session->req.virtual_path,"/");
SAFECOPY(session->req.physical_path,rpath);
return(TRUE);
}
/* rpath was an existing path and DID NOT contain an index. */
/* We do not allow scripts to mask existing dirs/files */
return(FALSE);
}
else {
if(isdir(rpath))
return(FALSE);
}
if(vp_slash==vpath)
return(FALSE);
/* Check if this is a script */
*rp_slash=0;
if(vp_slash!=vpath) {
if(stat(rpath,&sb)!=-1 && (!(sb.st_mode&S_IFDIR)))
{
SAFECOPY(session->req.extra_path_info,epath);
SAFECOPY(session->req.virtual_path,vpath);
SAFECOPY(session->req.physical_path,rpath);
return(TRUE);
}
}
}
}
return(FALSE);
}
static BOOL check_request(http_session_t * session)
{
char path[MAX_PATH+1];
char curdir[MAX_PATH+1];
char str[MAX_PATH+1];
char last_ch;
char* last_slash;
char filename[MAX_PATH+1];
char *spec;
str_list_t specs;
BOOL recheck_dynamic=FALSE;
if(session->req.finished)
return(FALSE);
SAFECOPY(path,session->req.physical_path);
lprintf(LOG_DEBUG,"%04d Path is: %s",session->socket,path);
if(isdir(path)) {
last_ch=*lastchar(path);
if(!IS_PATH_DELIM(last_ch)) {
strcat(session->req.physical_path,"/");
last_ch=*lastchar(session->req.virtual_path);
if(!IS_PATH_DELIM(last_ch)) {
strcat(session->req.virtual_path,"/");
}
last_slash=find_last_slash(path);
send_error(session,error_500);
last_slash++;
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]);
lprintf(LOG_DEBUG,"%04d Checking for %s",session->socket,path);
SAFECOPY(path,session->req.physical_path);
/* Don't send 404 unless authourized... prevent info leak */
if(startup->index_file_name==NULL || 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;
filename[0]=0;
}
else {
last_slash=find_last_slash(path);
if(last_slash==NULL)
last_slash=path;
else
last_slash++;
strcpy(filename,last_slash);
if(strnicmp(path,root_dir,strlen(root_dir))) {
session->req.keep_alive=FALSE;
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);
return(FALSE);
}
/* Set default ARS to a 0-length string */
session->req.ars[0]=0;
/* Walk up from root_dir checking for access.ars and webconfig.ini */
SAFECOPY(curdir,path);
last_slash=curdir+strlen(root_dir)-1;
/* Loop while there's more /s in path*/
while((last_slash=find_first_slash(p+1))!=NULL) {
/* Terminate the path after the slash */
*(last_slash+1)=0;
sprintf(str,"%saccess.ars",curdir);
/* NEVER serve up an access.ars file */
lprintf(LOG_WARNING,"%04d !WARNING! access.ars support is depreciated and will be REMOVED very soon.",session->socket);
lprintf(LOG_WARNING,"%04d !WARNING! access.ars found at %s.",session->socket,str);
if(!strcmp(path,str)) {
send_error(session,"403 Forbidden");
return(FALSE);
}
/* Read access.ars file */
if((file=fopen(str,"r"))!=NULL) {
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);
}
sprintf(str,"%swebctrl.ini",curdir);
if(!stat(str,&sb)) {
/* NEVER serve up a webctrl.ini file */
if(!strcmp(path,str)) {
send_error(session,"403 Forbidden");
return(FALSE);
}
/* Read webctrl.ars file */
if((file=fopen(str,"r"))!=NULL) {
specs=iniReadSectionList(file,NULL);
/* Read in globals */
if(iniReadString(file, NULL, "AccessRequirements", session->req.ars,str)==str)
SAFECOPY(session->req.ars,str);
if(iniReadString(file, NULL, "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, NULL, "ErrorDirectory", error_dir,str)==str) {
prep_dir(root_dir, str, sizeof(str));
/* FREE()d in close_request() */
session->req.error_dir=strdup(str);
}
if(iniReadString(file, NULL, "CGIDirectory", cgi_dir,str)==str) {
prep_dir(root_dir, str, sizeof(str));
/* FREE()d in close_request() */
session->req.cgi_dir=strdup(str);
recheck_dynamic=TRUE;
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, "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);
recheck_dynamic=TRUE;
session->req.path_info_index=iniReadBool(file, spec, "PathInfoIndex", FALSE);
}
free(spec);
}
iniFreeStringList(specs);
fclose(file);
if(session->req.path_info_index)
recheck_dynamic=TRUE;
}
else {
/* 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);
if(recheck_dynamic) {
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])
send404=TRUE;
if(!check_ars(session)) {
/* No authentication provided */
sprintf(str,"401 Unauthorized%s%s: Basic realm=\"%s\""
,newline,get_header(HEAD_WWWAUTH),session->req.realm?session->req.realm: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);
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)
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);
}
return(TRUE);
}
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];
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(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);
}
}
fclose(fp);
return(env_list);
}
static BOOL exec_cgi(http_session_t *session)
char cmdline[MAX_PATH+256];
/* ToDo: Damn, that's WAY too many variables */
int i=0;
int out_pipe[2];
int err_pipe[2];
fd_set read_set;
fd_set write_set;
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 done_wait=FALSE;
BOOL got_valid_headers=FALSE;
time_t start;
char cgipath[MAX_PATH+1];
char *p;
size_t idx;
BOOL set_chunked=FALSE;
SAFECOPY(cmdline,session->req.physical_path);
lprintf(LOG_INFO,"%04d Executing CGI: %s",session->socket,cmdline);
orig_keep=session->req.keep_alive;
/* Set up I/O pipes */
if(pipe(out_pipe)!=0) {
lprintf(LOG_ERR,"%04d Can't create out_pipe",session->socket);
return(FALSE);
}
if(pipe(err_pipe)!=0) {
lprintf(LOG_ERR,"%04d Can't create err_pipe",session->socket);
return(FALSE);
}
if((child=fork())==0) {
/* Do a full suid thing. */
if(startup->setuid!=NULL)
startup->setuid(TRUE);
/* Set up STDIO */
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);
}
/* Execute command */
if(get_cgi_handler(cgipath, sizeof(cgipath))) {
char* shell=os_cmdshell();
lprintf(LOG_INFO,"%04d Using handler %s to execute %s",session->socket,cgipath,cmdline);
execle(shell,shell,"-c",cgipath,NULL,env_list);
}
else {
execle(cmdline,cmdline,NULL,env_list);
}
lprintf(LOG_ERR,"%04d !FAILED! execle() (%d)",session->socket,errno);
exit(EXIT_FAILURE); /* Should never happen */
}
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 */
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;
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) {
int snt=0;
start=time(NULL);
if(session->req.method!=HTTP_HEAD) {
snt=writebuf(session,buf,i);
session->req.ld->size+=snt;
done_reading=TRUE;
}
else {
/* This is the tricky part */
i=pipereadline(out_pipe[0],buf,sizeof(buf), fbuf, sizeof(fbuf));
done_reading=TRUE;
got_valid_headers=FALSE;
}
start=time(NULL);
if(!done_parsing_headers && *buf) {
SAFECOPY(header,buf);
directive=strtok_r(header,":",&last);
if(directive != NULL) {
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)
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);
break;
case HEAD_TYPE:
got_valid_headers=TRUE;
strListPush(&session->req.dynamic_heads,buf);
break;
case HEAD_TRANSFER_ENCODING:
no_chunked=TRUE;
break;
default:
strListPush(&session->req.dynamic_heads,buf);
}
}
/* Invalid header line */
done_parsing_headers=TRUE;
}
}
else {
if(!no_chunked && session->http_ver>=HTTP_1_1) {
session->req.keep_alive=orig_keep;
set_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,set_chunked);
}
else {
/* Invalid headers... send 'er all as plain-text */
lprintf(LOG_DEBUG,"%04d Recieved invalid CGI headers, sending result as plain-text",session->socket);
/* 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);
/* Now send the tmpbuf */
for(i=0; tmpbuf != NULL && tmpbuf[i] != NULL; i++) {
snt=writebuf(session,tmpbuf[i],strlen(tmpbuf[i]));
session->req.ld->size+=snt;
}
}
}
if(strlen(fbuf)>0) {
snt=writebuf(session,fbuf,strlen(fbuf));
if(session->req.ld!=NULL && snt>0) {
session->req.ld->size+=snt;
}
done_parsing_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);
start=time(NULL);
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));
done_reading=TRUE;
start=0;
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 */