Logo Search packages:      
Sourcecode: webfs version File versions  Download package

cgi.c

/*
 * 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;
}

Generated by  Doxygen 1.6.0   Back to index