| /**************************************************************************** |
| * apps/netutils/thttpd/cgi-src/ssi.c |
| * Server-side-includes CGI program |
| * |
| * Copyright (C) 2009, 2015 Gregory Nutt. All rights reserved. |
| * Author: Gregory Nutt <gnutt@nuttx.org> |
| * |
| * 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 <fnmatch.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"; |
| |
| 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"; |
| |
| 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"; |
| |
| 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"; |
| |
| 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"; |
| |
| 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"; |
| |
| 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"; |
| |
| 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 size; |
| 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); |
| size = fl - vl; |
| |
| 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[size]) != 0) |
| { |
| return -1; |
| } |
| |
| if (size + strlen(val) >= fnsize) |
| { |
| return -1; |
| } |
| |
| strlcpy(fn, filename, fnsize); |
| strlcpy(&fn[size], val, fnsize - size); |
| } |
| 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; |
| } |
| |
| strlcpy(fn, filename, fnsize); |
| cp = strrchr(fn, '/'); |
| if (cp == (char *)0) |
| { |
| cp = &fn[strlen(fn)]; |
| *cp = '/'; |
| } |
| |
| cp++; |
| strlcpy(cp, val, fnsize - (cp - fn)); |
| } |
| 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) |
| { |
| strlcpy(dirname, ".", fnl + 1); |
| } |
| else |
| { |
| *cp = '\0'; |
| } |
| |
| fnl = strlen(dirname) + 1 + sizeof(CONFIG_AUTH_FILE); |
| authname = malloc(fnl); |
| if (!authname) |
| { |
| /* out of memory */ |
| |
| free(dirname); |
| return 0; |
| } |
| |
| snprintf(authname, fnl, "%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 && !fnmatch(cgi_pattern, filename, 0)) |
| { |
| 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) |
| { |
| puts(g_iobuffer2); |
| } |
| } |
| |
| static void show_size(off_t size) |
| { |
| switch (g_sizefmt) |
| { |
| case SF_BYTES: |
| printf("%ld", (long)size); /* spec says should have commas */ |
| break; |
| |
| case SF_ABBREV: |
| if (size < 1024) |
| { |
| printf("%ld", (long)size); |
| } |
| else if (size < 1024 * 1024) |
| { |
| printf("%ldK", (long)size / 1024L); |
| } |
| else if (size < 1024 * 1024 * 1024) |
| { |
| printf("%ldM", (long)size / (1024L * 1024L)); |
| } |
| else |
| { |
| 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) |
| { |
| strlcpy(g_timeformat, val, TIMEFMT_SIZE); |
| } |
| 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 == NULL) |
| { |
| not_found2(directive, tag, g_iobuffer1); |
| return; |
| } |
| |
| if (strcmp(tag, "virtual") == 0) |
| { |
| if (strlen(val) < BUFFER_SIZE) |
| { |
| strlcpy(g_iobuffer2, val, sizeof(g_iobuffer2)); |
| } |
| else |
| { |
| strlcpy(g_iobuffer2, g_iobuffer1, sizeof(g_iobuffer2)); /* same size, has to fit */ |
| } |
| } |
| else |
| { |
| if (strlen(vfilename) + 1 + strlen(val) < BUFFER_SIZE) |
| { |
| char *cp; |
| strlcpy(g_iobuffer2, vfilename, sizeof(g_iobuffer2)); |
| cp = strrchr(g_iobuffer2, '/'); |
| if (cp == (char *)0) |
| { |
| cp = &g_iobuffer2[strlen(g_iobuffer2)]; |
| *cp = '/'; |
| } |
| |
| cp++; |
| strlcpy(cp, val, sizeof(g_iobuffer2) - (cp - g_iobuffer2)); |
| } |
| else |
| { |
| strlcpy(g_iobuffer2, g_iobuffer1, sizeof(g_iobuffer2)); /* same size, has to fit */ |
| } |
| } |
| |
| read_file(instream2, g_iobuffer2, g_iobuffer1); |
| 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. */ |
| |
| puts(filename); |
| } |
| else if (strcmp(val, "DOCUMENT_URI") == 0) |
| { |
| /* The virtual path to this file (such as /~robm/foo.shtml). */ |
| |
| 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) |
| { |
| 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); |
| } |
| #if 0 /* fstat is not yet supported */ |
| 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); |
| } |
| } |
| #endif |
| else |
| { |
| /* Try an environment variable. */ |
| |
| cp = getenv(val); |
| if (cp == (char *)0) |
| { |
| unknown_value(filename, directive, tag, val); |
| } |
| else |
| { |
| 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; |
| puts("<!"); |
| } |
| break; |
| |
| case ST_MINUS1: |
| if (ich == '-') |
| { |
| state = ST_MINUS2; |
| continue; |
| } |
| else |
| { |
| state = ST_GROUND; |
| puts("<!-"); |
| } |
| break; |
| |
| case ST_MINUS2: |
| if (ich == '#') |
| { |
| slurp(instream, vfilename, filename); |
| state = ST_GROUND; |
| continue; |
| } |
| else |
| { |
| state = ST_GROUND; |
| puts("<!--"); |
| } |
| break; |
| } |
| |
| putchar((char)ich); |
| } |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_THTTPD_BINFS |
| int ssi_main(int argc, char *argv[]) |
| #else |
| int main(int argc, char *argv[]) |
| #endif |
| { |
| FILE *instream; |
| char *script_name; |
| char *path_info; |
| char *path_translated; |
| size_t len; |
| int errcode = 0; |
| |
| /* Default formats. */ |
| |
| strlcpy(g_timeformat, "%a %b %e %T %Z %Y", sizeof(g_timeformat)); |
| g_sizefmt = SF_BYTES; |
| |
| /* The MIME type has to be text/html. */ |
| |
| 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 = ""; |
| } |
| |
| len = strlen(script_name) + strlen(path_info) + 1; |
| g_url = (char *)malloc(len); |
| if (!g_url) |
| { |
| internal_error("Out of memory."); |
| return 2; |
| } |
| |
| snprintf(g_url, len, "%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."); |
| errcode = 3; |
| goto errout_with_g_url; |
| } |
| |
| if (!check_filename(path_translated)) |
| { |
| not_permitted("initial", "PATH_TRANSLATED", path_translated); |
| errcode = 4; |
| goto errout_with_g_url; |
| } |
| |
| /* Open it. */ |
| |
| instream = fopen(path_translated, "r"); |
| if (!instream) |
| { |
| not_found(path_translated); |
| errcode = 5; |
| goto errout_with_g_url; |
| } |
| |
| /* Read and handle the file. */ |
| |
| read_file(instream, path_info, path_translated); |
| |
| fclose(instream); |
| |
| errout_with_g_url: |
| free(g_url); |
| return errcode; |
| } |