You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
957 lines
22 KiB
957 lines
22 KiB
/**************************************************************************** |
|
* 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; |
|
}
|
|
|