⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 http-cgi.c

📁 Serveez是一个服务器框架
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * http-cgi.c - http cgi implementation * * Copyright (C) 2000, 2001 Stefan Jahn <stefan@lkcc.org> * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. *  * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. *  * You should have received a copy of the GNU General Public License * along with this package; see the file COPYING.  If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA.   * * $Id: http-cgi.c,v 1.49 2001/08/03 18:09:04 ela Exp $ * */#if HAVE_CONFIG_H# include <config.h>#endif#if ENABLE_HTTP_PROTO#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#if HAVE_UNISTD_H# include <unistd.h>#endif#include <signal.h>#if HAVE_STRINGS_H# include <strings.h>#endif#ifdef __MINGW32__# include <winsock2.h># include <io.h># include <shellapi.h>#endif#ifndef __MINGW32__# include <netinet/in.h># if HAVE_WAIT_H#  include <wait.h># endif# if HAVE_SYS_WAIT_H#  include <sys/wait.h># endif#endif#include "libserveez.h"#include "http-proto.h"#include "http-core.h"#include "http-cgi.h"/* * Extended disconnect_socket callback for CGIs. Handling CGI related * topics and afterwards we process the normal http disconnection * functionality. */inthttp_cgi_disconnect (svz_socket_t *sock){  http_socket_t *http = sock->data;  /* flush CGI output if necessary */  if (sock->flags & SOCK_FLAG_PIPE && sock->send_buffer_fill > 0)    if (sock->write_socket)      sock->write_socket (sock);  /* close both of the CGI pipes if necessary */  if (sock->pipe_desc[READ] != INVALID_HANDLE)    {      if (closehandle (sock->pipe_desc[READ]) == -1)	svz_log (LOG_ERROR, "close: %s\n", SYS_ERROR);      sock->pipe_desc[READ] = INVALID_HANDLE;      sock->flags &= ~SOCK_FLAG_RECV_PIPE;    }  if (sock->pipe_desc[WRITE] != INVALID_HANDLE)    {      if (closehandle (sock->pipe_desc[WRITE]) == -1)	svz_log (LOG_ERROR, "close: %s\n", SYS_ERROR);      sock->pipe_desc[WRITE] = INVALID_HANDLE;      sock->flags &= ~SOCK_FLAG_SEND_PIPE;    }#ifdef __MINGW32__  /*    * Close the process handle if necessary, but only in the Windows-Port !    */  if (http->pid != INVALID_HANDLE)    {      if (!TerminateProcess (http->pid, 0))	svz_log (LOG_ERROR, "TerminateProcess: %s\n", SYS_ERROR);      if (closehandle (http->pid) == -1)	svz_log (LOG_ERROR, "CloseHandle: %s\n", SYS_ERROR);      http->pid = INVALID_HANDLE;    }#else /* not __MINGW32__ */  /*   * Try killing the cgi script.   */  if (http->pid != INVALID_HANDLE)    {      if (kill (http->pid, SIGKILL) == -1)	svz_log (LOG_ERROR, "kill: %s\n", SYS_ERROR);#if HAVE_WAITPID      /* Test if the cgi is still running and cleanup. */      else if (waitpid (http->pid, NULL, 0) == -1)	svz_log (LOG_ERROR, "waitpid: %s\n", SYS_ERROR);#endif /* not HAVE_WAITPID */      http->pid = INVALID_HANDLE;    }#endif /* not __MINGW32__ */  return http_disconnect (sock);}/* * This is the default idle function for http connections. It checks  * whether any died child was a cgi script. */inthttp_cgi_died (svz_socket_t *sock){  http_socket_t *http = sock->data;#ifdef __MINGW32__  DWORD result;#endif  if (sock->flags & SOCK_FLAG_PIPE)    {#ifndef __MINGW32__      /* Check if a died child is this cgi. */      if (svz_child_died && http->pid == svz_child_died)	{	  svz_log (LOG_NOTICE, "cgi script pid %d died\n", 		   (int) svz_child_died);	  svz_child_died = 0;	}#if HAVE_WAITPID      /* Test if the cgi is still running. */      if (waitpid (http->pid, NULL, WNOHANG) == http->pid)	{	  svz_log (LOG_NOTICE, "cgi script pid %d died\n", (int) http->pid);	  http->pid = INVALID_HANDLE;	}#endif /* HAVE_WAITPID */#else /* __MINGW32__ */      /*       * Check if there died a process handle in Win32, this has to be       * done regularly here because there is no SIGCHLD in Win32 !       */      if (http->pid != INVALID_HANDLE)	{	  result = WaitForSingleObject (http->pid, LEAST_WAIT_OBJECT);	  if (result == WAIT_FAILED)	    {	      svz_log (LOG_ERROR, "WaitForSingleObject: %s\n", SYS_ERROR);	    }	  else if (result != WAIT_TIMEOUT)	    {	      if (closehandle (http->pid) == -1)		svz_log (LOG_ERROR, "CloseHandle: %s\n", SYS_ERROR);	      svz_child_died = http->pid;	      http->pid = INVALID_HANDLE;	    }	}#endif /* __MINGW32__ */    }    sock->idle_counter = 1;  return 0;}/* * The http cgi reader gets data from the stdout of a cgi * program and stores the data into the send buffer of * the socket structure. We set the HTTP_FLAG_DONE flag * to indicate there was no more data. */inthttp_cgi_read (svz_socket_t *sock){  int do_read;  int num_read;  http_socket_t *http = sock->data;  /* read as much space is left in the buffer */  do_read = sock->send_buffer_size - sock->send_buffer_fill;  if (do_read <= 0)     {      return 0;    }#ifdef __MINGW32__  /* check how many bytes could be read from the cgi pipe */  if (!PeekNamedPipe (sock->pipe_desc[READ], NULL, 0, 		      NULL, (DWORD *) &num_read, NULL))    {      svz_log (LOG_ERROR, "cgi: PeekNamedPipe: %s\n", SYS_ERROR);      return -1;    }  /* adjust number of bytes to read */  if (do_read > num_read)    do_read = num_read;  /* really read from pipe */  if (!ReadFile (sock->pipe_desc[READ],		 sock->send_buffer + sock->send_buffer_fill,		 do_read, (DWORD *) &num_read, NULL))    {      svz_log (LOG_ERROR, "cgi: ReadFile: %s\n", SYS_ERROR);      num_read = -1;    }#else /* not __MINGW32__ */  if ((num_read = read (sock->pipe_desc[READ],			sock->send_buffer + sock->send_buffer_fill,			do_read)) == -1)    {      svz_log (LOG_ERROR, "cgi: read: %s\n", SYS_ERROR);      if (svz_errno == EAGAIN)	return 0;      num_read = -1;    }#endif /* not __MINGW32__ */  /* data has been read */  else if (num_read > 0)    {      http->length += num_read;      sock->send_buffer_fill += num_read;      return 0;    }#ifdef __MINGW32__  /*   * because pipes cannot be select()ed it can happen that there is no   * data within the receiving pipe, but the cgi has not yet terminated   */  if (num_read == 0 && http->pid != INVALID_HANDLE)    {      return 0;    }#endif /* __MINGW32__ */  /* no data has been received */  sock->userflags |= HTTP_FLAG_DONE;  if (sock->send_buffer_fill == 0)    {#if ENABLE_DEBUG      svz_log (LOG_DEBUG, "cgi: data successfully received and resent\n");#endif      sock->userflags &= ~HTTP_FLAG_CGI;      sock->flags &= ~SOCK_FLAG_RECV_PIPE;      return -1;    }    return 0;}/* * HTTP_CGI_WRITE pipes all read data from the http socket connection  * into the cgi stdin. This is necessary for the so called post method. * It directly reads from the RECV_BUFFER of the socket structure. */inthttp_cgi_write (svz_socket_t *sock){  int do_write;  int num_written;  http_socket_t *http = sock->data;  /*    * Write as many bytes as possible, remember how many   * were actually sent. Do not write more than the content   * length of the post data.   */  do_write = sock->recv_buffer_fill;  if (do_write > http->contentlength)    do_write = http->contentlength;#ifdef __MINGW32__  if (!WriteFile (sock->pipe_desc[WRITE], sock->recv_buffer, 		  do_write, (DWORD *) &num_written, NULL))    {      svz_log (LOG_ERROR, "cgi: WriteFile: %s\n", SYS_ERROR);      num_written = -1;    }#else /* !__MINGW32__ */  if ((num_written = write (sock->pipe_desc[WRITE], 			    sock->recv_buffer, do_write)) == -1)    {      svz_log (LOG_ERROR, "cgi: write: %s\n", SYS_ERROR);    }#endif /* !__MINGW32__ */  /* data has been successfully sent */  if (num_written > 0)    {      sock->last_send = time (NULL);      /*       * Shuffle the data in the output buffer around, so that       * new data can get stuffed into it.       */      if (sock->recv_buffer_fill > num_written)	{	  memmove (sock->recv_buffer, 		   sock->recv_buffer + num_written,		   sock->recv_buffer_fill - num_written);	}      sock->recv_buffer_fill -= num_written;      http->contentlength -= num_written;    }  /*    * If we have written all data to the CGI stdin, we can now start   * reading from the CGI's stdout and write again to the http   * connection.   */  if (http->contentlength <= 0)    {#if ENABLE_DEBUG      svz_log (LOG_DEBUG, "cgi: post data sent to cgi\n");#endif      sock->userflags &= ~HTTP_FLAG_POST;      sock->flags &= ~SOCK_FLAG_SEND_PIPE;      sock->userflags |= HTTP_FLAG_CGI;      sock->flags |= SOCK_FLAG_RECV_PIPE;      sock->read_socket = http_cgi_read;      sock->write_socket = http_default_write;    }  /*   * Return a non-zero value if an error occurred.   */  return (num_written < 0) ? -1 : 0;}/* * Create the environment block for a CGI script. Depending on the * system the environment is a field of null terminated char pointers * (for Unices) followed by a null pointer or one char pointer where * the variables a separated by zeros and the block is terminated * by a further zero. It returns the amount of defined variables. */static inthttp_create_cgi_envp (svz_socket_t *sock,  /* socket for this request */		      svz_envblock_t *env, /* env block */		      char *script,        /* the cgi script's filename */		      int type)            /* the cgi type */{  http_socket_t *http;  http_config_t *cfg = sock->cfg;  /* request type identifiers */  static char request_type[2][5] = { "POST", "GET" };  /*    * This data field is needed for the conversion of http request    * properties into environment variables.   */  static struct   {     char *property; /* property identifier */    char *env;      /* variable identifier */  }  env_var[] =  {    { "Content-length",  "CONTENT_LENGTH"       },    { "Content-type",    "CONTENT_TYPE"         },    { "Accept",          "HTTP_ACCEPT"          },    { "Referer",         "HTTP_REFERER"         },    { "User-Agent",      "HTTP_USER_AGENT"      },    { "Host",            "HTTP_HOST"            },    { "Connection",      "HTTP_CONNECTION"      },    { "Accept-Encoding", "HTTP_ACCEPT_ENCODING" },    { "Accept-Language", "HTTP_ACCEPT_LANGUAGE" },    { "Accept-Charset",  "HTTP_ACCEPT_CHARSET"  },    { NULL, NULL }  };  unsigned n;   int c;  /* setup default environment */  svz_envblock_default (env);  /* get http socket structure */  http = sock->data;  /* convert some http request properties into environment variables */  if (http->property)    for (c = 0; env_var[c].property; c++)      for (n = 0; http->property[n]; n += 2)	if (!svz_strcasecmp (http->property[n], env_var[c].property))	  {	    svz_envblock_add (env, "%s=%s", 			      env_var[c].env, http->property[n + 1]);	    break;	  }  /*    * set up some more environment variables which might be    * necessary for the cgi script   */  svz_envblock_add (env, "SERVER_NAME=%s", 		    cfg->host ? cfg->host : svz_inet_ntoa (sock->local_addr));  svz_envblock_add (env, "SERVER_PORT=%u", ntohs (sock->local_port));  svz_envblock_add (env, "REMOTE_ADDR=%s", http->host ? http->host :		    svz_inet_ntoa (sock->remote_addr));  svz_envblock_add (env, "REMOTE_PORT=%u", ntohs (sock->remote_port));  svz_envblock_add (env, "SCRIPT_NAME=%s%s", cfg->cgiurl, script);  svz_envblock_add (env, "GATEWAY_INTERFACE=%s", CGI_VERSION);  svz_envblock_add (env, "SERVER_PROTOCOL=%s", HTTP_VERSION);  svz_envblock_add (env, "SERVER_SOFTWARE=%s/%s", 		    svz_library, svz_version);  svz_envblock_add (env, "REQUEST_METHOD=%s", request_type[type]);  return env->size;}/* * Check the http option (the URL) for a cgi request. This routine * parses the text of the request and delivers the real file to be * invoked. This function makes sure that the cgi script file exists * and is executable. On success it delivers a pointer which must be * svz_free()ed after use. */char *http_check_cgi (svz_socket_t *sock, char *request){#ifndef __MINGW32__  struct stat buf;#endif  char *file;  int fd;  int size;  char *p;  char *saverequest;  int len;  http_config_t *cfg = sock->cfg;  /* check if the request is a real cgi request */  if (strstr (request, cfg->cgiurl) != request)    {      return HTTP_NO_CGI;    }  /*    * skip the CGI url and concate the script file itself, then   * check for trailing '?' which is the starting character for   * GET variables.   */  /* store the request in a local variable */  len = strlen (request) + 1 - strlen (cfg->cgiurl);  saverequest = svz_malloc (len);  strcpy (saverequest, request + strlen (cfg->cgiurl));  /* find the actual URL */  p = saverequest;  while (*p != '?' && *p != 0)    p++;  *p = 0;  size = strlen (cfg->cgidir) + len;  file = svz_malloc (size);  sprintf (file, "%s%s", cfg->cgidir, saverequest);  /* test if the file really exists and close it again */  if ((fd = open (file, O_RDONLY)) == -1)    {      svz_log (LOG_ERROR, "cgi: open: %s (%s)\n", SYS_ERROR, file);      svz_free (file);      svz_free (saverequest);      return NULL;    }#ifndef __MINGW32__  /* test the file being an executable */  if (fstat (fd, &buf) == -1)    {      svz_log (LOG_ERROR, "cgi: fstat: %s\n", SYS_ERROR);      close (fd);      svz_free (file);      svz_free (saverequest);      return NULL;    }  if (!(buf.st_mode & S_IFREG) ||      !(buf.st_mode & S_IXUSR) || !(buf.st_mode & S_IRUSR))      {      svz_log (LOG_ERROR, "cgi: no executable: %s\n", file);      close (fd);      svz_free (file);      svz_free (saverequest);      return NULL;    }#endif  if (close (fd) == -1)    svz_log (LOG_ERROR, "cgi: close: %s\n", SYS_ERROR);  /* return a pointer referring to the actual plain cgi file */  strcpy (file, saverequest);  file = svz_realloc (file, strlen (file) + 1);  svz_free (saverequest);  return file;}/* * Prepare the invocation of a cgi script which means to change to  * the referred directory and the creation of a valid environment * block. Return a NULL pointer on errors or a pointer to the full

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -