mirror of
https://github.com/NishiOwO/ncsa-httpd.git
synced 2025-04-21 08:44:40 +00:00
725 lines
21 KiB
C
725 lines
21 KiB
C
/************************************************************************
|
|
* NCSA HTTPd Server
|
|
* Software Development Group
|
|
* National Center for Supercomputing Applications
|
|
* University of Illinois at Urbana-Champaign
|
|
* 605 E. Springfield, Champaign, IL 61820
|
|
* httpd@ncsa.uiuc.edu
|
|
*
|
|
* Copyright (C) 1995, Board of Trustees of the University of Illinois
|
|
*
|
|
************************************************************************
|
|
*
|
|
* cgi.c,v 1.44 1996/04/05 18:54:31 blong Exp
|
|
*
|
|
************************************************************************
|
|
*
|
|
* cgi: keeps all script-related ramblings together.
|
|
*
|
|
* Based on NCSA HTTPd 1.3 by Rob McCool
|
|
*
|
|
* 03-07-95 blong
|
|
* Added support for variable REMOTE_GROUP from access files
|
|
*
|
|
* 03-20-95 sguillory
|
|
* Moved to more dynamic memory management of environment arrays
|
|
*
|
|
* 04-03-95 blong
|
|
* Added support for variables DOCUMENT_ROOT, ERROR_REQINFO->STATUS
|
|
* ERROR_URL, ERROR_REQUEST
|
|
*
|
|
* 04-20-95 blong
|
|
* Added Apache patch "B18" from Rob Hartill to allow nondelayed redirects
|
|
*
|
|
* 05-02-95 blong
|
|
* Since Apache is using REDIRECT_ as the env variables, I've decided to
|
|
* go with this in the interest of general Internet Harmony and Peace.
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
#include "portability.h"
|
|
|
|
#include <stdio.h>
|
|
#ifndef NO_STDLIB_H
|
|
# include <stdlib.h>
|
|
#endif /* NO_STDLIB_H */
|
|
#include <ctype.h>
|
|
#ifndef NO_MALLOC_H
|
|
# ifdef NEED_SYS_MALLOC_H
|
|
# include <sys/malloc.h>
|
|
# else
|
|
# include <malloc.h>
|
|
# endif /* NEED_SYS_MALLOC_H */
|
|
#endif /* NO_MALLOC_H */
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#include "constants.h"
|
|
#include "fdwrap.h"
|
|
#include "allocate.h"
|
|
#include "cgi.h"
|
|
#include "env.h"
|
|
#include "http_access.h"
|
|
#include "http_alias.h"
|
|
#include "http_auth.h"
|
|
#include "http_config.h"
|
|
#include "http_include.h"
|
|
#include "http_log.h"
|
|
#include "http_mime.h"
|
|
#include "http_request.h"
|
|
#include "http_send.h"
|
|
#include "http_include.h"
|
|
#include "httpd.h"
|
|
#include "util.h"
|
|
|
|
|
|
int pid;
|
|
|
|
#ifdef KRB4
|
|
# include <krb.h>
|
|
extern AUTH_DAT kerb_kdata;
|
|
# include <string.h>
|
|
#endif /* KRB4 */
|
|
|
|
void kill_children(per_request *reqInfo) {
|
|
char errstr[MAX_STRING_LEN];
|
|
sprintf(errstr,"killing CGI process %d",pid);
|
|
log_error(errstr,reqInfo->hostInfo->error_log);
|
|
|
|
kill(pid,SIGTERM);
|
|
sleep(3); /* give them time to clean up */
|
|
kill(pid,SIGKILL);
|
|
waitpid(pid,NULL,0);
|
|
}
|
|
|
|
char **create_argv(per_request *reqInfo,char *av0) {
|
|
register int x,n;
|
|
char **av;
|
|
char *str1,*str2;
|
|
|
|
str1 = newString(HUGE_STRING_LEN,STR_TMP);
|
|
str2 = newString(HUGE_STRING_LEN,STR_TMP);
|
|
|
|
for(x=0,n=2;reqInfo->args[x];x++)
|
|
if(reqInfo->args[x] == '+') ++n;
|
|
|
|
if(!(av = (char **)malloc((n+1)*sizeof(char *)))) {
|
|
freeString(str1);
|
|
freeString(str2);
|
|
die(reqInfo,SC_NO_MEMORY,"create_argv");
|
|
}
|
|
av[0] = av0;
|
|
strcpy(str2,reqInfo->args);
|
|
for(x=1;x<n;x++) {
|
|
getword(str1,str2,'+');
|
|
unescape_url(str1);
|
|
escape_shell_cmd(str1);
|
|
if(!(av[x] = strdup(str1))) {
|
|
freeString(str1);
|
|
freeString(str2);
|
|
die(reqInfo,SC_NO_MEMORY,"create_argv");
|
|
}
|
|
}
|
|
av[n] = NULL;
|
|
freeString(str1);
|
|
freeString(str2);
|
|
return av;
|
|
}
|
|
|
|
void get_path_info(per_request *reqInfo, struct stat *finfo)
|
|
{
|
|
register int x,max;
|
|
char *str;
|
|
|
|
str = newString(HUGE_STRING_LEN,STR_TMP);
|
|
|
|
max=count_dirs(reqInfo->filename);
|
|
for(x=dirs_in_alias;x<=max;x++) {
|
|
make_dirstr(reqInfo->filename,x+1,str);
|
|
if(!(stat(str,finfo))) {
|
|
if(S_ISREG(finfo->st_mode)) {
|
|
int l=strlen(str);
|
|
strcpy(reqInfo->path_info,&(reqInfo->filename[l]));
|
|
reqInfo->filename[l] = '\0';
|
|
reqInfo->url[strlen(reqInfo->url) - strlen(reqInfo->path_info)] = '\0';
|
|
freeString(str);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
for(x=dirs_in_alias - 1; (x > 0) ;--x) {
|
|
make_dirstr(reqInfo->filename,x+1,str);
|
|
if(!(stat(str,finfo))) {
|
|
if(S_ISREG(finfo->st_mode)) {
|
|
strcpy(reqInfo->path_info,&(reqInfo->filename[strlen(str)]));
|
|
strcpy(reqInfo->filename,str);
|
|
reqInfo->url[strlen(reqInfo->url) - strlen(reqInfo->path_info)] = '\0';
|
|
freeString(str);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
log_reason(reqInfo,"script does not exist",reqInfo->filename);
|
|
freeString(str);
|
|
die(reqInfo,SC_NOT_FOUND,reqInfo->url);
|
|
}
|
|
|
|
int add_cgi_vars(per_request *reqInfo, int *content)
|
|
{
|
|
char *str;
|
|
|
|
str = newString(HUGE_STRING_LEN,STR_TMP);
|
|
|
|
make_env_str(reqInfo,"GATEWAY_INTERFACE","CGI/1.1");
|
|
|
|
make_env_str(reqInfo,"SERVER_PROTOCOL",
|
|
protocals[reqInfo->http_version]);
|
|
make_env_str(reqInfo, "REQUEST_METHOD",
|
|
methods[reqInfo->method]);
|
|
|
|
make_env_str(reqInfo,"SCRIPT_NAME",reqInfo->url);
|
|
if(reqInfo->path_info[0]) {
|
|
make_env_str(reqInfo,"PATH_INFO",reqInfo->path_info);
|
|
translate_name(reqInfo,reqInfo->path_info,str);
|
|
make_env_str(reqInfo,"PATH_TRANSLATED",str);
|
|
}
|
|
make_env_str(reqInfo,"QUERY_STRING",reqInfo->args);
|
|
|
|
if(content) {
|
|
*content=0;
|
|
if ((reqInfo->method == M_POST) || (reqInfo->method == M_PUT)) {
|
|
*content=1;
|
|
sprintf(str,"%d",reqInfo->inh_content_length);
|
|
make_env_str(reqInfo,"CONTENT_TYPE",reqInfo->inh_content_type);
|
|
make_env_str(reqInfo,"CONTENT_LENGTH",str);
|
|
}
|
|
}
|
|
|
|
freeString(str);
|
|
return TRUE;
|
|
}
|
|
|
|
int add_common_vars(per_request *reqInfo) {
|
|
char *env_path,*env_tz;
|
|
char *str;
|
|
|
|
str = newString(HUGE_STRING_LEN,STR_TMP);
|
|
|
|
if(!(env_path = getenv("PATH")))
|
|
env_path=DEFAULT_PATH;
|
|
make_env_str(reqInfo,"PATH",env_path);
|
|
if((env_tz = getenv("TZ")))
|
|
make_env_str(reqInfo,"TZ",env_tz);
|
|
make_env_str(reqInfo,"SERVER_SOFTWARE",SERVER_VERSION);
|
|
make_env_str(reqInfo,"SERVER_NAME",reqInfo->hostInfo->server_hostname);
|
|
make_env_str(reqInfo,"SERVER_ADMIN",reqInfo->hostInfo->server_admin);
|
|
|
|
sprintf(str,"%d",port);
|
|
make_env_str(reqInfo,"SERVER_PORT",str);
|
|
|
|
make_env_str(reqInfo,"REMOTE_HOST",reqInfo->remote_name);
|
|
make_env_str(reqInfo,"REMOTE_ADDR",reqInfo->remote_ip);
|
|
make_env_str(reqInfo,"DOCUMENT_ROOT",reqInfo->hostInfo->document_root);
|
|
|
|
if(reqInfo->auth_user[0])
|
|
make_env_str(reqInfo,"REMOTE_USER",reqInfo->auth_user);
|
|
if(reqInfo->auth_group[0])
|
|
make_env_str(reqInfo,"REMOTE_GROUP",reqInfo->auth_group);
|
|
|
|
if(reqInfo->hostInfo->annotation_server[0])
|
|
make_env_str(reqInfo,"ANNOTATION_SERVER",
|
|
reqInfo->hostInfo->annotation_server);
|
|
|
|
if (reqInfo->auth_type[0]) {
|
|
#ifdef KRB4
|
|
if(strncmp(reqInfo->auth_type, "kerberos", 8) == 0) {
|
|
make_env_str(reqInfo,"AUTH_TYPE","KERB4_MUTUAL");
|
|
make_env_str(reqInfo,"KERB4_USER",kerb_kdata.pname);
|
|
make_env_str(reqInfo,"KERB4_INSTANCE",kerb_kdata.pinst);
|
|
make_env_str(reqInfo,"KERB4_REALM",kerb_kdata.prealm);
|
|
sprintf (str, "%s.%s@%s", kerb_kdata.pname,
|
|
kerb_kdata.pinst, kerb_kdata.prealm);
|
|
make_env_str(reqInfo,"KERB4_PRINCIPAL",str);
|
|
} else
|
|
#endif /* KRB4 */
|
|
make_env_str(reqInfo,"AUTH_TYPE",reqInfo->auth_type);
|
|
}
|
|
|
|
if(do_rfc931 && remote_logname)
|
|
make_env_str(reqInfo,"REMOTE_IDENT",remote_logname);
|
|
if (ErrorStat) {
|
|
if (failed_request[0])
|
|
make_env_str(reqInfo,"REDIRECT_REQUEST",failed_request);
|
|
if (failed_url[0])
|
|
make_env_str(reqInfo,"REDIRECT_URL",failed_url);
|
|
make_env_str(reqInfo,"REDIRECT_STATUS",set_stat_line(reqInfo));
|
|
}
|
|
|
|
freeString(str);
|
|
return TRUE;
|
|
}
|
|
|
|
int scan_cgi_header(per_request *reqInfo, int pd)
|
|
{
|
|
char *l;
|
|
int p;
|
|
int ret;
|
|
int options = 0;
|
|
char *str;
|
|
|
|
str = newString(HUGE_STRING_LEN,STR_TMP);
|
|
|
|
/* Don't do keepalive unless the script returns a content-length
|
|
header */
|
|
keep_alive.bKeepAlive = 0;
|
|
|
|
reqInfo->cgi_buf = new_sock_buf(reqInfo,pd);
|
|
cgibuf_count++;
|
|
|
|
/* ADC put in the G_SINGLE_CHAR option, so that CGI SSI's would work.
|
|
* it was:
|
|
* if((ret = getline(reqInfo->cgi_buf,str,HUGE_STRING_LEN-1,0,timeout)) <= 0)
|
|
*
|
|
* This should be cleaned up perhaps so that it only does this if SSI's are
|
|
* allowed for this script directory. ZZZZ
|
|
*/
|
|
#ifdef CGI_SSI_HACK
|
|
if (reqInfo->RequestFlags & DOING_SHTTP)
|
|
options = G_SINGLE_CHAR;
|
|
#endif /* CGI_SSI_HACK */
|
|
|
|
while(1) {
|
|
if((ret = getline(reqInfo->cgi_buf,str,HUGE_STRING_LEN-1,options,timeout)) <= 0)
|
|
{
|
|
char error_msg[MAX_STRING_LEN];
|
|
Close(pd);
|
|
freeString(str);
|
|
sprintf(error_msg,"HTTPd: malformed header from script %s",
|
|
reqInfo->filename);
|
|
die(reqInfo,SC_SERVER_ERROR,error_msg);
|
|
}
|
|
|
|
/* Always return zero, so as not to cause redirect+sleep3+kill */
|
|
if(str[0] == '\0') {
|
|
if (reqInfo->outh_content_type[0] == '\0') {
|
|
if (reqInfo->outh_location[0] != '\0') {
|
|
strcpy(reqInfo->outh_content_type,"text/html");
|
|
} else {
|
|
if (local_default_type[0] != '\0')
|
|
strcpy(reqInfo->outh_content_type,local_default_type);
|
|
else strcpy(reqInfo->outh_content_type,
|
|
reqInfo->hostInfo->default_type);
|
|
}
|
|
}
|
|
freeString(str);
|
|
return 0;
|
|
}
|
|
if(!(l = strchr(str,':')))
|
|
l = str;
|
|
*l++ = '\0';
|
|
if(!strcasecmp(str,"Content-type")) {
|
|
/* Thanks Netscape for showing this bug to everyone */
|
|
/* delete trailing whitespace, esp. for "server push" */
|
|
char *endp = l + strlen(l) - 1;
|
|
while ((endp > l) && isspace(*endp)) *endp-- = '\0';
|
|
sscanf(l,"%s",reqInfo->outh_content_type);
|
|
}
|
|
else if(!strcasecmp(str,"Location")) {
|
|
/* If we don't already have a status line, make one */
|
|
if (!reqInfo->status_line) {
|
|
reqInfo->status = SC_REDIRECT_TEMP;
|
|
set_stat_line(reqInfo);
|
|
}
|
|
sscanf(l,"%s",reqInfo->outh_location);
|
|
}
|
|
else if(!strcasecmp(str,"Status")) {
|
|
for(p=0;isspace(l[p]);p++);
|
|
sscanf(&l[p],"%d",&(reqInfo->status));
|
|
if(!(reqInfo->status_line = dupStringP(&l[p],STR_REQ))) {
|
|
Close(pd);
|
|
freeString(str);
|
|
die(reqInfo,SC_NO_MEMORY,"CGI: scan_cgi_header");
|
|
}
|
|
}
|
|
else if(!strcasecmp(str,"Content-length")) {
|
|
keep_alive.bKeepAlive = 1;
|
|
sscanf(l,"%d",&(reqInfo->outh_content_length));
|
|
}
|
|
else if(!strcasecmp(str,"WWW-Authenticate")) {
|
|
if (!reqInfo->status_line) {
|
|
reqInfo->status = SC_AUTH_REQUIRED;
|
|
set_stat_line(reqInfo);
|
|
}
|
|
strncpy(reqInfo->outh_www_auth,l,HUGE_STRING_LEN);
|
|
reqInfo->outh_www_auth[HUGE_STRING_LEN-1] = '\0';
|
|
}
|
|
else {
|
|
*(--l) = ':';
|
|
for(p=0;str[p];p++);
|
|
str[p] = LF;
|
|
str[++p] = '\0';
|
|
if(!(reqInfo->outh_cgi)) {
|
|
if(!(reqInfo->outh_cgi = strdup(str))) {
|
|
Close(pd);
|
|
freeString(str);
|
|
die(reqInfo,SC_NO_MEMORY,"CGI: scan_cgi_header");
|
|
}
|
|
}
|
|
else {
|
|
int loh = strlen(reqInfo->outh_cgi);
|
|
reqInfo->outh_cgi = (char *) realloc(reqInfo->outh_cgi,
|
|
(loh+strlen(str)+1)*sizeof(char));
|
|
if(!(reqInfo->outh_cgi)) {
|
|
Close(pd);
|
|
freeString(str);
|
|
die(reqInfo,SC_NO_MEMORY,"CGI: scan_cgi_header");
|
|
}
|
|
strcpy(&(reqInfo->outh_cgi[loh]),str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void internal_redirect(per_request *reqInfo)
|
|
{
|
|
char url[HUGE_STRING_LEN],args[HUGE_STRING_LEN],*argp;
|
|
per_request *newInfo;
|
|
|
|
url[0] = '\0';
|
|
args[0] = '\0';
|
|
|
|
/* Split the location header into URL and args */
|
|
strncpy(url,reqInfo->outh_location,HUGE_STRING_LEN);
|
|
if((argp = strchr(url,'?'))) {
|
|
*argp++ = '\0';
|
|
strcpy(args,argp);
|
|
}
|
|
log_transaction(reqInfo);
|
|
|
|
/* The global string the_request currently holds the request as
|
|
* read off the socket, and is used to log the information. We force
|
|
* it to this internal request here. Only redirects to GET requests
|
|
*/
|
|
sprintf(the_request,"GET ");
|
|
strncat(the_request,url,HUGE_STRING_LEN - strlen(the_request));
|
|
if (args[0] != '\0') {
|
|
strncat(the_request,"?",HUGE_STRING_LEN - strlen(the_request));
|
|
strncat(the_request,args, HUGE_STRING_LEN - strlen(the_request));
|
|
}
|
|
|
|
strncat(the_request," ",HUGE_STRING_LEN - strlen(the_request));
|
|
/* Replace the protocal with Internal to let people know it was an
|
|
* internal redirect.
|
|
*/
|
|
/* strncat(the_request, protocals[reqInfo->http_version],
|
|
HUGE_STRING_LEN - strlen(the_request)); */
|
|
strncat(the_request, "Internal",HUGE_STRING_LEN - strlen(the_request));
|
|
newInfo = continue_request(reqInfo, KEEP_AUTH | FORCE_GET);
|
|
newInfo->status = SC_DOCUMENT_FOLLOWS;
|
|
set_stat_line(newInfo);
|
|
strcpy(newInfo->url, url);
|
|
strcpy(newInfo->args, args);
|
|
construct_url(newInfo->outh_location,reqInfo->hostInfo,
|
|
reqInfo->outh_location);
|
|
process_request(newInfo);
|
|
}
|
|
|
|
int cgi_stub(per_request *reqInfo, struct stat *finfo, int allow_options)
|
|
{
|
|
int p[2], p2[2]; /* p = script-> server, p2 = server -> script */
|
|
int content, nph;
|
|
char *argv0;
|
|
char errlog[100];
|
|
FILE *fp = NULL;
|
|
|
|
if(!can_exec(finfo)) {
|
|
log_reason(reqInfo,
|
|
"client denied by server configuration (CGI non-executable)",
|
|
reqInfo->filename);
|
|
die(reqInfo,SC_FORBIDDEN,reqInfo->url);
|
|
}
|
|
|
|
if((argv0 = strrchr(reqInfo->filename,'/')) != NULL)
|
|
argv0++;
|
|
else argv0 = reqInfo->filename;
|
|
|
|
chdir_file(reqInfo->filename);
|
|
|
|
if(Pipe(p) < 0)
|
|
die(reqInfo,SC_SERVER_ERROR,"HTTPd/CGI: could not create IPC pipe");
|
|
if(Pipe(p2) < 0) {
|
|
Close(p[0]);
|
|
Close(p[1]);
|
|
die(reqInfo,SC_SERVER_ERROR,"HTTPd/CGI: could not create IPC pipe");
|
|
}
|
|
|
|
if((pid = fork()) < 0) {
|
|
Close(p[0]);
|
|
Close(p[1]);
|
|
Close(p2[0]);
|
|
Close(p2[1]);
|
|
sprintf(errlog,"HTTPd/CGI: could not fork new process, errno is %d",
|
|
errno);
|
|
die(reqInfo,SC_SERVER_ERROR,errlog);
|
|
}
|
|
|
|
nph = (strncmp(argv0,"nph-",4) ? 0 : 1);
|
|
if(!pid) {
|
|
Close(p[0]);
|
|
Close(p2[1]);
|
|
standalone = 0;
|
|
add_cgi_vars(reqInfo,&content);
|
|
|
|
/* TAKE OUT "if (nph)" THROUGH "else {" IF SHIT HAPPENS */
|
|
if (nph) {
|
|
if (fileno(reqInfo->out) != STDOUT_FILENO) {
|
|
dup2(fileno(reqInfo->out), STDOUT_FILENO);
|
|
close(fileno(reqInfo->out));
|
|
}
|
|
}
|
|
else
|
|
dup2(p[1],STDOUT_FILENO);
|
|
Close(p[1]);
|
|
dup2(p2[0],STDIN_FILENO);
|
|
Close(p2[0]);
|
|
|
|
/* Close the socket so the CGI program doesn't hold it open */
|
|
close(csd);
|
|
|
|
error_log2stderr(reqInfo->hostInfo->error_log);
|
|
/* To make the signal handling work on HPUX, according to
|
|
* David-Michael Lincke (dlincke@bandon.unisg.ch)
|
|
*/
|
|
#ifdef HPUX
|
|
signal(SIGCHLD, SIG_DFL);
|
|
#endif /* HPUX */
|
|
/* Only ISINDEX scripts get decoded arguments. */
|
|
if((!reqInfo->args[0]) || (ind(reqInfo->args,'=') >= 0)) {
|
|
execle(reqInfo->filename,argv0,NULL,reqInfo->env);
|
|
}
|
|
else {
|
|
execve(reqInfo->filename,create_argv(reqInfo,argv0),
|
|
reqInfo->env);
|
|
|
|
}
|
|
fprintf(stderr,"HTTPd/CGI: exec of %s failed, errno is %d\n",
|
|
reqInfo->filename,errno);
|
|
exit(1);
|
|
}
|
|
else {
|
|
Close(p[1]);
|
|
Close(p2[0]);
|
|
if (reqInfo->inh_content_length > 0) {
|
|
/* read content off socket and write to script */
|
|
char szBuf[HUGE_STRING_LEN];
|
|
int nBytes, nTotalBytes = 0;
|
|
int nDone = 0;
|
|
|
|
signal(SIGPIPE,SIG_IGN);
|
|
nBytes=getline(reqInfo->sb, szBuf,HUGE_STRING_LEN,G_FLUSH, timeout);
|
|
nTotalBytes = nBytes;
|
|
if (nBytes >= 0) {
|
|
if (nBytes > 0) write(p2[1], szBuf, nBytes);
|
|
while (!nDone && (nTotalBytes < reqInfo->inh_content_length)) {
|
|
nBytes=read(reqInfo->in, szBuf,HUGE_STRING_LEN);
|
|
if(nBytes < 1) {
|
|
break;
|
|
}
|
|
write(p2[1], szBuf, nBytes);
|
|
nTotalBytes += nBytes;
|
|
}
|
|
}
|
|
}
|
|
Close(p2[1]);
|
|
}
|
|
|
|
if(!nph) {
|
|
reqInfo->outh_content_type[0] = '\0';
|
|
|
|
scan_cgi_header(reqInfo,p[0]);
|
|
if(reqInfo->outh_location[0] == '/') {
|
|
Close(p[0]);
|
|
waitpid(pid,NULL,0);
|
|
internal_redirect(reqInfo);
|
|
return SC_REDIRECT_LOCAL;
|
|
}
|
|
reqInfo->outh_content_length = -1;
|
|
|
|
/* Previously, this was broken because we read the results of the CGI using
|
|
* getline, but the SSI parser used buffered stdio.
|
|
*
|
|
* ADC changed scan_cgi_header so that it uses G_SINGLE_CHAR when it
|
|
* calls getline. Yes, this means pitiful performance for CGI scripts.
|
|
*/
|
|
/* Fine, parse the output of CGI scripts. Talk about useless
|
|
* overhead. . .
|
|
*/
|
|
#ifdef CGI_SSI_HACK
|
|
if (!strcasecmp(reqInfo->outh_content_type, INCLUDES_MAGIC_TYPE) &&
|
|
(allow_options & OPT_INCLUDES)) {
|
|
strcpy(reqInfo->outh_content_type, "text/html");
|
|
if(reqInfo->http_version != P_HTTP_0_9)
|
|
send_http_header(reqInfo);
|
|
if(reqInfo->method != M_HEAD) {
|
|
rflush(reqInfo);
|
|
alarm(timeout);
|
|
add_include_vars(reqInfo,DEFAULT_TIME_FORMAT);
|
|
if (!(fp = FdOpen(p[0],"r"))) {
|
|
char errstr[MAX_STRING_LEN];
|
|
sprintf(errstr,"HTTPd/CGI/SSI: Could not fdopen() fildes, errno is %d.",errno);
|
|
die(reqInfo,SC_SERVER_ERROR,errstr);
|
|
}
|
|
send_parsed_content(reqInfo,fp,allow_options & OPT_EXECCGI);
|
|
}
|
|
}
|
|
else
|
|
#endif /* CGI_SSI_HACK */
|
|
{ /* Not Parsed, send normally */
|
|
if(reqInfo->http_version != P_HTTP_0_9)
|
|
send_http_header(reqInfo);
|
|
if(reqInfo->method != M_HEAD) {
|
|
/* Send a default body of text if the script
|
|
* failed to produce any, but ONLY for redirects
|
|
*/
|
|
if (!send_fd(reqInfo,p[0],NULL) && reqInfo->outh_location[0]) {
|
|
title_html(reqInfo,"Document moved");
|
|
rprintf(reqInfo,
|
|
"This document has temporarily moved <A HREF=\"%s\">here</A>.</P>%c",
|
|
reqInfo->outh_location,LF);
|
|
}
|
|
} else
|
|
kill_children(reqInfo);
|
|
}
|
|
}
|
|
else { /* Is nph- script */
|
|
reqInfo->bytes_sent = -1;
|
|
/* If there is KeepAlive going on, its handled internally to the
|
|
script. This means that we want to close the connection after
|
|
the nph- script has finished. */
|
|
keep_alive.bKeepAlive = 0;
|
|
}
|
|
|
|
waitpid(pid,NULL,0);
|
|
if (fp != NULL) FClose(fp); else Close(p[0]);
|
|
return SC_DOCUMENT_FOLLOWS;
|
|
}
|
|
|
|
/*
|
|
We'll make it return the number of bytes sent
|
|
so that we know if we need to send a body by default
|
|
*/
|
|
long send_fd(per_request *reqInfo, int pd, void (*onexit)(void))
|
|
{
|
|
char *buf;
|
|
register int n,w,o;
|
|
int fd;
|
|
|
|
buf = newString(IOBUFSIZE,STR_TMP);
|
|
|
|
signal(SIGALRM,send_fd_timed_out);
|
|
signal(SIGPIPE,send_fd_timed_out);
|
|
|
|
/* Flush stdio pipe, since scripts now use non buffered i/o */
|
|
rflush(reqInfo);
|
|
fd = fileno(reqInfo->out);
|
|
|
|
alarm(timeout);
|
|
if (reqInfo->cgi_buf != NULL)
|
|
n=getline(reqInfo->cgi_buf, buf,IOBUFSIZE,G_FLUSH,timeout);
|
|
else
|
|
n = 0;
|
|
while (1) {
|
|
o=0;
|
|
while(n) {
|
|
w = write(fd, buf + o,n);
|
|
|
|
if (w < 1) {
|
|
if (errno != EINTR) break;
|
|
}
|
|
n-=w;
|
|
o+=w;
|
|
reqInfo->bytes_sent += w;
|
|
}
|
|
if((n=read(pd, buf,IOBUFSIZE)) < 1) {
|
|
if (n == -1 && errno == EINTR) {
|
|
n = 0;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
alarm(0);
|
|
signal(SIGALRM,SIG_IGN);
|
|
signal(SIGPIPE,SIG_IGN);
|
|
freeString(buf);
|
|
return reqInfo->bytes_sent;
|
|
}
|
|
|
|
/* Called for ScriptAliased directories */
|
|
void exec_cgi_script(per_request *reqInfo) {
|
|
struct stat finfo;
|
|
int stub_returns;
|
|
int allow;
|
|
char allow_options;
|
|
|
|
get_path_info(reqInfo,&finfo);
|
|
|
|
evaluate_access(reqInfo,&finfo,&allow,&allow_options);
|
|
if(!allow) {
|
|
log_reason(reqInfo,
|
|
"client denied by server configuration (CGI)",reqInfo->filename);
|
|
/* unmunge_name(reqInfo,reqInfo->filename); */
|
|
die(reqInfo,SC_FORBIDDEN,reqInfo->url);
|
|
}
|
|
add_common_vars(reqInfo);
|
|
|
|
reqInfo->bytes_sent = 0;
|
|
stub_returns = cgi_stub(reqInfo,&finfo,allow_options);
|
|
|
|
switch (stub_returns) {
|
|
case SC_REDIRECT_TEMP:
|
|
die(reqInfo,SC_REDIRECT_TEMP,reqInfo->outh_location);
|
|
break;
|
|
case SC_REDIRECT_LOCAL:
|
|
break;
|
|
default:
|
|
log_transaction(reqInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Almost exactly equalivalent to exec_cgi_script, but this one
|
|
gets all of the path info passed to it, instead of calling get_path_info
|
|
and also gets the allow information passed to it instead of calling
|
|
evaluate_access
|
|
*/
|
|
|
|
void send_cgi(per_request *reqInfo,struct stat *finfo, char allow_options)
|
|
{
|
|
int stub_returns;
|
|
|
|
if (!(allow_options & OPT_EXECCGI)) {
|
|
log_reason(reqInfo,"client denied by server configuration (CGI)",
|
|
reqInfo->filename);
|
|
die(reqInfo,SC_FORBIDDEN,reqInfo->url);
|
|
}
|
|
add_common_vars(reqInfo);
|
|
|
|
reqInfo->bytes_sent = 0;
|
|
stub_returns = cgi_stub(reqInfo,finfo,allow_options);
|
|
|
|
switch (stub_returns) {
|
|
case SC_REDIRECT_TEMP:
|
|
die(reqInfo,SC_REDIRECT_TEMP,reqInfo->outh_location);
|
|
break;
|
|
case SC_REDIRECT_LOCAL:
|
|
break;
|
|
default:
|
|
log_transaction(reqInfo);
|
|
break;
|
|
}
|
|
}
|