diff options
author | kraxel <kraxel> | 2004-06-08 12:51:57 +0000 |
---|---|---|
committer | kraxel <kraxel> | 2004-06-08 12:51:57 +0000 |
commit | 996cd0bab7bfff049402fe3eb3ca5de88d69ab41 (patch) | |
tree | 79d9e47fe25645bec085a0fecd1d7dda50a95918 /cgi.c | |
download | webfs-996cd0bab7bfff049402fe3eb3ca5de88d69ab41.tar.gz |
Initial revision
Diffstat (limited to 'cgi.c')
-rw-r--r-- | cgi.c | 257 |
1 files changed, 257 insertions, 0 deletions
@@ -0,0 +1,257 @@ +/* + * started writing (limited) CGI support for webfsd + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "httpd.h" + +/* ---------------------------------------------------------------------- */ + +extern char **environ; + +static char *env_wlist[] = { + "PATH", "HOME", + NULL +}; + +static void env_add(struct strlist **list, char *name, char *value) +{ + char *line; + + line = malloc(strlen(name) + strlen(value) + 2); + sprintf(line,"%s=%s",name,value); + if (debug) + fprintf(stderr,"cgi: env %s\n",line); + list_add(list,line,1); +} + +static char** +env_convert(struct strlist *list) +{ + struct strlist *elem; + char **env; + int i; + + for (i = 2, elem = list; NULL != elem; elem = elem->next) + i++; + env = malloc(sizeof(char*)*i); + for (i = 0, elem = list; NULL != elem; elem = elem->next) + env[i++] = elem->line; + env[i++] = NULL; + return env; +} + +static void env_copy(struct strlist **list) +{ + int i,j,l; + + for (i = 0; environ[i] != NULL; i++) { + for (j = 0; env_wlist[j] != NULL; j++) { + l = strlen(env_wlist[j]); + if (0 == strncmp(environ[i],env_wlist[j],l) && + environ[i][l] == '=') { + env_add(list,env_wlist[j],environ[i]+l+1); + break; + } + } + } +} + +/* ---------------------------------------------------------------------- */ + +void +cgi_request(struct REQUEST *req) +{ + struct sockaddr_storage addr; + struct strlist *env = NULL, *item; + char host[65],serv[9]; + char filename[1024], *h, *argv[2], envname[128]; + int pid,p[2],i,length; + + if (debug) + fprintf(stderr,"%03d: is cgi request\n",req->fd); + if (-1 == pipe(p)) { + mkerror(req,500,0); + return; + } + pid = fork(); + switch (pid) { + case -1: + /* error */ + if (debug) + perror("fork"); + mkerror(req,500,0); + return; + case 0: + break; + default: + /* parent - webfsd */ + close(p[1]); + req->cgipid = pid; + req->cgipipe = p[0]; + req->state = STATE_CGI_HEADER; + close_on_exec(req->cgipipe); + fcntl(req->cgipipe,F_SETFL,O_NONBLOCK); + return; + } + + /* -------- below is the child process (cgi) code -------- */ + + /* lookup local socket (before it gets closed) */ + length = sizeof(addr); + getsockname(req->fd,(struct sockaddr*)&addr,&length); + getnameinfo((struct sockaddr*)&addr,length,host,64,serv,8, + NI_NUMERICHOST | NI_NUMERICSERV); + + /* setup file descriptors */ + dup2(p[1],1); /* pipe -> stdout */ + if (have_tty) { + int devnull = open("/dev/null",O_RDWR); + dup2(devnull,0); /* stdin */ + dup2(devnull,2); /* stderr */ + close(devnull); + } else { + /* nothing -- already attached to /dev/null */ + } + close_on_exec(p[0]); + close_on_exec(p[1]); + + /* setup environment */ + env_copy(&env); + + env_add(&env,"DOCUMENT_ROOT",doc_root); + env_add(&env,"GATEWAY_INTERFACE","CGI/1.1"); + env_add(&env,"QUERY_STRING",req->query); + env_add(&env,"REQUEST_URI",req->uri); + env_add(&env,"REMOTE_ADDR",req->peerhost); + env_add(&env,"REMOTE_PORT",req->peerserv); + env_add(&env,"REQUEST_METHOD",req->type); + env_add(&env,"SERVER_ADMIN","root@localhost"); + env_add(&env,"SERVER_NAME",server_host); + env_add(&env,"SERVER_PROTOCOL","HTTP/1.1"); + env_add(&env,"SERVER_SOFTWARE",server_name); + env_add(&env,"SERVER_ADDR",host); + env_add(&env,"SERVER_PORT",serv); + + for (item = req->header; NULL != item; item = item->next) { + strcpy(envname,"HTTP_"); + if (1 != sscanf(item->line,"%120[-A-Za-z]: %n",envname+5,&length)) + continue; + for (i = 0; envname[i]; i++) { + if (isalpha(envname[i])) + envname[i] = toupper(envname[i]); + if ('-' == envname[i]) + envname[i] = '_'; + } + env_add(&env,envname,item->line+length); + } + + h = req->path + strlen(cgipath); + h = strchr(h,'/'); + if (h) { + env_add(&env,"PATH_INFO",h); + *h = 0; + } else { + env_add(&env,"PATH_INFO",""); + } + env_add(&env,"SCRIPT_NAME",req->path); + snprintf(filename,sizeof(filename)-1,"%s%s",doc_root,req->path); + env_add(&env,"SCRIPT_FILENAME",filename); + + /* start cgi app */ + argv[0] = filename; + argv[1] = NULL; + execve(filename,argv,env_convert(env)); + + /* exec failed ... */ + printf("Content-Type: text/plain\n" + "\n" + "execve %s: %s\n", + filename,strerror(errno)); + exit(1); +} + +/* ---------------------------------------------------------------------- */ + +void +cgi_read_header(struct REQUEST *req) +{ + struct strlist *list = NULL; + char *h,*next,*status = NULL; + int rc; + + restart: + rc = read(req->cgipipe, req->cgibuf+req->cgilen, MAX_HEADER-req->cgilen); + switch (rc) { + case -1: + if (errno == EAGAIN) + return; + if (errno == EINTR) + goto restart; + /* fall through */ + case 0: + mkerror(req,500,0); + return; + default: + req->cgilen += rc; + req->cgibuf[req->cgilen] = 0; + } + + /* header complete ?? */ + if (NULL != (h = strstr(req->cgibuf,"\r\n\r\n")) || + NULL != (h = strstr(req->cgibuf,"\n\n"))) { + + /* parse cgi header */ + for (h = req->cgibuf;; h = next) { + next = strstr(h,"\n"); + next[0] = 0; + if (next[-1] == '\r') + next[-1] = 0; + next++; + + if (0 == strlen(h)) + break; + if (debug) + fprintf(stderr,"%03d: cgi: hdr %s\n",req->fd,h); + if (0 == strncasecmp(h,"Status: ",8)) { + status = h+8; + if (debug) + fprintf(stderr,"%03d: cgi: status %s\n",req->fd,status); + continue; + } + if (0 == strncasecmp(h,"Server:",7) || + 0 == strncasecmp(h,"Connection:",11) || + 0 == strncasecmp(h,"Accept-Ranges:",14) || + 0 == strncasecmp(h,"Date:",5)) + /* webfsd adds them -- filter out */ + continue; + list_add(&list,h,0); + } + mkcgi(req, status ? status : "200 OK", list); + list_free(&list); + req->cgipos = next - req->cgibuf; + if (debug) + fprintf(stderr,"%03d: cgi: pos=%d len=%d\n",req->fd, + req->cgipos, req->cgilen); + return; + } + + if (req->cgilen == MAX_HEADER) { + mkerror(req,400,0); + return; + } + return; +} |