Newer
Older
static int fastcgi_write_in(void *arg, char *buf, size_t bufsz)
{
struct fastcgi_header head;
struct fastcgi_data *cd = (struct fastcgi_data *)arg;
size_t pos;
size_t chunk_size;
for (pos = 0; pos < bufsz;) {
chunk_size = bufsz - pos;
if (chunk_size > UINT16_MAX)
chunk_size = UINT16_MAX;
head.len = htons(chunk_size);
if (sendsocket(cd->sock, (void *)&head, sizeof(head)) != sizeof(head))
return -1;
if (sendsocket(cd->sock, buf+pos, chunk_size) != chunk_size)
return -1;
pos += chunk_size;
}
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
return bufsz;
}
static int fastcgi_done_wait(void *arg)
{
struct fastcgi_data *cd = (struct fastcgi_data *)arg;
return (!socket_check(cd->sock, NULL, NULL, /* timeout: */0));
}
#ifdef __unix__
struct cgi_data {
int out_pipe; // out_pipe[0]
int err_pipe; // err_pipe[0]
pid_t child; // child
};
static int cgi_read_wait_timeout(void *arg)
{
int ret = 0;
int status=0;
int high_fd;
fd_set read_set;
fd_set write_set;
struct cgi_data *cd = (struct cgi_data *)arg;
struct timeval tv;
high_fd = cd->err_pipe;
if (cd->out_pipe > cd->err_pipe)
high_fd = cd->out_pipe;
tv.tv_sec=startup->max_cgi_inactivity;
tv.tv_usec=0;
FD_ZERO(&read_set);
FD_SET(cd->out_pipe,&read_set);
FD_SET(cd->err_pipe,&read_set);
FD_ZERO(&write_set);
if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0) {
if (FD_ISSET(cd->out_pipe,&read_set))
ret |= CGI_OUTPUT_READY;
if(FD_ISSET(cd->err_pipe,&read_set))
ret |= CGI_ERROR_READY;
if (waitpid(cd->child,&status,WNOHANG)==cd->child)
ret |= CGI_PROCESS_TERMINATED;
return ret;
}
static int cgi_read_out(void *arg, char *buf, size_t sz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
static int cgi_read_err(void *arg, char *buf, size_t sz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return read(cd->err_pipe,buf,sz);
}
static int cgi_readln_out(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return pipereadline(cd->out_pipe, buf, bufsz, fbuf, fbufsz);
}
static int cgi_write_in(void *arg, char *buf, size_t bufsz)
{
// *nix doesn't have an input pipe
return 0;
}
static int cgi_done_wait(void *arg)
{
int status=0;
struct cgi_data *cd = (struct cgi_data *)arg;
return waitpid(cd->child,&status,WNOHANG)==cd->child;
}
#else
struct cgi_data {
HANDLE rdpipe;
HANDLE wrpipe;
HANDLE child;
};
static int cgi_read_wait_timeout(void *arg)
{
int ret = 0;
struct cgi_data *cd = (struct cgi_data *)arg;
DWORD waiting;
time_t end = time(NULL) + startup->max_cgi_inactivity;
while(ret == 0) {
if(WaitForSingleObject(cd->child,0)==WAIT_OBJECT_0)
ret |= CGI_PROCESS_TERMINATED;
waiting = 0;
PeekNamedPipe(
cd->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)
ret |= CGI_OUTPUT_READY;
if(!session_check(cd->session, &rd, NULL, /* timeout: */0))
ret |= CGI_INPUT_READY;
if (rd)
ret |= CGI_INPUT_READY;
if (time(NULL) >= end)
break;
if (ret == 0)
Sleep(1);
}
return ret;
}
static int cgi_read_out(void *arg, char *buf, size_t sz)
{
DWORD msglen = 0;
struct cgi_data *cd = (struct cgi_data *)arg;
if(ReadFile(cd->rdpipe,buf,sz,&msglen,NULL)==FALSE) {
lprintf(LOG_ERR,"%04d !ERROR %d reading from pipe"
,cd->session->socket,GetLastError());
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
}
static int cgi_read_err(void *arg, char *buf, size_t sz)
{
// Win32 doesn't have an error pipe
return 0;
}
static int cgi_readln_out(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return pipereadline(cd->rdpipe, buf, bufsz, NULL, 0);
}
static int cgi_write_in(void *arg, char *buf, size_t bufsz)
{
int wr;
struct cgi_data *cd = (struct cgi_data *)arg;
WriteFile(cd->wrpipe, buf, bufsz, &wr, /* Overlapped: */NULL);
return wr;
}
static int cgi_done_wait(void *arg)
{
struct cgi_data *cd = (struct cgi_data *)arg;
return (WaitForSingleObject(cd->child,0)==WAIT_OBJECT_0);
}
#endif
struct cgi_api {
int (*read_wait_timeout)(void *arg);
int (*read_out)(void *arg, char *buf, size_t sz);
int (*read_err)(void *arg, char *buf, size_t sz);
int (*readln_out)(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz);
int (*write_in)(void *arg, char *buf, size_t bufsz);
int (*done_wait)(void *arg);
void *arg;
};
/*
* Need to return:
* Success/fail
* Timeout out or not
* Done parsing headers or not
* Got valid headers or not
* Process exited or not.
*/
static int do_cgi_stuff(http_session_t *session, struct cgi_api *cgi, BOOL orig_keep)
{
int ret = 0;
#define CGI_STUFF_FAILED (1<<0)
#define CGI_STUFF_TIMEDOUT (1<<1)
#define CGI_STUFF_DONE_PARSING (1<<2)
#define CGI_STUFF_VALID_HEADERS (1<<3)
#define CGI_STUFF_PROCESS_EXITED (1<<4)
int ready;
int i;
char cgi_status[MAX_REQUEST_LINE+1];
char header[MAX_REQUEST_LINE+1];
char buf[1024];
char fbuf[1026];
char *directive=NULL;
char *value=NULL;
char *last;
BOOL done_reading=FALSE;
BOOL done_wait=FALSE;
BOOL no_chunked=FALSE;
BOOL set_chunked=FALSE;
time_t start;
str_list_t tmpbuf;
start=time(NULL);
/* ToDo: Magically set done_parsing_headers for nph-* scripts */
cgi_status[0]=0;
while(!done_reading) {
ready = cgi->read_wait_timeout(cgi->arg);
if(ready) {
if(ready & CGI_OUTPUT_READY) {
if((ret & CGI_STUFF_DONE_PARSING) && (ret & CGI_STUFF_VALID_HEADERS)) {
i=cgi->read_out(cgi->arg,buf,sizeof(buf));
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=cgi->readln_out(cgi->arg, buf, sizeof(buf), fbuf, sizeof(fbuf));
done_reading=TRUE;
start=time(NULL);
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:
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:
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 */
}
}
else {
if(!no_chunked && session->http_ver>=HTTP_1_1) {
session->req.keep_alive=orig_keep;
if (session->req.method != HTTP_HEAD)
set_chunked=TRUE;
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;
}
}
}
if(ready & CGI_ERROR_READY) {
i=cgi->read_err(cgi->arg,buf,sizeof(buf)-1);
if(i>0) {
buf[i]=0;
lprintf(LOG_ERR,"%04d CGI Error: %s",session->socket,buf);
start=time(NULL);
if(ready & CGI_INPUT_READY) {
/* Send received POST Data to stdin of CGI process */
if((i=sess_recv(session, buf, sizeof(buf), 0)) > 0) {
lprintf(LOG_DEBUG,"%04d CGI Received %d bytes of POST data"
,session->socket, i);
cgi->write_in(cgi->arg, buf, i);
}
}
done_wait = TRUE;
}
if(!done_wait)
done_wait = cgi->done_wait(cgi->arg);
if((!(ready & (CGI_OUTPUT_READY|CGI_ERROR_READY))) && 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(session->req.physical_path));
done_reading=TRUE;
start=0;
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
return ret;
}
static BOOL exec_fastcgi(http_session_t *session)
{
int msglen;
BOOL orig_keep=FALSE;
SOCKET sock;
struct fastcgi_message *msg;
struct fastcgi_begin_request *br;
struct fastcgi_data cd;
struct cgi_api cgi = {
.read_wait_timeout = fastcgi_read_wait_timeout,
.read_out = fastcgi_read,
.read_err = fastcgi_read,
.readln_out = fastcgi_readln_out,
.write_in = fastcgi_write_in,
.done_wait = fastcgi_done_wait,
.arg = &cd
};
lprintf(LOG_INFO,"%04d Executing FastCGI: %s",session->socket,session->req.physical_path);
if (session->req.fastcgi_socket == NULL) {
lprintf(LOG_ERR, "%04d No FastCGI socket configured!",session->socket);
return FALSE;
}
orig_keep=session->req.keep_alive;
session->req.keep_alive=FALSE;
sock = fastcgi_connect(session->req.fastcgi_socket, session->socket);
if (sock == INVALID_SOCKET)
return FALSE;
// Set up request...
msglen = sizeof(struct fastcgi_header) + sizeof(struct fastcgi_begin_request);
msg = (struct fastcgi_message *)malloc(msglen);
if (msg == NULL) {
closesocket(sock);
lprintf(LOG_ERR, "%04d Failure to allocate memory for FastCGI message!", session->socket);
return FALSE;
}
fastcgi_init_header(&msg->head, FCGI_BEGIN_REQUEST);
msg->head.len = htons(sizeof(struct fastcgi_begin_request));
br = (struct fastcgi_begin_request *)&msg->body;
br->flags = 0;
memset(br->reserved, 0, sizeof(br->reserved));
if (sendsocket(sock, (void *)msg, msglen) != msglen) {
free(msg);
closesocket(sock);
lprintf(LOG_ERR, "%04d Failure to send to FastCGI socket!", session->socket);
return FALSE;
}
if (!fastcgi_send_params(sock, session)) {
free(msg);
closesocket(sock);
return FALSE;
}
// TODO handle stdin better
memset(&cd, 0, sizeof(cd));
cd.sock = sock;
fastcgi_write_in(&cd, session->req.post_data, session->req.post_len);
if (sendsocket(sock, (void *)msg, sizeof(struct fastcgi_header)) != sizeof(struct fastcgi_header)) {
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
free(msg);
closesocket(sock);
lprintf(LOG_ERR, "%04d Failure to send stdin to FastCGI socket!", session->socket);
return FALSE;
}
free(msg);
// Now handle stuff coming back from the FastCGI socket...
int ret = do_cgi_stuff(session, &cgi, orig_keep);
FREE_AND_NULL(cd.body);
closesocket(sock);
if(!(ret & CGI_STUFF_VALID_HEADERS)) {
lprintf(LOG_ERR,"%04d FastCGI Process did not generate valid headers");
return(FALSE);
}
if(!(ret & CGI_STUFF_DONE_PARSING)) {
lprintf(LOG_ERR,"%04d FastCGI Process did not send data header termination");
return(FALSE);
}
return TRUE;
}
static BOOL exec_cgi(http_session_t *session)
{
struct cgi_data cd;
struct cgi_api cgi = {
.read_wait_timeout = cgi_read_wait_timeout,
.read_out = cgi_read_out,
.read_err = cgi_read_err,
.readln_out = cgi_readln_out,
.write_in = cgi_write_in,
.done_wait = cgi_done_wait,
.arg = &cd
};
#ifdef __unix__
char cmdline[MAX_PATH+256];
int i=0;
int status=0;
pid_t child=0;
int out_pipe[2];
int err_pipe[2];
struct timeval tv={0,0};
fd_set read_set;
int high_fd=0;
char buf[1024];
BOOL done_parsing_headers=FALSE;
BOOL done_wait=FALSE;
BOOL got_valid_headers=FALSE;
time_t start;
char cgipath[MAX_PATH+1];
char *p;
BOOL orig_keep=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;
/* 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) {
str_list_t env_list;
/* Do a full suid thing. */
if(startup->setuid!=NULL)
startup->setuid(TRUE);
env_list=get_cgi_env(session);
/* 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 */
if(child==-1)
return(FALSE);
start=time(NULL);
high_fd=out_pipe[0];
if(err_pipe[0]>high_fd)
high_fd=err_pipe[0];
cd.out_pipe = out_pipe[0];
cd.err_pipe = err_pipe[0];
cd.child = child;
int ret = do_cgi_stuff(session, &cgi, orig_keep);
if (ret & CGI_STUFF_DONE_PARSING)
done_parsing_headers = TRUE;
if (ret & CGI_STUFF_PROCESS_EXITED)
done_wait = TRUE;
if (ret & CGI_STUFF_TIMEDOUT)
start = 1;
if (ret & CGI_STUFF_VALID_HEADERS)
got_valid_headers = TRUE;
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];
BOOL orig_keep;
BOOL done_parsing_headers=FALSE;
BOOL got_valid_headers=FALSE;
char *directive=NULL;
char *value=NULL;
int set_chunked=FALSE;
/* Win32-specific */
char startup_dir[MAX_PATH+1];
HANDLE rdpipe=INVALID_HANDLE_VALUE;
HANDLE wrpipe=INVALID_HANDLE_VALUE;
HANDLE rdoutpipe;
HANDLE wrinpipe;
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);
}
cd.wrpipe = wrpipe;
cd.rdpipe = rdpipe;
cd.child = process_info.hProcess;
int ret = do_cgi_stuff(session, &cgi, orig_keep);
if (ret & CGI_STUFF_DONE_PARSING)
done_parsing_headers = TRUE;
if (ret & CGI_STUFF_VALID_HEADERS)
got_valid_headers = TRUE;
if(GetExitCodeProcess(process_info.hProcess, &retval)==FALSE)
lprintf(LOG_ERR,"%04d !ERROR GetExitCodeProcess(%s) returned %d"
,session->socket,getfname(cmdline),GetLastError());
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) {