Newer
Older
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;
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);
recheck_dynamic=TRUE;
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);
}
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)) {
unsigned *auth_list;
unsigned auth_list_len;
/* No authentication provided */
strcpy(str,"401 Unauthorized");
auth_list=parseEnumList(session->req.auth_list?session->req.auth_list:default_auth_list, ",", auth_type_names, &auth_list_len);
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)
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];
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);
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)
continue;
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);
}
}
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 status=0;
pid_t child=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;
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((p=get_cgi_handler(cmdline))!=NULL) {
char* shell=os_cmdshell();
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);
}
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 */
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);
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) {
snt=writebuf(session,buf,i);
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 */
if(!got_valid_headers) {
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));
return(FALSE);
return(TRUE);
/* Win32 exec_cgi() */
/* 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;
int set_chunked=FALSE;
/* Win32-specific */
char startup_dir[MAX_PATH+1];
int wr;
BOOL rd;
HANDLE rdpipe=INVALID_HANDLE_VALUE;
HANDLE wrpipe=INVALID_HANDLE_VALUE;
HANDLE rdoutpipe;
HANDLE wrinpipe;
DWORD waiting;
DWORD msglen;
DWORD retval;
BOOL process_terminated=FALSE;
PROCESS_INFORMATION process_info;
SECURITY_ATTRIBUTES sa;
STARTUPINFO startup_info={0};
str_list_t env_list;
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;
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);
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);
while(server_socket!=INVALID_SOCKET) {
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);
break;
}
/* 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);
}
}
waiting = 0;
PeekNamedPipe(
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) {
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);
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);
}
continue;
}
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;
set_chunked=TRUE;
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());
if(retval==STILL_ACTIVE) {
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);
if(!got_valid_headers)
lprintf(LOG_WARNING,"%04d !CGI Process %s did not generate valid headers"
,session->socket,getfname(cmdline));
if(!done_parsing_headers)
lprintf(LOG_WARNING,"%04d !CGI Process %s did not send data header termination"
,session->socket,getfname(cmdline));
return(TRUE);
/********************/
/* JavaScript stuff */
/********************/
JSObject* DLLCALL js_CreateHttpReplyObject(JSContext* cx
,JSObject* parent, http_session_t *session)
{
JSObject* reply;
JSObject* headers;
jsval val;
JSString* js_str;
/* Return existing object if it's already been created */
if(JS_GetProperty(cx,parent,"http_reply",&val) && val!=JSVAL_VOID) {
reply = JSVAL_TO_OBJECT(val);
JS_ClearScope(cx,reply);
}
else
reply = JS_DefineObject(cx, parent, "http_reply", NULL
, NULL, JSPROP_ENUMERATE|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)
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);
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);
/* 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);
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);
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;
int log_level;
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);
else
file[0]=0;
if(report->lineno)
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;
warning="";
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);
}
static JSBool
js_writefunc(JSContext *cx, uintN argc, jsval *arglist, BOOL writeln)
uintN i;
JSString* str=NULL;
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) {
rc=JS_SUSPENDREQUEST(cx);
JS_RESUMEREQUEST(cx, rc);
return(JS_FALSE);
JS_RESUMEREQUEST(cx, rc);
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;
rc=JS_SUSPENDREQUEST(cx);
JS_RESUMEREQUEST(cx, rc);
return(JS_FALSE);
JS_RESUMEREQUEST(cx, rc);