tinycobol/test.code/tdb03/cgi-util.c

1007 lines
20 KiB
C

/*
cgi-util.c
version 2.1.0
by Bill Kendrick <bill@newbreedsoftware.com>
and Mike Simons <msimons@moria.simons-clan.com>
New Breed Software
http://www.newbreedsoftware.com/cgi-util/
April 6, 1996 - July 26, 2000
LICENSE: GPL.
Modified for use in TinyCobol by Rildo Pragana, 2001.
*/
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#ifndef NO_STDLIB_H
#include <stdlib.h>
#else
char *getenv();
#endif
#include <string.h>
#include "cgi-util.h"
/* Globals: */
cgi_entry_type * cgi_entries = NULL;
int cgi_num_entries = 0;
int cgi_errno = CGIERR_NONE;
int cgi_request_method = CGIREQ_NONE;
int cgi_content_type = CGITYPE_NONE;
char * cgi_query = NULL;
/* English error strings: */
char * cgi_error_strings[CGIERR_NUM_ERRS] = {
"", "Not an integer", "Not a double", "Not a boolean",
"Unknown method", "Incorrect Content Type",
"NULL Query String", "Bad Content Length",
"Content Length Discrepancy", "No Cookies", "Cookie Not Found"
};
/* Debugging. If set, this library will attempt to append debugging
information to a file named "debug.txt" in the current directory. */
/* #define DEBUG */
/* Converts hexadecimal to decimal (character): */
char x2c(char *what)
{
register char digit;
digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
digit *= 16;
digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
return (digit);
}
/* Unescapes "%"-escaped characters in a query: */
void unescape_url(char *url)
{
register int x,y,len;
len = strlen(url);
for (x=0, y=0; url[y]; ++x, ++y)
{
if ((url[x] = url[y]) == '%'
&& y < len - 2) /* 2.0.4 - MJ Pomraning (pilcrow@mailbag.com) */
{
url[x] = x2c(&url[y+1]);
y+=2;
}
}
url[x] = '\0';
}
/* Converts pluses back to spaces in a query: */
void plustospace(char *str)
{
register int x;
for (x=0; str[x]; x++)
if (str[x] == '+')
str[x] = ' ';
}
/* Internal use: Write debugging stuff to a file */
void debug(char * str1, char * str2)
{
#ifdef DEBUG
FILE * fi;
fi = fopen("debug.txt", "a");
if (fi != NULL)
{
fprintf(fi, "%s:%s\n", str1, str2);
fclose(fi);
}
#endif
}
/* Internal use: Read a line and return its length: */
int lineread(FILE * stream, char * buf, int count)
{
fgets(buf, count, stream);
if (!feof(stream))
return(strlen(buf));
else
return(0);
}
/* Initialize the CGI. Grab data from the browser and prepare it for us. */
#define CGI_RETURN(ret) *result = (ret); return;
void
cgi_init( int *result ) {
int cl, i, in_multipart_headers, which_entry, length_gotten;
char * boundary;
/* Default, no errors, no name/value pairs ("entries"): */
cgi_errno = CGIERR_NONE;
cgi_num_entries = 0;
length_gotten = 0;
/* Check for REQUEST_METHOD (set by HTTP server): */
if (getenv("REQUEST_METHOD") == NULL)
{
/* None set? Assume the user is invoking the CGI from a shell prompt
(for debugging): */
cgi_request_method = CGIREQ_NONE;
}
else
{
/* Determine the exact request method, and grab the data (if any)
in the appropriate manner: */
if (strcmp(getenv("REQUEST_METHOD"), "POST") == 0)
{
/* Post method (data is sent to us via "stdin"): */
cgi_request_method = CGIREQ_POST;
if (getenv("CONTENT_TYPE") == NULL)
{
/* Content type is not set! */
cgi_errno = CGIERR_INCORRECT_TYPE;
cgi_content_type = CGITYPE_UNKNOWN;
CGI_RETURN(cgi_errno);
}
else if (strcmp(getenv("CONTENT_TYPE"),
"application/x-www-form-urlencoded") == 0)
{
cgi_content_type = CGITYPE_APPLICATION_X_WWW_FORM_URLENCODED;
/* How much data do we expect? */
if (getenv("CONTENT_LENGTH") == NULL ||
sscanf(getenv("CONTENT_LENGTH"), "%d", &cl) != 1)
{
cgi_errno = CGIERR_BAD_CONTENT_LENGTH;
CGI_RETURN(cgi_errno);
}
/* Create space for it: */
cgi_query = malloc(cl + 1);
/* 2.0.1 - Tadek Orlowski (orlowski@epnet.com) ... "+1" */
if (cgi_query == NULL)
{
cgi_errno = CGIERR_OUT_OF_MEMORY;
CGI_RETURN(cgi_errno);
}
/* Read it in: */
fgets(cgi_query, cl + 1, stdin);
/* Verify that we got as much data as we expected: */
if (strlen(cgi_query) != cl)
cgi_errno = CGIERR_CONTENT_LENGTH_DISCREPANCY;
}
else if (strstr(getenv("CONTENT_TYPE"),
"multipart/form-data") == getenv("CONTENT_TYPE"))
{
cgi_content_type = CGITYPE_MULTIPART_FORM_DATA;
cgi_query = malloc(2050);
if (cgi_query == NULL)
{
cgi_errno = CGIERR_OUT_OF_MEMORY;
CGI_RETURN(cgi_errno);
}
/* Determine the boundary string: */
if (strstr(getenv("CONTENT_TYPE"),
"boundary=") == NULL)
{
cgi_errno = CGIERR_NO_BOUNDARY;
CGI_RETURN(cgi_errno);
}
boundary = strdup(strstr(getenv("CONTENT_TYPE"),
"boundary=") + 9);
debug("boundary", boundary);
/* Read in until there's no more: */
in_multipart_headers = 0;
which_entry = -1;
do
{
length_gotten = lineread(stdin, cgi_query, 2048);
debug("cgi_query", cgi_query);
if (length_gotten > 0)
{
if (strstr(cgi_query, boundary) == cgi_query + 2 &&
cgi_query[0] == '-' && cgi_query[1] == '-')
{
/* We got a boundary! */
in_multipart_headers = 1;
which_entry = -1;
}
else /* (Not a boundary) */
{
if (in_multipart_headers == 1)
{
/* We had just got a boundary, read headers: */
if (cgi_query[0] == '\r' || cgi_query[0] == '\n')
{
/* Blank line, end of headers: */
in_multipart_headers = 0;
}
else /* (Not a blank line) */
{
/* What kind of header is it? */
if (strstr(cgi_query,
"Content-Disposition: ") ==
cgi_query)
{
/* Content-disposition: */
/* For now, just look for "name=": */
if (strstr(cgi_query, "name=\"") != NULL)
{
/* Add a new entry: */
which_entry = cgi_num_entries;
cgi_num_entries++;
/* Make more room: */
cgi_entries =
realloc(cgi_entries,
sizeof(cgi_entry_type) *
cgi_num_entries);
if (cgi_entries == NULL)
{
cgi_errno = CGIERR_OUT_OF_MEMORY;
CGI_RETURN(cgi_errno);
}
/* Fill in the name slot: */
cgi_entries[which_entry].name =
strdup(strstr(cgi_query,
"name=\"") +
6);
/* Truncate after quote: */
if (strchr(cgi_entries[which_entry].
name, '\"') != NULL)
{
strcpy(strchr(cgi_entries
[which_entry].name,
'\"'), "\0");
}
/* Set default content-type: */
cgi_entries[which_entry].
content_type =
"application/octet-stream";
/* Set default content-length: */
cgi_entries[which_entry].
content_length = 0;
/* Set default value: */
cgi_entries[which_entry].val =
strdup("");
debug("entry.name",
cgi_entries[which_entry].name);
}
}
else if (strstr(cgi_query,
"Content-Type: ") ==
cgi_query)
{
/* Content-type: */
cgi_entries[which_entry].content_type =
strdup(strstr(cgi_query,
"Content-Type: ") +
14);
debug("entry.content_type",
cgi_entries[which_entry].
content_type);
}
}
}
else /* in_multipart_headers == 0 */
{
/* If we're recording into a particular
entry, copy the data: */
if (which_entry != -1)
{
/* Make more room: */
cgi_entries[which_entry].val =
realloc(cgi_entries[which_entry].val,
strlen(cgi_entries[which_entry].
val) + length_gotten + 1);
if (cgi_entries[which_entry].val == NULL)
{
cgi_errno = CGIERR_OUT_OF_MEMORY;
CGI_RETURN(cgi_errno);
}
/* Append the data: */
memcpy(cgi_entries[which_entry].val +
(cgi_entries[which_entry].
content_length),
cgi_query, length_gotten);
cgi_entries[which_entry].content_length =
(cgi_entries[which_entry].content_length +
length_gotten);
}
}
}
}
}
while (length_gotten > 0);
free(cgi_query);
}
else
{
/* Content type is unrecognized! */
cgi_errno = CGIERR_INCORRECT_TYPE;
cgi_content_type = CGITYPE_UNKNOWN;
CGI_RETURN(cgi_errno);
}
}
else if (strcmp(getenv("REQUEST_METHOD"), "GET") == 0)
{
/* For now, assume Content Type of
"application/x-www-form-urlencoded"
(Is this a bad assumption?) */
cgi_content_type = CGITYPE_APPLICATION_X_WWW_FORM_URLENCODED;
/* GET method (data sent via "QUERY_STRING" env. variable): */
cgi_request_method = CGIREQ_GET;
/* Get a pointer to the data: */
cgi_query = getenv("QUERY_STRING");
if (cgi_query == NULL)
{
/* Does the "QUERY_STRING" env. variable not exist!? */
cgi_errno = CGIERR_NULL_QUERY_STRING;
CGI_RETURN(cgi_errno);
}
else
{
/* Determine the content length by seeing how big the
string is: */
cl = strlen(cgi_query);
}
}
else
{
/* Something else? We can't handle it! */
cgi_request_method = CGIREQ_UNKNOWN;
cgi_errno = CGIERR_UNKNOWN_METHOD;
cgi_num_entries = 0;
CGI_RETURN(cgi_errno);
}
if (cgi_content_type != CGITYPE_MULTIPART_FORM_DATA)
{
/* How many entries (name/value pairs) do we need to
allocate space for? (They should be separated by "&"'s) */
cgi_num_entries = 0;
for (i = 0; i <= cl; i++)
if (cgi_query[i] == '&' || cgi_query[i] == '\0')
cgi_num_entries++;
/* Allocate the space for that many structures: */
cgi_entries = malloc(sizeof(cgi_entry_type) * cgi_num_entries);
if (cgi_entries == NULL)
{
cgi_errno = CGIERR_OUT_OF_MEMORY;
CGI_RETURN(cgi_errno);
}
/* Grab each name/value pair: */
cgi_num_entries = 0;
/* (Begin with the first half of the first pair): */
if (cgi_query[0] != '\0' && cgi_query[0] != '&')
{
cgi_entries[0].name = cgi_query;
cgi_entries[0].content_type = "text/html";
}
/* Go through the entire string of characters: */
for (i = 0; i <= cl; i++)
{
if (cgi_query[i] == '&')
{
/* "&" represents the end of a name/value pair: */
cgi_entries[cgi_num_entries].name = cgi_query + i + 1;
cgi_entries[cgi_num_entries].content_type = "text/html";
cgi_query[i] = '\0';
}
else if (cgi_query[i] == '=')
{
/* "=" is the end of the name half of a name/value pair: */
cgi_entries[cgi_num_entries].val = cgi_query + i + 1;
/* plustospace(cgi_entries[cgi_num_entries].val);
unescape_url(cgi_entries[cgi_num_entries].val); */
cgi_num_entries++;
cgi_query[i] = '\0';
}
}
for (i = 0; i < cgi_num_entries; i++)
{
plustospace(cgi_entries[i].val);
unescape_url(cgi_entries[i].val);
}
}
/* Fix any NULL strings to be empty strings */
/* 2.0.4 - MJ Pomraning (pilcrow@mailbag.com) */
for (i = 0; i < cgi_num_entries; i++)
{
if (cgi_entries[i].name == NULL)
cgi_entries[i].name = "";
if (cgi_entries[i].val == NULL)
cgi_entries[i].val = "";
}
}
CGI_RETURN(CGIERR_NONE);
}
/* Free up memory that was allocated when we called "cgi_init()": */
void cgi_quit(void)
{
if (cgi_request_method == CGIREQ_NONE ||
cgi_request_method == CGIREQ_UNKNOWN)
{
/* Nothing to do! */
}
else
{
if (cgi_request_method == CGIREQ_POST)
{
/* Was it POST method? Free the data we had read from "stdin" */
free(cgi_query);
}
/* Free the entry structures themselves: */
free(cgi_entries);
}
cgi_entries = NULL;
cgi_num_entries = 0;
cgi_errno = CGIERR_NONE;
cgi_request_method = CGIREQ_NONE;
cgi_query = NULL;
cgi_content_type = CGITYPE_NONE;
}
/* Grab a cookie, if it exists. Return NULL if it doesn't: */
/* (Based on code by Pete Cassidy (pcassidy@iol.ie) - May 10, 2000) */
const char * cgi_getcookie(const char * cookie_name)
{
char * cookieval, * tmpcookie, * rawcookie, * left, * right;
int done;
/* Get raw cookie data: */
rawcookie = getenv("HTTP_COOKIE");
if (rawcookie == NULL)
{
/* No cookies at all? The cookie we want can't exist! */
cgi_errno = CGIERR_NO_COOKIES;
return(NULL);
}
/* Strtok is destructive, so make a temporary copy of the raw cookie data: */
tmpcookie = malloc(sizeof(char) * (strlen(rawcookie) + 1));
if (tmpcookie == NULL)
{
cgi_errno = CGIERR_OUT_OF_MEMORY;
return(NULL);
}
strcpy(tmpcookie, rawcookie);
/* Tokenize out all cookies and check for the one we're looking for: */
left = strtok(tmpcookie, ";");
cookieval = NULL;
done = 0;
do
{
/* Grab the righthand size of the current cookie pair's "=" sign: */
right = strchr(left, '=') + (1 * sizeof(char));
/* Change the "=" into a NULL character, to get the lefthand side: */
*strchr(left, '=') = '\0';
/* See if this is our cookie: */
if (strcmp(left, cookie_name) == 0)
{
/* If so, set our return-string to the value (righthand side): */
cookieval = malloc(sizeof(char) * (strlen(right) + 1));
if (cookieval == NULL)
{
cgi_errno = CGIERR_OUT_OF_MEMORY;
return(NULL);
}
strcpy(cookieval, right);
done = 1;
}
/* Jump to next cookie: */
if (!done)
{
left = strtok(NULL, ";");
if (left == NULL)
{
/* No more to parse? */
done = 1;
}
else
{
/* Skip the extra space: */
left++;
}
}
}
while (!done);
/* Free the temporary copy of the raw cookie data: */
free(tmpcookie);
/* Return the cookie value (which may be NULL if we never found it): */
if (cookieval == NULL)
cgi_errno = CGIERR_COOKIE_NOT_FOUND;
else
cgi_errno = CGIERR_NONE;
return(cookieval);
}
/* Grab a value and return it as a string: */
const char * cgi_getentrystr(const char *field_name)
{
int x;
if (cgi_request_method != CGIREQ_NONE)
{
/* Look for the name: */
for (x = 0; x < cgi_num_entries; x++)
{
if (strcmp(cgi_entries[x].name, field_name) == 0)
{
return (cgi_entries[x].val);
}
}
return(NULL);
}
else
{
/* printf("CGI-UTIL: \"%s\" ? ", field_name);
fgets(buf, 512, stdin);
buf[strlen(buf) - 1] = '\0'; */
return("x");
}
}
/* Grab a content-type and return it as a string: */
const char * cgi_getentrytype(const char *field_name)
{
int x;
if (cgi_request_method != CGIREQ_NONE)
{
/* Look for the name: */
for (x = 0; x < cgi_num_entries; x++)
{
if (strcmp(cgi_entries[x].name, field_name) == 0)
{
return (cgi_entries[x].content_type);
}
}
return(NULL);
}
else
return(NULL);
}
/* Grab a value and return it as an integer: */
int cgi_getentryint(const char *field_name)
{
int v;
v = 0;
if (cgi_getentrystr(field_name) != NULL)
{
if (sscanf(cgi_getentrystr(field_name), "%d", &v) != 1)
cgi_errno = CGIERR_NOT_INTEGER;
}
else
cgi_errno = CGIERR_NOT_INTEGER;
return(v);
}
/* Grab a value and return it as a double: */
double cgi_getentrydouble(const char *field_name)
{
double v;
v = 0;
if (cgi_getentrystr(field_name) != NULL)
{
if (sscanf(cgi_getentrystr(field_name), "%lf", &v) != 1)
cgi_errno = CGIERR_NOT_DOUBLE;
}
else
cgi_errno = CGIERR_NOT_DOUBLE;
return(v);
}
/* Grab a value and return it as a boolean (depending on if the
value was "yes", "on" or "true", or "no", "off" or "false"): */
int cgi_getentrybool(const char *field_name, int def)
{
const char * temp;
int v;
/* Assume the default: */
v = def;
/* Get the value (if any): */
temp = cgi_getentrystr(field_name);
if (temp != NULL)
{
if (strcasecmp(temp, "yes") == 0 ||
strcasecmp(temp, "on") == 0 ||
strcasecmp(temp, "true") == 0)
{
/* A "yes" or "on" is a 1: */
v = 1;
}
else if (strcasecmp(temp, "no") == 0 ||
strcasecmp(temp, "off") == 0 ||
strcasecmp(temp, "false") == 0)
{
/* A "no" or "off" is a 0: */
v = 0;
}
else if (temp[0] != 0)
{
/* We got something, but not "yes", "on", "no" or "off": */
cgi_errno = CGIERR_NOT_BOOL;
}
}
else
cgi_errno = CGIERR_NOT_BOOL;
return(v);
}
/* Open a file and send it to "stdout" (the browser): */
/* (Returns an error if we can't open the file) */
int cgi_dump_no_abort(const char * filename)
{
FILE * fi;
int c;
cgi_errno = CGIERR_NONE;
/* Open the file: */
fi = fopen(filename, "r");
if (fi == NULL)
cgi_errno = CGIERR_CANT_OPEN;
else
{
/* Read data and push it to "stdout": */
do
{
c = fgetc(fi);
if (c != EOF)
fputc(c, stdout);
}
while (c != EOF);
fclose(fi);
}
return(CGIERR_NONE);
}
/* Open a file and send it to "stdout" (the browser): */
/* (Displays an error message and quits the CGI if we can't open the file) */
void cgi_dump(const char * filename)
{
if (cgi_dump_no_abort(filename) != CGIERR_NONE)
{
printf("Can't open %s - %s\n", filename, strerror(errno));
exit(0);
}
}
/* Display a simple error message and quit the CGI: */
void cgi_error(const char * reason)
{
printf("<h1>Error</h1>\n");
printf("%s\n", reason);
exit(0);
}
/* Returns whether or not an e-mail address appears to be in the correct
syntax ("username@host.domain"): */
int cgi_goodemailaddress(const char * addr)
{
int i;
/* No "@".. what? */
if (strchr(addr, '@') == NULL)
return 0;
/* "@" or "." at the end or beginning? */
if (addr[strlen(addr - 1)] == '@' ||
addr[strlen(addr - 1)] == '.' ||
addr[0] == '@' || addr[0] == '.')
return 0;
/* No "." after the "@"? More than one "@"? */
if (strchr(strchr(addr, '@'), '.') == NULL ||
strchr(strchr(addr, '@') + 1, '@') != NULL)
return 0;
/* Any illegal characters within the string? */
for (i = 0; i < strlen(addr); i++)
{
if (isalnum(addr[i]) == 0 &&
addr[i] != '.' && addr[i] != '@' && addr[i] != '_' &&
addr[i] != '-')
return(0);
}
/* Must be ok... */
return 1;
}
/* Returns the English string description for a particular cgi-util error
value: */
const char * cgi_strerror(int err)
{
if (err < 0 || err > CGIERR_NUM_ERRS)
return("");
else
return(cgi_error_strings[err]);
}
/******** TinyCobol interfacing functions *************/
void
cgi_get_string( char *name, char *value ) {
char *s;
s = name;
while ( *s != ' ' || *(s+1) != ' ' ) s++;
*s=0;
s = cgi_getentrystr(name);
if (s != NULL) {
while (*s) {
*value++ = *s++;
}
}
}
void
cgi_get_integer( char *name, int *value ) {
char *s;
s = name;
while ( *s != ' ' || *(s+1) != ' ' ) s++;
*s=0;
*value = cgi_getentryint(name);
}
void
cgi_set_string( char *name, char *newvalue ) {
int x,i;
char *s;
s = name;
while ( *s != ' ' || *(s+1) != ' ' ) s++;
*s=0;
if (cgi_request_method != CGIREQ_NONE) {
for (x = 0; x < cgi_num_entries; x++) {
if (strcmp(cgi_entries[x].name, name) == 0) {
strcpy(cgi_entries[x].val,newvalue);
}
}
}
}