summaryrefslogtreecommitdiff
path: root/apps/netutils/thttpd/cgi-src/ssi.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/netutils/thttpd/cgi-src/ssi.c')
-rw-r--r--apps/netutils/thttpd/cgi-src/ssi.c957
1 files changed, 957 insertions, 0 deletions
diff --git a/apps/netutils/thttpd/cgi-src/ssi.c b/apps/netutils/thttpd/cgi-src/ssi.c
new file mode 100644
index 000000000..4afb141f9
--- /dev/null
+++ b/apps/netutils/thttpd/cgi-src/ssi.c
@@ -0,0 +1,957 @@
+/****************************************************************************
+ * netutils/thttpd/cgi-src/ssi.c
+ * Server-side-includes CGI program
+ *
+ * Copyright (C) 2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <spudmonkey@racsa.co.cr>
+ *
+ * Derived from the file of the same name in the original THTTPD package:
+ *
+ * Copyright © 1995 by Jef Poskanzer <jef@mail.acme.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <nuttx/regex.h>
+
+#include "config.h"
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+#define ST_GROUND 0
+#define ST_LESSTHAN 1
+#define ST_BANG 2
+#define ST_MINUS1 3
+#define ST_MINUS2 4
+
+#define SF_BYTES 0
+#define SF_ABBREV 1
+
+#define DI_CONFIG 0
+#define DI_INCLUDE 1
+#define DI_ECHO 2
+#define DI_FSIZE 3
+#define DI_FLASTMOD 4
+
+#define BUFFER_SIZE 512
+#define TIMEFMT_SIZE 80
+#define MAX_TAGS 32
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void read_file(FILE *instream, char *vfilename, char *filename);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static char *g_url;
+static char g_timeformat[TIMEFMT_SIZE];
+static char g_iobuffer1[BUFFER_SIZE];
+static char g_iobuffer2[BUFFER_SIZE];
+static char *g_tags[MAX_TAGS];
+static int g_sizefmt;
+static struct stat g_sb;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void internal_error(char *reason)
+{
+ char *title = "500 Internal Error";
+
+ (void)printf("\
+<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\
+<BODY><H2>%s</H2>\n\
+Something unusual went wrong during a server-side-includes request:\n\
+<BLOCKQUOTE>\n\
+%s\n\
+</BLOCKQUOTE>\n\
+</BODY></HTML>\n", title, title, reason);
+}
+
+static void not_found(char *filename)
+{
+ char *title = "404 Not Found";
+
+ (void)printf("\
+<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\
+<BODY><H2>%s</H2>\n\
+The requested server-side-includes filename, %s,\n\
+does not seem to exist.\n\
+</BODY></HTML>\n", title, title, filename);
+}
+
+static void not_found2(char *directive, char *tag, char *filename)
+{
+ char *title = "Not Found";
+
+ (void)printf("\
+<HR><H2>%s</H2>\n\
+The filename requested in a %s %s directive, %s,\n\
+does not seem to exist.\n\
+<HR>\n", title, directive, tag, filename);
+}
+
+static void not_permitted(char *directive, char *tag, char *val)
+{
+ char *title = "Not Permitted";
+
+ (void)printf("\
+<HR><H2>%s</H2>\n\
+The filename requested in the %s %s=%s directive\n\
+may not be fetched.\n\
+<HR>\n", title, directive, tag, val);
+}
+
+static void unknown_directive(char *filename, char *directive)
+{
+ char *title = "Unknown Directive";
+
+ (void)printf("\
+<HR><H2>%s</H2>\n\
+The requested server-side-includes filename, %s,\n\
+tried to use an unknown directive, %s.\n\
+<HR>\n", title, filename, directive);
+}
+
+static void unknown_tag(char *filename, char *directive, char *tag)
+{
+ char *title = "Unknown Tag";
+
+ (void)printf("\
+<HR><H2>%s</H2>\n\
+The requested server-side-includes filename, %s,\n\
+tried to use the directive %s with an unknown tag, %s.\n\
+<HR>\n", title, filename, directive, tag);
+}
+
+static void unknown_value(char *filename, char *directive, char *tag, char *val)
+{
+ char *title = "Unknown Value";
+
+ (void)printf("\
+<HR><H2>%s</H2>\n\
+The requested server-side-includes filename, %s,\n\
+tried to use the directive %s %s with an unknown value, %s.\n\
+<HR>\n", title, filename, directive, tag, val);
+}
+
+static int get_filename(char *vfilename, char *filename,
+ char *directive, char *tag, char *val, char *fn,
+ int fnsize)
+{
+ char *cp;
+ int vl;
+ int fl;
+
+ /* Used for the various commands that accept a file name. These commands
+ * accept two tags: virtual Gives a virtual path to a document on the
+ * server. file Gives a pathname relative to the current directory. ../
+ * cannot be used in this pathname, nor can absolute paths be used.
+ */
+
+ vl = strlen(vfilename);
+ fl = strlen(filename);
+
+ if (strcmp(tag, "virtual") == 0)
+ {
+ if (strstr(val, "../") != (char *)0)
+ {
+ not_permitted(directive, tag, val);
+ return -1;
+ }
+
+ /* Figure out root using difference between vfilename and filename. */
+
+ if (vl > fl || strcmp(vfilename, &filename[fl - vl]) != 0)
+ {
+ return -1;
+ }
+
+ if (fl - vl + strlen(val) >= fnsize)
+ {
+ return -1;
+ }
+
+ (void)strncpy(fn, filename, fl - vl);
+ (void)strcpy(&fn[fl - vl], val);
+ }
+ else if (strcmp(tag, "file") == 0)
+ {
+ if (val[0] == '/' || strstr(val, "../") != (char *)0)
+ {
+ not_permitted(directive, tag, val);
+ return -1;
+ }
+ if (fl + 1 + strlen(val) >= fnsize)
+ {
+ return -1;
+ }
+
+ (void)strcpy(fn, filename);
+ cp = strrchr(fn, '/');
+ if (cp == (char *)0)
+ {
+ cp = &fn[strlen(fn)];
+ *cp = '/';
+ }
+ (void)strcpy(++cp, val);
+ }
+ else
+ {
+ unknown_tag(filename, directive, tag);
+ return -1;
+ }
+ return 0;
+}
+
+static int check_filename(char *filename)
+{
+ static int inited = 0;
+ static char *cgi_pattern;
+#ifdef CONFIG_AUTH_FILE
+ struct stat sb;
+ char *dirname;
+ char *authname;
+ char *cp;
+ int fnl;
+ int r;
+#endif
+
+ if (!inited)
+ {
+ /* Get the cgi pattern. */
+
+ cgi_pattern = getenv("CGI_PATTERN");
+#ifdef CGI_PATTERN
+ if (cgi_pattern == (char *)0)
+ {
+ cgi_pattern = CGI_PATTERN;
+ }
+#endif /* CGI_PATTERN */
+ inited = 1;
+ }
+
+ /* ../ is not permitted. */
+
+ if (strstr(filename, "../") !=NULL)
+ {
+ return 0;
+ }
+
+ /* Ensure that we are not reading a basic auth password file. */
+
+#ifdef CONFIG_AUTH_FILE
+ fnl = strlen(filename);
+ if (strcmp(filename, CONFIG_AUTH_FILE) == 0 ||
+ (fnl >= sizeof(CONFIG_AUTH_FILE) &&
+ strcmp(&filename[fnl - sizeof(CONFIG_AUTH_FILE) + 1], CONFIG_AUTH_FILE) == 0 &&
+ filename[fnl - sizeof(CONFIG_AUTH_FILE)] == '/'))
+ {
+ return 0;
+ }
+
+ /* Check for an auth file in the same directory. We can't do an actual **
+ * auth password check here because CGI programs are not given the **
+ * authorization header, for security reasons. So instead we just **
+ * prohibit access to all auth-protected files.
+ */
+
+ dirname = strdup(filename);
+ if (dirname == (char *)0)
+ {
+ /* out of memory */
+
+ return 0;
+ }
+
+ cp = strrchr(dirname, '/');
+ if (cp == (char *)0)
+ {
+ (void)strcpy(dirname, ".");
+ }
+ else
+ {
+ *cp = '\0';
+ }
+
+ authname = malloc(strlen(dirname) + 1 + sizeof(CONFIG_AUTH_FILE));
+ if (!authname)
+ {
+ /* out of memory */
+
+ free(dirname);
+ return 0;
+ }
+
+ (void)sprintf(authname, "%s/%s", dirname, CONFIG_AUTH_FILE);
+ r = stat(authname, &sb);
+
+ free(dirname);
+ free(authname);
+
+ if (r == 0)
+ {
+ return 0;
+ }
+#endif /* CONFIG_AUTH_FILE */
+
+ /* Ensure that we are not reading a CGI file. */
+
+ if (cgi_pattern != (char *)0 && match(cgi_pattern, filename))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+static void show_time(time_t t, int gmt)
+{
+ struct tm *tmP;
+
+ if (gmt)
+ {
+ tmP = gmtime(&t);
+ }
+ else
+ {
+ tmP = localtime(&t);
+ }
+
+ if (strftime(g_iobuffer2, BUFFER_SIZE, g_timeformat, tmP) > 0)
+ {
+ (void)puts(g_iobuffer2);
+ }
+}
+
+static void show_size(off_t size)
+{
+ switch (g_sizefmt)
+ {
+ case SF_BYTES:
+ (void)printf("%ld", (long)size); /* spec says should have commas */
+ break;
+
+ case SF_ABBREV:
+ if (size < 1024)
+ {
+ (void)printf("%ld", (long)size);
+ }
+ else if (size < 1024)
+ {
+ (void)printf("%ldK", (long)size / 1024L);
+ }
+ else if (size < 1024 * 1024)
+ {
+ (void)printf("%ldM", (long)size / (1024L * 1024L));
+ }
+ else
+ {
+ (void)printf("%ldG", (long)size / (1024L * 1024L * 1024L));
+ }
+ break;
+ }
+}
+
+static void do_config(FILE *instream, char *vfilename, char *filename,
+ char *directive, char *tag, char *val)
+{
+ /* The config directive controls various aspects of the file parsing. **
+ * There are two valid tags: g_timeformat Gives the server a new format to
+ * use when providing dates. This is a string compatible with the
+ * strftime library call. g_sizefmt Determines the formatting to be used
+ * when displaying the size of a file. Valid choices are bytes, for a
+ * formatted byte count (formatted as 1,234,567), or abbrev for an
+ * abbreviated version displaying the number of kilobytes or megabytes the
+ * file occupies.
+ */
+
+ if (strcmp(tag, "g_timeformat") == 0)
+ {
+ (void)strncpy(g_timeformat, val, TIMEFMT_SIZE - 1);
+ g_timeformat[TIMEFMT_SIZE - 1] = '\0';
+ }
+ else if (strcmp(tag, "g_sizefmt") == 0)
+ {
+ if (strcmp(val, "bytes") == 0)
+ {
+ g_sizefmt = SF_BYTES;
+ }
+ else if (strcmp(val, "abbrev") == 0)
+ {
+ g_sizefmt = SF_ABBREV;
+ }
+ else
+ {
+ unknown_value(filename, directive, tag, val);
+ }
+ }
+ else
+ {
+ unknown_tag(filename, directive, tag);
+ }
+}
+
+static void do_include(FILE *instream, char *vfilename, char *filename,
+ char *directive, char *tag, char *val)
+{
+ FILE *instream2;
+ int ret;
+
+ /* Inserts the text of another document into the parsed document. */
+
+ ret = get_filename(vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE);
+ if (ret < 0)
+ {
+ return;
+ }
+
+ if (!check_filename(g_iobuffer1))
+ {
+ not_permitted(directive, tag, g_iobuffer1);
+ return;
+ }
+
+ instream2 = fopen(g_iobuffer1, "r");
+ if (instream2 == (FILE *) 0)
+ {
+ not_found2(directive, tag, g_iobuffer1);
+ return;
+ }
+
+ if (strcmp(tag, "virtual") == 0)
+ {
+ if (strlen(val) <BUFFER_SIZE)
+ {
+ (void)strcpy(g_iobuffer2, val);
+ }
+ else
+ {
+ (void)strcpy(g_iobuffer2, g_iobuffer1); /* same size, has to fit */
+ }
+ }
+ else
+ {
+ if (strlen(vfilename) + 1 + strlen(val) < BUFFER_SIZE)
+ {
+ char *cp;
+ (void)strcpy(g_iobuffer2, vfilename);
+ cp = strrchr(g_iobuffer2, '/');
+ if (cp == (char *)0)
+ {
+ cp = &g_iobuffer2[strlen(g_iobuffer2)];
+ *cp = '/';
+ }
+ (void)strcpy(++cp, val);
+ }
+ else
+ {
+ (void)strcpy(g_iobuffer2, g_iobuffer1); /* same size, has to fit */
+ }
+ }
+
+ read_file(instream2, g_iobuffer2, g_iobuffer1);
+ (void)fclose(instream2);
+}
+
+static void do_echo(FILE *instream, char *vfilename, char *filename,
+ char *directive, char *tag, char *val)
+{
+ char *cp;
+
+ /* Prints the value of one of the include variables. Any dates are
+ * printed subject to the currently configured g_timeformat. The only valid
+ * tag is var, whose value is the name of the variable you wish to echo.
+ */
+
+ if (strcmp(tag, "var") != 0)
+ {
+ unknown_tag(filename, directive, tag);
+ }
+ else
+ {
+ if (strcmp(val, "DOCUMENT_NAME") == 0)
+ {
+ /* The current filename. */
+
+ (void)puts(filename);
+ }
+ else if (strcmp(val, "DOCUMENT_URI") == 0)
+ {
+ /* The virtual path to this file (such as /~robm/foo.shtml). */
+
+ (void)puts(vfilename);
+ }
+ else if (strcmp(val, "QUERY_STRING_UNESCAPED") == 0)
+ {
+ /* The unescaped version of any search query the client sent. */
+
+ cp = getenv("QUERY_STRING");
+ if (cp != (char *)0)
+ {
+ (void)puts(cp);
+ }
+ }
+ else if (strcmp(val, "DATE_LOCAL") == 0)
+ {
+ struct timeval tm;
+
+ /* The current date, local time zone. */
+
+ gettimeofday(&tm, NULL);
+ show_time(tm.tv_sec, 0);
+ }
+ else if (strcmp(val, "DATE_GMT") == 0)
+ {
+ struct timeval tm;
+
+ /* Same as DATE_LOCAL but in Greenwich mean time. */
+
+ gettimeofday(&tm, NULL);
+ show_time(tm.tv_sec, 1);
+ }
+ else if (strcmp(val, "LAST_MODIFIED") == 0)
+ {
+ /* The last modification date of the current document. */
+
+ if (fstat(fileno(instream), &g_sb) >= 0)
+ {
+ show_time(g_sb.st_mtime, 0);
+ }
+ }
+ else
+ {
+ /* Try an environment variable. */
+
+ cp = getenv(val);
+ if (cp == (char *)0)
+ {
+ unknown_value(filename, directive, tag, val);
+ }
+ else
+ {
+ (void)puts(cp);
+ }
+ }
+ }
+}
+
+static void do_fsize(FILE *instream, char *vfilename, char *filename,
+ char *directive, char *tag, char *val)
+{
+ int ret;
+
+ /* Prints the size of the specified file. */
+
+ ret = get_filename(vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE);
+ if (ret < 0)
+ {
+ return;
+ }
+
+ if (stat(g_iobuffer1, &g_sb) < 0)
+ {
+ not_found2(directive, tag, g_iobuffer1);
+ return;
+ }
+
+ show_size(g_sb.st_size);
+}
+
+static void do_flastmod(FILE *instream, char *vfilename, char *filename,
+ char *directive, char *tag, char *val)
+{
+ int ret;
+
+ /* Prints the last modification date of the specified file. */
+
+ ret = get_filename(vfilename, filename, directive, tag, val, g_iobuffer1, BUFFER_SIZE);
+ if (ret < 0)
+ {
+ return;
+ }
+
+ if (stat(g_iobuffer1, &g_sb) < 0)
+ {
+ not_found2(directive, tag, g_iobuffer1);
+ return;
+ }
+ show_time(g_sb.st_mtime, 0);
+}
+
+static void parse(FILE *instream, char *vfilename, char *filename, char *str)
+{
+ char *directive;
+ char *cp;
+ int ntags;
+ int dirn;
+ int i;
+ char *val;
+
+ directive = str;
+ directive += strspn(directive, " \t\n\r");
+
+ ntags = 0;
+ cp = directive;
+ for (;;)
+ {
+ cp = strpbrk(cp, " \t\n\r\"");
+ if (cp == (char *)0)
+ {
+ break;
+ }
+
+ if (*cp == '"')
+ {
+ cp = strpbrk(cp + 1, "\"");
+ cp++;
+ if (*cp == '\0')
+ {
+ break;
+ }
+ }
+
+ *cp++ = '\0';
+ cp += strspn(cp, " \t\n\r");
+ if (*cp == '\0')
+ {
+ break;
+ }
+
+ if (ntags < MAX_TAGS)
+ {
+ g_tags[ntags++] = cp;
+ }
+ }
+
+ if (strcmp(directive, "config") == 0)
+ {
+ dirn = DI_CONFIG;
+ }
+ else if (strcmp(directive, "include") == 0)
+ {
+ dirn = DI_INCLUDE;
+ }
+ else if (strcmp(directive, "echo") == 0)
+ {
+ dirn = DI_ECHO;
+ }
+ else if (strcmp(directive, "fsize") == 0)
+ {
+ dirn = DI_FSIZE;
+ }
+ else if (strcmp(directive, "flastmod") == 0)
+ {
+ dirn = DI_FLASTMOD;
+ }
+ else
+ {
+ unknown_directive(filename, directive);
+ return;
+ }
+
+ for (i = 0; i < ntags; ++i)
+ {
+ if (i > 0)
+ {
+ putchar(' ');
+ }
+
+ val = strchr(g_tags[i], '=');
+ if (val == (char *)0)
+ {
+ val = "";
+ }
+ else
+ {
+ *val++ = '\0';
+ }
+
+ if (*val == '"' && val[strlen(val) - 1] == '"')
+ {
+ val[strlen(val) - 1] = '\0';
+ ++val;
+ }
+
+ switch (dirn)
+ {
+ case DI_CONFIG:
+ do_config(instream, vfilename, filename, directive, g_tags[i], val);
+ break;
+
+ case DI_INCLUDE:
+ do_include(instream, vfilename, filename, directive, g_tags[i], val);
+ break;
+
+ case DI_ECHO:
+ do_echo(instream, vfilename, filename, directive, g_tags[i], val);
+ break;
+
+ case DI_FSIZE:
+ do_fsize(instream, vfilename, filename, directive, g_tags[i], val);
+ break;
+
+ case DI_FLASTMOD:
+ do_flastmod(instream, vfilename, filename, directive, g_tags[i], val);
+ break;
+ }
+ }
+}
+
+static void slurp(FILE *instream, char *vfilename, char *filename)
+{
+ int state;
+ int ich;
+ int i;
+
+ /* Now slurp in the rest of the comment from the input file. */
+
+ i = 0;
+ state = ST_GROUND;
+ while ((ich = getc(instream)) != EOF)
+ {
+ switch (state)
+ {
+ case ST_GROUND:
+ if (ich == '-')
+ {
+ state = ST_MINUS1;
+ }
+ break;
+
+ case ST_MINUS1:
+ if (ich == '-')
+ {
+ state = ST_MINUS2;
+ }
+ else
+ {
+ state = ST_GROUND;
+ }
+ break;
+
+ case ST_MINUS2:
+ if (ich == '>')
+ {
+ g_iobuffer1[i - 2] = '\0';
+ parse(instream, vfilename, filename, g_iobuffer1);
+ return;
+ }
+ else if (ich != '-')
+ {
+ state = ST_GROUND;
+ }
+ break;
+ }
+
+ if (i < BUFFER_SIZE - 1)
+ {
+ g_iobuffer1[i++] = (char)ich;
+ }
+ }
+}
+
+static void read_file(FILE *instream, char *vfilename, char *filename)
+{
+ int ich;
+ int state;
+
+ /* Copy it to output, while running a state-machine to look for SSI
+ * directives.
+ */
+
+ state = ST_GROUND;
+ while ((ich = getc(instream)) != EOF)
+ {
+ switch (state)
+ {
+ case ST_GROUND:
+ if (ich == '<')
+ {
+ state = ST_LESSTHAN;
+ continue;
+ }
+ break;
+
+ case ST_LESSTHAN:
+ if (ich == '!')
+ {
+ state = ST_BANG;
+ continue;
+ }
+ else
+ {
+ state = ST_GROUND;
+ putchar('<');
+ }
+ break;
+
+ case ST_BANG:
+ if (ich == '-')
+ {
+ state = ST_MINUS1;
+ continue;
+ }
+ else
+ {
+ state = ST_GROUND;
+ (void)puts("<!");
+ }
+ break;
+
+ case ST_MINUS1:
+ if (ich == '-')
+ {
+ state = ST_MINUS2;
+ continue;
+ }
+ else
+ {
+ state = ST_GROUND;
+ (void)puts("<!-");
+ }
+ break;
+
+ case ST_MINUS2:
+ if (ich == '#')
+ {
+ slurp(instream, vfilename, filename);
+ state = ST_GROUND;
+ continue;
+ }
+ else
+ {
+ state = ST_GROUND;
+ (void)puts("<!--");
+ }
+ break;
+ }
+
+ putchar((char)ich);
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, char **argv)
+{
+ FILE *instream;
+ char *script_name;
+ char *path_info;
+ char *path_translated;
+ int err = 0;
+
+ /* Default formats. */
+
+ (void)strcpy(g_timeformat, "%a %b %e %T %Z %Y");
+ g_sizefmt = SF_BYTES;
+
+ /* The MIME type has to be text/html. */
+
+ (void)puts("Content-type: text/html\n\n");
+
+ /* Get the name that we were run as. */
+
+ script_name = getenv("SCRIPT_NAME");
+ if (!script_name)
+ {
+ internal_error("Couldn't get SCRIPT_NAME environment variable.");
+ return 1;
+ }
+
+ /* Append the PATH_INFO, if any, to get the full URL. */
+
+ path_info = getenv("PATH_INFO");
+ if (!path_info)
+ {
+ path_info = "";
+ }
+
+ g_url = (char*)malloc(strlen(script_name) + strlen(path_info) + 1);
+ if (!g_url)
+ {
+ internal_error("Out of memory.");
+ return 2;
+ }
+ (void)sprintf(g_url, "%s%s", script_name, path_info);
+
+ /* Get the name of the file to parse. */
+
+ path_translated = getenv("PATH_TRANSLATED");
+ if (!path_translated)
+ {
+ internal_error("Couldn't get PATH_TRANSLATED environment variable.");
+ err = 3;
+ goto errout_with_g_url;
+ }
+
+ if (!check_filename(path_translated))
+ {
+ not_permitted("initial", "PATH_TRANSLATED", path_translated);
+ err = 4;
+ goto errout_with_g_url;
+ }
+
+ /* Open it. */
+
+ instream = fopen(path_translated, "r");
+ if (!instream)
+ {
+ not_found(path_translated);
+ err = 5;
+ goto errout_with_g_url;
+ }
+
+ /* Read and handle the file. */
+
+ read_file(instream, path_info, path_translated);
+
+ (void)fclose(instream);
+
+errout_with_g_url:
+ free(g_url);
+ return err;
+}