home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_cgi.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  17.4 KB  |  597 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /*
  59.  * http_script: keeps all script-related ramblings together.
  60.  * 
  61.  * Compliant to CGI/1.1 spec
  62.  * 
  63.  * Adapted by rst from original NCSA code by Rob McCool
  64.  *
  65.  * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
  66.  * custom error responses, and DOCUMENT_ROOT because we found it useful.
  67.  * It also adds SERVER_ADMIN - useful for scripts to know who to mail when 
  68.  * they fail.
  69.  */
  70.  
  71. #include "httpd.h"
  72. #include "http_config.h"
  73. #include "http_request.h"
  74. #include "http_core.h"
  75. #include "http_protocol.h"
  76. #include "http_main.h"
  77. #include "http_log.h"
  78. #include "util_script.h"
  79. #include "http_conf_globals.h"
  80.  
  81. module MODULE_VAR_EXPORT cgi_module;
  82.  
  83. /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
  84.  * in ScriptAliased directories, which means we need to know if this
  85.  * request came through ScriptAlias or not... so the Alias module
  86.  * leaves a note for us.
  87.  */
  88.  
  89. static int is_scriptaliased(request_rec *r)
  90. {
  91.     const char *t = ap_table_get(r->notes, "alias-forced-type");
  92.     return t && (!strcasecmp(t, "cgi-script"));
  93. }
  94.  
  95. /* Configuration stuff */
  96.  
  97. #define DEFAULT_LOGBYTES 10385760
  98. #define DEFAULT_BUFBYTES 1024
  99.  
  100. typedef struct {
  101.     char *logname;
  102.     long logbytes;
  103.     int bufbytes;
  104. } cgi_server_conf;
  105.  
  106. static void *create_cgi_config(pool *p, server_rec *s)
  107. {
  108.     cgi_server_conf *c =
  109.     (cgi_server_conf *) ap_pcalloc(p, sizeof(cgi_server_conf));
  110.  
  111.     c->logname = NULL;
  112.     c->logbytes = DEFAULT_LOGBYTES;
  113.     c->bufbytes = DEFAULT_BUFBYTES;
  114.  
  115.     return c;
  116. }
  117.  
  118. static void *merge_cgi_config(pool *p, void *basev, void *overridesv)
  119. {
  120.     cgi_server_conf *base = (cgi_server_conf *) basev, *overrides = (cgi_server_conf *) overridesv;
  121.  
  122.     return overrides->logname ? overrides : base;
  123. }
  124.  
  125. static const char *set_scriptlog(cmd_parms *cmd, void *dummy, char *arg)
  126. {
  127.     server_rec *s = cmd->server;
  128.     cgi_server_conf *conf =
  129.     (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
  130.  
  131.     conf->logname = arg;
  132.     return NULL;
  133. }
  134.  
  135. static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, char *arg)
  136. {
  137.     server_rec *s = cmd->server;
  138.     cgi_server_conf *conf =
  139.     (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
  140.  
  141.     conf->logbytes = atol(arg);
  142.     return NULL;
  143. }
  144.  
  145. static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, char *arg)
  146. {
  147.     server_rec *s = cmd->server;
  148.     cgi_server_conf *conf =
  149.     (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
  150.  
  151.     conf->bufbytes = atoi(arg);
  152.     return NULL;
  153. }
  154.  
  155. static const command_rec cgi_cmds[] =
  156. {
  157.     {"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1,
  158.      "the name of a log for script debugging info"},
  159.     {"ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1,
  160.      "the maximum length (in bytes) of the script debug log"},
  161.     {"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1,
  162.      "the maximum size (in bytes) to record of a POST request"},
  163.     {NULL}
  164. };
  165.  
  166. static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret,
  167.                int show_errno, char *error)
  168. {
  169.     FILE *f;
  170.     struct stat finfo;
  171.  
  172.     ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, r, 
  173.         "%s: %s", error, r->filename);
  174.  
  175.     if (!conf->logname ||
  176.     ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
  177.      &&   (finfo.st_size > conf->logbytes)) ||
  178.          ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
  179.               "a")) == NULL)) {
  180.     return ret;
  181.     }
  182.  
  183.     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
  184.     fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
  185.         r->args ? "?" : "", r->args ? r->args : "", r->protocol);
  186.     /* "%% 500 /usr/local/apache/cgi-bin */
  187.     fprintf(f, "%%%% %d %s\n", ret, r->filename);
  188.  
  189.     fprintf(f, "%%error\n%s\n", error);
  190.  
  191.     ap_pfclose(r->pool, f);
  192.     return ret;
  193. }
  194.  
  195. static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
  196.           char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err)
  197. {
  198.     array_header *hdrs_arr = ap_table_elts(r->headers_in);
  199.     table_entry *hdrs = (table_entry *) hdrs_arr->elts;
  200.     char argsbuffer[HUGE_STRING_LEN];
  201.     FILE *f;
  202.     int i;
  203.     struct stat finfo;
  204.  
  205.     if (!conf->logname ||
  206.     ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
  207.      &&   (finfo.st_size > conf->logbytes)) ||
  208.          ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
  209.               "a")) == NULL)) {
  210.     /* Soak up script output */
  211.     while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
  212.         continue;
  213.     while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
  214.         continue;
  215.     return ret;
  216.     }
  217.  
  218.     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
  219.     fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
  220.         r->args ? "?" : "", r->args ? r->args : "", r->protocol);
  221.     /* "%% 500 /usr/local/apache/cgi-bin" */
  222.     fprintf(f, "%%%% %d %s\n", ret, r->filename);
  223.  
  224.     fputs("%request\n", f);
  225.     for (i = 0; i < hdrs_arr->nelts; ++i) {
  226.     if (!hdrs[i].key)
  227.         continue;
  228.     fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
  229.     }
  230.     if ((r->method_number == M_POST || r->method_number == M_PUT)
  231.     && *dbuf) {
  232.     fprintf(f, "\n%s\n", dbuf);
  233.     }
  234.  
  235.     fputs("%response\n", f);
  236.     hdrs_arr = ap_table_elts(r->err_headers_out);
  237.     hdrs = (table_entry *) hdrs_arr->elts;
  238.  
  239.     for (i = 0; i < hdrs_arr->nelts; ++i) {
  240.     if (!hdrs[i].key)
  241.         continue;
  242.     fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
  243.     }
  244.  
  245.     if (sbuf && *sbuf)
  246.     fprintf(f, "%s\n", sbuf);
  247.  
  248.     if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
  249.     fputs("%stdout\n", f);
  250.     fputs(argsbuffer, f);
  251.     while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
  252.         fputs(argsbuffer, f);
  253.     fputs("\n", f);
  254.     }
  255.  
  256.     if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
  257.     fputs("%stderr\n", f);
  258.     fputs(argsbuffer, f);
  259.     while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
  260.         fputs(argsbuffer, f);
  261.     fputs("\n", f);
  262.     }
  263.  
  264.     ap_bclose(script_in);
  265.     ap_bclose(script_err);
  266.  
  267.     ap_pfclose(r->pool, f);
  268.     return ret;
  269. }
  270.  
  271. /****************************************************************
  272.  *
  273.  * Actual CGI handling...
  274.  */
  275.  
  276.  
  277. struct cgi_child_stuff {
  278.     request_rec *r;
  279.     int nph;
  280.     int debug;
  281.     char *argv0;
  282. };
  283.  
  284. static int cgi_child(void *child_stuff, child_info *pinfo)
  285. {
  286.     struct cgi_child_stuff *cld = (struct cgi_child_stuff *) child_stuff;
  287.     request_rec *r = cld->r;
  288.     char *argv0 = cld->argv0;
  289.     int child_pid;
  290.  
  291. #ifdef DEBUG_CGI
  292. #ifdef OS2
  293.     /* Under OS/2 need to use device con. */
  294.     FILE *dbg = fopen("con", "w");
  295. #else
  296.     FILE *dbg = fopen("/dev/tty", "w");
  297. #endif
  298.     int i;
  299. #endif
  300.  
  301.     char **env;
  302.  
  303.     RAISE_SIGSTOP(CGI_CHILD);
  304. #ifdef DEBUG_CGI
  305.     fprintf(dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n",
  306.         r->filename, cld->nph ? "NPH " : "", argv0);
  307. #endif
  308.  
  309.     ap_add_cgi_vars(r);
  310.     env = ap_create_environment(r->pool, r->subprocess_env);
  311.  
  312. #ifdef DEBUG_CGI
  313.     fprintf(dbg, "Environment: \n");
  314.     for (i = 0; env[i]; ++i)
  315.     fprintf(dbg, "'%s'\n", env[i]);
  316. #endif
  317.  
  318. #ifndef WIN32
  319.     ap_chdir_file(r->filename);
  320. #endif
  321.     if (!cld->debug)
  322.     ap_error_log2stderr(r->server);
  323.  
  324.     /* Transumute outselves into the script.
  325.      * NB only ISINDEX scripts get decoded arguments.
  326.      */
  327.  
  328.     ap_cleanup_for_exec();
  329.  
  330.     child_pid = ap_call_exec(r, pinfo, argv0, env, 0);
  331. #ifdef WIN32
  332.     return (child_pid);
  333. #else
  334.  
  335.     /* Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
  336.      * EARTH-shattering kaboom!
  337.      *
  338.      * Oh, well.  Muddle through as best we can...
  339.      *
  340.      * Note that only stderr is available at this point, so don't pass in
  341.      * a server to aplog_error.
  342.      */
  343.  
  344.     ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed", r->filename);
  345.     exit(0);
  346.     /* NOT REACHED */
  347.     return (0);
  348. #endif
  349. }
  350.  
  351. static int cgi_handler(request_rec *r)
  352. {
  353.     int retval, nph, dbpos = 0;
  354.     char *argv0, *dbuf = NULL;
  355.     BUFF *script_out, *script_in, *script_err;
  356.     char argsbuffer[HUGE_STRING_LEN];
  357.     int is_included = !strcmp(r->protocol, "INCLUDED");
  358.     void *sconf = r->server->module_config;
  359.     cgi_server_conf *conf =
  360.     (cgi_server_conf *) ap_get_module_config(sconf, &cgi_module);
  361.  
  362.     struct cgi_child_stuff cld;
  363.  
  364.     if (r->method_number == M_OPTIONS) {
  365.     /* 99 out of 100 CGI scripts, this is all they support */
  366.     r->allowed |= (1 << M_GET);
  367.     r->allowed |= (1 << M_POST);
  368.     return DECLINED;
  369.     }
  370.  
  371.     if ((argv0 = strrchr(r->filename, '/')) != NULL)
  372.     argv0++;
  373.     else
  374.     argv0 = r->filename;
  375.  
  376.     nph = !(strncmp(argv0, "nph-", 4));
  377.  
  378.     if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
  379.     return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
  380.                    "Options ExecCGI is off in this directory");
  381.     if (nph && is_included)
  382.     return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
  383.                    "attempt to include NPH CGI script");
  384.  
  385. #if defined(OS2) || defined(WIN32)
  386.     /* Allow for cgi files without the .EXE extension on them under OS/2 */
  387.     if (r->finfo.st_mode == 0) {
  388.     struct stat statbuf;
  389.     char *newfile;
  390.  
  391.     newfile = ap_pstrcat(r->pool, r->filename, ".EXE", NULL);
  392.  
  393.     if ((stat(newfile, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) {
  394.         return log_scripterror(r, conf, NOT_FOUND, 0,
  395.                    "script not found or unable to stat");
  396.     } else {
  397.         r->filename = newfile;
  398.     }
  399.     }
  400. #else
  401.     if (r->finfo.st_mode == 0)
  402.     return log_scripterror(r, conf, NOT_FOUND, APLOG_NOERRNO,
  403.                    "script not found or unable to stat");
  404. #endif
  405.     if (S_ISDIR(r->finfo.st_mode))
  406.     return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
  407.                    "attempt to invoke directory as script");
  408.     if (!ap_suexec_enabled) {
  409.     if (!ap_can_exec(&r->finfo))
  410.         return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
  411.                    "file permissions deny server execution");
  412.     }
  413.  
  414.     if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
  415.     return retval;
  416.  
  417.     ap_add_common_vars(r);
  418.     cld.argv0 = argv0;
  419.     cld.r = r;
  420.     cld.nph = nph;
  421.     cld.debug = conf->logname ? 1 : 0;
  422.  
  423. #ifdef CHARSET_EBCDIC
  424.     /* XXX:@@@ Is the generated/included output ALWAYS in text/ebcdic format? */
  425.     /* Or must we check the Content-Type first? */
  426.     ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
  427. #endif /*CHARSET_EBCDIC*/
  428.  
  429.     /*
  430.      * we spawn out of r->main if it's there so that we can avoid
  431.      * waiting for free_proc_chain to cleanup in the middle of an
  432.      * SSI request -djg
  433.      */
  434.     if (!ap_bspawn_child(r->main ? r->main->pool : r->pool, cgi_child,
  435.              (void *) &cld, kill_after_timeout,
  436.              &script_out, &script_in, &script_err)) {
  437.     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  438.             "couldn't spawn child process: %s", r->filename);
  439.     return HTTP_INTERNAL_SERVER_ERROR;
  440.     }
  441.  
  442.     /* Transfer any put/post args, CERN style...
  443.      * Note that if a buggy script fails to read everything we throw
  444.      * at it, or a buggy client sends too much, we get a SIGPIPE, so
  445.      * we have to ignore SIGPIPE while doing this.  CERN does the same
  446.      * (and in fact, they pretty nearly guarantee themselves a SIGPIPE
  447.      * on every invocation by chasing the real client data with a
  448.      * spurious newline).
  449.      */
  450.  
  451.     if (ap_should_client_block(r)) {
  452.     void (*handler) (int);
  453.     int dbsize, len_read;
  454.  
  455.     if (conf->logname) {
  456.         dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1);
  457.         dbpos = 0;
  458.     }
  459.  
  460.     ap_hard_timeout("copy script args", r);
  461. #ifdef SIGPIPE
  462.     handler = signal(SIGPIPE, SIG_IGN);
  463. #endif
  464.  
  465.     while ((len_read =
  466.         ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) {
  467.         if (conf->logname) {
  468.         if ((dbpos + len_read) > conf->bufbytes) {
  469.             dbsize = conf->bufbytes - dbpos;
  470.         }
  471.         else {
  472.             dbsize = len_read;
  473.         }
  474.         memcpy(dbuf + dbpos, argsbuffer, dbsize);
  475.         dbpos += dbsize;
  476.         }
  477.         ap_reset_timeout(r);
  478.         if (ap_bwrite(script_out, argsbuffer, len_read) < len_read) {
  479.         /* silly script stopped reading, soak up remaining message */
  480.         while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) {
  481.             /* dump it */
  482.         }
  483.         break;
  484.         }
  485.     }
  486.  
  487.     ap_bflush(script_out);
  488.     signal(SIGPIPE, handler);
  489.  
  490.     ap_kill_timeout(r);
  491.     }
  492.  
  493.     ap_bclose(script_out);
  494.  
  495.     /* Handle script return... */
  496.     if (script_in && !nph) {
  497.     const char *location;
  498.     char sbuf[MAX_STRING_LEN];
  499.     int ret;
  500.  
  501.     if ((ret = ap_scan_script_header_err_buff(r, script_in, sbuf))) {
  502.         return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
  503.     }
  504.  
  505. #ifdef CHARSET_EBCDIC
  506.         /* Now check the Content-Type to decide if conversion is needed */
  507.         ap_checkconv(r);
  508. #endif /*CHARSET_EBCDIC*/
  509.  
  510.     location = ap_table_get(r->headers_out, "Location");
  511.  
  512.     if (location && location[0] == '/' && r->status == 200) {
  513.  
  514.         /* Soak up all the script output */
  515.         ap_hard_timeout("read from script", r);
  516.         while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
  517.         continue;
  518.         }
  519.         while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
  520.         continue;
  521.         }
  522.         ap_kill_timeout(r);
  523.  
  524.  
  525.         /* This redirect needs to be a GET no matter what the original
  526.          * method was.
  527.          */
  528.         r->method = ap_pstrdup(r->pool, "GET");
  529.         r->method_number = M_GET;
  530.  
  531.         /* We already read the message body (if any), so don't allow
  532.          * the redirected request to think it has one.  We can ignore 
  533.          * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
  534.          */
  535.         ap_table_unset(r->headers_in, "Content-Length");
  536.  
  537.         ap_internal_redirect_handler(location, r);
  538.         return OK;
  539.     }
  540.     else if (location && r->status == 200) {
  541.         /* XX Note that if a script wants to produce its own Redirect
  542.          * body, it now has to explicitly *say* "Status: 302"
  543.          */
  544.         return REDIRECT;
  545.     }
  546.  
  547.     ap_send_http_header(r);
  548.     if (!r->header_only) {
  549.         ap_send_fb(script_in, r);
  550.     }
  551.     ap_bclose(script_in);
  552.  
  553.     ap_soft_timeout("soaking script stderr", r);
  554.     while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
  555.         continue;
  556.     }
  557.     ap_kill_timeout(r);
  558.     ap_bclose(script_err);
  559.     }
  560.  
  561.     if (script_in && nph) {
  562.     ap_send_fb(script_in, r);
  563.     }
  564.  
  565.     return OK;            /* NOT r->status, even if it has changed. */
  566. }
  567.  
  568. static const handler_rec cgi_handlers[] =
  569. {
  570.     {CGI_MAGIC_TYPE, cgi_handler},
  571.     {"cgi-script", cgi_handler},
  572.     {NULL}
  573. };
  574.  
  575. module MODULE_VAR_EXPORT cgi_module =
  576. {
  577.     STANDARD_MODULE_STUFF,
  578.     NULL,            /* initializer */
  579.     NULL,            /* dir config creater */
  580.     NULL,            /* dir merger --- default is to override */
  581.     create_cgi_config,        /* server config */
  582.     merge_cgi_config,        /* merge server config */
  583.     cgi_cmds,            /* command table */
  584.     cgi_handlers,        /* handlers */
  585.     NULL,            /* filename translation */
  586.     NULL,            /* check_user_id */
  587.     NULL,            /* check auth */
  588.     NULL,            /* check access */
  589.     NULL,            /* type_checker */
  590.     NULL,            /* fixups */
  591.     NULL,            /* logger */
  592.     NULL,            /* header parser */
  593.     NULL,            /* child_init */
  594.     NULL,            /* child_exit */
  595.     NULL            /* post read-request */
  596. };
  597.