/*
* Copyright 2001-2004 Brandon Long
* All Rights Reserved.
*
* ClearSilver Templating System
*
* This code is made available under the terms of the ClearSilver License.
* http://www.clearsilver.net/license.hdf
*
*/
#include "cs_config.h"
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#if defined(HTML_COMPRESSION)
#include <zlib.h>
#endif
#include "util/neo_misc.h"
#include "util/neo_err.h"
#include "util/neo_hdf.h"
#include "util/neo_str.h"
#include "cgi.h"
#include "cgiwrap.h"
#include "html.h"
#include "cs/cs.h"
struct _cgi_vars
{
char *env_name;
char *hdf_name;
} CGIVars[] = {
{"AUTH_TYPE", "AuthType"},
{"CONTENT_TYPE", "ContentType"},
{"CONTENT_LENGTH", "ContentLength"},
{"DOCUMENT_ROOT", "DocumentRoot"},
{"GATEWAY_INTERFACE", "GatewayInterface"},
{"PATH_INFO", "PathInfo"},
{"PATH_TRANSLATED", "PathTranslated"},
{"QUERY_STRING", "QueryString"},
{"REDIRECT_REQUEST", "RedirectRequest"},
{"REDIRECT_QUERY_STRING", "RedirectQueryString"},
{"REDIRECT_STATUS", "RedirectStatus"},
{"REDIRECT_URL", "RedirectURL",},
{"REMOTE_ADDR", "RemoteAddress"},
{"REMOTE_HOST", "RemoteHost"},
{"REMOTE_IDENT", "RemoteIdent"},
{"REMOTE_PORT", "RemotePort"},
{"REMOTE_USER", "RemoteUser"},
{"REMOTE_GROUP", "RemoteGroup"},
{"REQUEST_METHOD", "RequestMethod"},
{"REQUEST_URI", "RequestURI"},
{"SCRIPT_FILENAME", "ScriptFilename"},
{"SCRIPT_NAME", "ScriptName"},
{"SERVER_ADDR", "ServerAddress"},
{"SERVER_ADMIN", "ServerAdmin"},
{"SERVER_NAME", "ServerName"},
{"SERVER_PORT", "ServerPort"},
{"SERVER_ROOT", "ServerRoot"},
{"SERVER_PROTOCOL", "ServerProtocol"},
{"SERVER_SOFTWARE", "ServerSoftware"},
/* SSL Vars from mod_ssl */
{"HTTPS", "HTTPS"},
{"SSL_PROTOCOL", "SSL.Protocol"},
{"SSL_SESSION_ID", "SSL.SessionID"},
{"SSL_CIPHER", "SSL.Cipher"},
{"SSL_CIPHER_EXPORT", "SSL.Cipher.Export"},
{"SSL_CIPHER_USEKEYSIZE", "SSL.Cipher.UseKeySize"},
{"SSL_CIPHER_ALGKEYSIZE", "SSL.Cipher.AlgKeySize"},
{"SSL_VERSION_INTERFACE", "SSL.Version.Interface"},
{"SSL_VERSION_LIBRARY", "SSL.Version.Library"},
{"SSL_CLIENT_M_VERSION", "SSL.Client.M.Version"},
{"SSL_CLIENT_M_SERIAL", "SSL.Client.M.Serial"},
{"SSL_CLIENT_S_DN", "SSL.Client.S.DN"},
{"SSL_CLIENT_S_DN_x509", "SSL.Client.S.DN.x509"},
{"SSL_CLIENT_I_DN", "SSL.Client.I.DN"},
{"SSL_CLIENT_I_DN_x509", "SSL.Client.I.DN.x509"},
{"SSL_CLIENT_V_START", "SSL.Client.V.Start"},
{"SSL_CLIENT_V_END", "SSL.Client.V.End"},
{"SSL_CLIENT_A_SIG", "SSL.Client.A.SIG"},
{"SSL_CLIENT_A_KEY", "SSL.Client.A.KEY"},
{"SSL_CLIENT_CERT", "SSL.Client.CERT"},
{"SSL_CLIENT_CERT_CHAINn", "SSL.Client.CERT.CHAINn"},
{"SSL_CLIENT_VERIFY", "SSL.Client.Verify"},
{"SSL_SERVER_M_VERSION", "SSL.Server.M.Version"},
{"SSL_SERVER_M_SERIAL", "SSL.Server.M.Serial"},
{"SSL_SERVER_S_DN", "SSL.Server.S.DN"},
{"SSL_SERVER_S_DN_x509", "SSL.Server.S.DN.x509"},
{"SSL_SERVER_S_DN_CN", "SSL.Server.S.DN.CN"},
{"SSL_SERVER_S_DN_EMAIL", "SSL.Server.S.DN.Email"},
{"SSL_SERVER_S_DN_O", "SSL.Server.S.DN.O"},
{"SSL_SERVER_S_DN_OU", "SSL.Server.S.DN.OU"},
{"SSL_SERVER_S_DN_C", "SSL.Server.S.DN.C"},
{"SSL_SERVER_S_DN_SP", "SSL.Server.S.DN.SP"},
{"SSL_SERVER_S_DN_L", "SSL.Server.S.DN.L"},
{"SSL_SERVER_I_DN", "SSL.Server.I.DN"},
{"SSL_SERVER_I_DN_x509", "SSL.Server.I.DN.x509"},
{"SSL_SERVER_I_DN_CN", "SSL.Server.I.DN.CN"},
{"SSL_SERVER_I_DN_EMAIL", "SSL.Server.I.DN.Email"},
{"SSL_SERVER_I_DN_O", "SSL.Server.I.DN.O"},
{"SSL_SERVER_I_DN_OU", "SSL.Server.I.DN.OU"},
{"SSL_SERVER_I_DN_C", "SSL.Server.I.DN.C"},
{"SSL_SERVER_I_DN_SP", "SSL.Server.I.DN.SP"},
{"SSL_SERVER_I_DN_L", "SSL.Server.I.DN.L"},
{"SSL_SERVER_V_START", "SSL.Server.V.Start"},
{"SSL_SERVER_V_END", "SSL.Server.V.End"},
{"SSL_SERVER_A_SIG", "SSL.Server.A.SIG"},
{"SSL_SERVER_A_KEY", "SSL.Server.A.KEY"},
{"SSL_SERVER_CERT", "SSL.Server.CERT"},
/* SSL Vars mapped from others */
/* Hmm, if we're running under mod_ssl w/ +CompatEnvVars, we set these
* twice... */
{"SSL_PROTOCOL_VERSION", "SSL.Protocol"},
{"SSLEAY_VERSION", "SSL.Version.Library"},
{"HTTPS_CIPHER", "SSL.Cipher"},
{"HTTPS_EXPORT", "SSL.Cipher.Export"},
{"HTTPS_SECRETKEYSIZE", "SSL.Cipher.UseKeySize"},
{"HTTPS_KEYSIZE", "SSL.Cipher.AlgKeySize"},
{"SSL_SERVER_KEY_SIZE", "SSL.Cipher.AlgKeySize"},
{"SSL_SERVER_CERTIFICATE", "SSL.Server.CERT"},
{"SSL_SERVER_CERT_START", "SSL.Server.V.Start"},
{"SSL_SERVER_CERT_END", "SSL.Server.V.End"},
{"SSL_SERVER_CERT_SERIAL", "SSL.Server.M.Serial"},
{"SSL_SERVER_SIGNATURE_ALGORITHM", "SSL.Server.A.SIG"},
{"SSL_SERVER_DN", "SSL.Server.S.DN"},
{"SSL_SERVER_CN", "SSL.Server.S.DN.CN"},
{"SSL_SERVER_EMAIL", "SSL.Server.S.DN.Email"},
{"SSL_SERVER_O", "SSL.Server.S.DN.O"},
{"SSL_SERVER_OU", "SSL.Server.S.DN.OU"},
{"SSL_SERVER_C", "SSL.Server.S.DN.C"},
{"SSL_SERVER_SP", "SSL.Server.S.DN.SP"},
{"SSL_SERVER_L", "SSL.Server.S.DN.L"},
{"SSL_SERVER_IDN", "SSL.Server.I.DN"},
{"SSL_SERVER_ICN", "SSL.Server.I.DN.CN"},
{"SSL_SERVER_IEMAIL", "SSL.Server.I.DN.Email"},
{"SSL_SERVER_IO", "SSL.Server.I.DN.O"},
{"SSL_SERVER_IOU", "SSL.Server.I.DN.OU"},
{"SSL_SERVER_IC", "SSL.Server.I.DN.C"},
{"SSL_SERVER_ISP", "SSL.Server.I.DN.SP"},
{"SSL_SERVER_IL", "SSL.Server.I.DN.L"},
{"SSL_CLIENT_CERTIFICATE", "SSL.Client.CERT"},
{"SSL_CLIENT_CERT_START", "SSL.Client.V.Start"},
{"SSL_CLIENT_CERT_END", "SSL.Client.V.End"},
{"SSL_CLIENT_CERT_SERIAL", "SSL.Client.M.Serial"},
{"SSL_CLIENT_SIGNATURE_ALGORITHM", "SSL.Client.A.SIG"},
{"SSL_CLIENT_DN", "SSL.Client.S.DN"},
{"SSL_CLIENT_CN", "SSL.Client.S.DN.CN"},
{"SSL_CLIENT_EMAIL", "SSL.Client.S.DN.Email"},
{"SSL_CLIENT_O", "SSL.Client.S.DN.O"},
{"SSL_CLIENT_OU", "SSL.Client.S.DN.OU"},
{"SSL_CLIENT_C", "SSL.Client.S.DN.C"},
{"SSL_CLIENT_SP", "SSL.Client.S.DN.SP"},
{"SSL_CLIENT_L", "SSL.Client.S.DN.L"},
{"SSL_CLIENT_IDN", "SSL.Client.I.DN"},
{"SSL_CLIENT_ICN", "SSL.Client.I.DN.CN"},
{"SSL_CLIENT_IEMAIL", "SSL.Client.I.DN.Email"},
{"SSL_CLIENT_IO", "SSL.Client.I.DN.O"},
{"SSL_CLIENT_IOU", "SSL.Client.I.DN.OU"},
{"SSL_CLIENT_IC", "SSL.Client.I.DN.C"},
{"SSL_CLIENT_ISP", "SSL.Client.I.DN.SP"},
{"SSL_CLIENT_IL", "SSL.Client.I.DN.L"},
{"SSL_EXPORT", "SSL.Cipher.Export"},
{"SSL_KEYSIZE", "SSL.Cipher.AlgKeySize"},
{"SSL_SECKEYSIZE", "SSL.Cipher.UseKeySize"},
{"SSL_SSLEAY_VERSION", "SSL.Version.Library"},
/* Old vars not in mod_ssl */
{"SSL_STRONG_CRYPTO", "SSL.Strong.Crypto"},
{"SSL_SERVER_KEY_EXP", "SSL.Server.Key.Exp"},
{"SSL_SERVER_KEY_ALGORITHM", "SSL.Server.Key.Algorithm"},
{"SSL_SERVER_KEY_SIZE", "SSL.Server.Key.Size"},
{"SSL_SERVER_SESSIONDIR", "SSL.Server.SessionDir"},
{"SSL_SERVER_CERTIFICATELOGDIR", "SSL.Server.CertificateLogDir"},
{"SSL_SERVER_CERTFILE", "SSL.Server.CertFile"},
{"SSL_SERVER_KEYFILE", "SSL.Server.KeyFile"},
{"SSL_SERVER_KEYFILETYPE", "SSL.Server.KeyFileType"},
{"SSL_CLIENT_KEY_EXP", "SSL.Client.Key.Exp"},
{"SSL_CLIENT_KEY_ALGORITHM", "SSL.Client.Key.Algorithm"},
{"SSL_CLIENT_KEY_SIZE", "SSL.Client.Key.Size"},
{NULL, NULL}
};
struct _http_vars
{
char *env_name;
char *hdf_name;
} HTTPVars[] = {
{"HTTP_ACCEPT", "Accept"},
{"HTTP_ACCEPT_CHARSET", "AcceptCharset"},
{"HTTP_ACCEPT_ENCODING", "AcceptEncoding"},
{"HTTP_ACCEPT_LANGUAGE", "AcceptLanguage"},
{"HTTP_COOKIE", "Cookie"},
{"HTTP_HOST", "Host"},
{"HTTP_USER_AGENT", "UserAgent"},
{"HTTP_IF_MODIFIED_SINCE", "IfModifiedSince"},
{"HTTP_REFERER", "Referer"},
{"HTTP_VIA", "Via"},
/* SOAP */
{"HTTP_SOAPACTION", "Soap.Action"},
{NULL, NULL}
};
static char *Argv0 = "";
int IgnoreEmptyFormVars = 0;
static int ExceptionsInit = 0;
NERR_TYPE CGIFinished = -1;
NERR_TYPE CGIUploadCancelled = -1;
NERR_TYPE CGIParseNotHandled = -1;
static NEOERR *_add_cgi_env_var (CGI *cgi, char *env, char *name)
{
NEOERR *err;
char *s;
err = cgiwrap_getenv (env, &s);
if (err != STATUS_OK) return nerr_pass (err);
if (s != NULL)
{
err = hdf_set_buf (cgi->hdf, name, s);
if (err != STATUS_OK)
{
free(s);
return nerr_pass (err);
}
}
return STATUS_OK;
}
char *cgi_url_unescape (char *value)
{
int i = 0, o = 0;
unsigned char *s = (unsigned char *)value;
if (s == NULL) return value;
while (s[i])
{
if (s[i] == '+')
{
s[o++] = ' ';
i++;
}
else if (s[i] == '%' && isxdigit(s[i+1]) && isxdigit(s[i+2]))
{
char num;
num = (s[i+1] >= 'A') ? ((s[i+1] & 0xdf) - 'A') + 10 : (s[i+1] - '0');
num *= 16;
num += (s[i+2] >= 'A') ? ((s[i+2] & 0xdf) - 'A') + 10 : (s[i+2] - '0');
s[o++] = num;
i+=3;
}
else {
s[o++] = s[i++];
}
}
if (i && o) s[o] = '\0';
return (char *)s;
}
NEOERR *cgi_js_escape (const char *in, char **esc)
{
return nerr_pass(neos_js_escape(in, esc));
}
NEOERR *cgi_url_escape (const char *buf, char **esc)
{
return nerr_pass(neos_url_escape(buf, esc, NULL));
}
NEOERR *cgi_url_escape_more (const char *in, char **esc,
const char *other)
{
return nerr_pass(neos_url_escape(in, esc, other));
}
NEOERR *cgi_url_validate (const char *buf, char **esc)
{
return nerr_pass(neos_url_validate(buf, esc));
}
static NEOERR *_parse_query (CGI *cgi, char *query)
{
NEOERR *err = STATUS_OK;
char *t, *k, *v, *l;
char buf[256];
char unnamed[10];
int unnamed_count = 0;
HDF *obj, *child;
if (query && *query)
{
k = strtok_r(query, "&", &l);
while (k && *k)
{
v = strchr(k, '=');
if (v == NULL)
{
v = "";
}
else
{
*v = '\0';
v++;
}
/* Check for some invalid query strings */
if (*k == 0) {
/* '?=foo' gets mapped in as Query._1=foo */
snprintf(unnamed,sizeof(unnamed), "_%d", unnamed_count++);
k = unnamed;
} else if (*k == '.') {
/* an hdf element can't start with a period */
*k = '_';
}
snprintf(buf, sizeof(buf), "Query.%s", cgi_url_unescape(k));
if (!(cgi->ignore_empty_form_vars && (*v == '\0')))
{
cgi_url_unescape(v);
obj = hdf_get_obj (cgi->hdf, buf);
if (obj != NULL)
{
int i = 0;
char buf2[10];
child = hdf_obj_child (obj);
if (child == NULL)
{
t = hdf_obj_value (obj);
err = hdf_set_value (obj, "0", t);
if (err != STATUS_OK) break;
i = 1;
}
else
{
while (child != NULL)
{
i++;
child = hdf_obj_next (child);
if (err != STATUS_OK) break;
}
if (err != STATUS_OK) break;
}
snprintf (buf2, sizeof(buf2), "%d", i);
err = hdf_set_value (obj, buf2, v);
if (err != STATUS_OK) break;
}
err = hdf_set_value (cgi->hdf, buf, v);
if (nerr_match(err, NERR_ASSERT)) {
STRING str;
string_init(&str);
nerr_error_string(err, &str);
ne_warn("Unable to set Query value: %s = %s: %s", buf, v, str.buf);
string_clear(&str);
nerr_ignore(&err);
}
if (err != STATUS_OK) break;
}
k = strtok_r(NULL, "&", &l);
}
}
return nerr_pass(err);
}
/* Is it an error if its a short read? */
static NEOERR *_parse_post_form (CGI *cgi)
{
NEOERR *err = STATUS_OK;
char *l, *query;
int len, r, o;
l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
if (l == NULL) return STATUS_OK;
len = atoi (l);
if (len <= 0) return STATUS_OK;
cgi->data_expected = len;
query = (char *) malloc (sizeof(char) * (len + 1));
if (query == NULL)
return nerr_raise (NERR_NOMEM,
"Unable to allocate memory to read POST input of length %d", len);
o = 0;
while (o < len)
{
cgiwrap_read (query + o, len - o, &r);
if (r <= 0) break;
o = o + r;
}
if (r < 0)
{
free(query);
return nerr_raise_errno (NERR_IO, "Short read on CGI POST input (%d < %d)",
o, len);
}
if (o != len)
{
free(query);
return nerr_raise (NERR_IO, "Short read on CGI POST input (%d < %d)",
o, len);
}
query[len] = '\0';
err = _parse_query (cgi, query);
free(query);
return nerr_pass(err);
}
static NEOERR *_parse_cookie (CGI *cgi)
{
NEOERR *err;
char *cookie;
char *k, *v, *l;
HDF *obj;
err = hdf_get_copy (cgi->hdf, "HTTP.Cookie", &cookie, NULL);
if (err != STATUS_OK) return nerr_pass(err);
if (cookie == NULL) return STATUS_OK;
err = hdf_set_value (cgi->hdf, "Cookie", cookie);
if (err != STATUS_OK)
{
free(cookie);
return nerr_pass(err);
}
obj = hdf_get_obj (cgi->hdf, "Cookie");
k = l = cookie;
while (*l && *l != '=' && *l != ';') l++;
while (*k)
{
if (*l == '=')
{
if (*l) *l++ = '\0';
v = l;
while (*l && *l != ';') l++;
if (*l) *l++ = '\0';
}
else
{
v = "";
if (*l) *l++ = '\0';
}
k = neos_strip (k);
v = neos_strip (v);
if (k[0] && v[0])
{
err = hdf_set_value (obj, k, v);
if (nerr_match(err, NERR_ASSERT)) {
STRING str;
string_init(&str);
nerr_error_string(err, &str);
ne_warn("Unable to set Cookie value: %s = %s: %s", k, v, str.buf);
string_clear(&str);
nerr_ignore(&err);
}
if (err) break;
}
k = l;
while (*l && *l != '=' && *l != ';') l++;
}
free (cookie);
return nerr_pass(err);
}
#ifdef ENABLE_REMOTE_DEBUG
static void _launch_debugger (CGI *cgi, char *display)
{
pid_t myPid, pid;
char buffer[127];
char *debugger;
HDF *obj;
char *allowed;
/* Only allow remote debugging from allowed hosts */
for (obj = hdf_get_child (cgi->hdf, "Config.Displays");
obj; obj = hdf_obj_next (obj))
{
allowed = hdf_obj_value (obj);
if (allowed && !strcmp (display, allowed)) break;
}
if (obj == NULL) return;
myPid = getpid();
if ((pid = fork()) < 0)
return;
if ((debugger = hdf_get_value (cgi->hdf, "Config.Debugger", NULL)) == NULL)
{
debugger = "/usr/local/bin/sudo /usr/local/bin/ddd -display %s %s %d";
}
if (!pid)
{
sprintf(buffer, debugger, display, Argv0, myPid);
execl("/bin/sh", "sh", "-c", buffer, NULL);
}
else
{
sleep(60);
}
}
#endif
static NEOERR *cgi_pre_parse (CGI *cgi)
{
NEOERR *err;
int x = 0;
char buf[256];
char *query;
while (CGIVars[x].env_name)
{
snprintf (buf, sizeof(buf), "CGI.%s", CGIVars[x].hdf_name);
err = _add_cgi_env_var(cgi, CGIVars[x].env_name, buf);
if (err != STATUS_OK) return nerr_pass (err);
x++;
}
x = 0;
while (HTTPVars[x].env_name)
{
snprintf (buf, sizeof(buf), "HTTP.%s", HTTPVars[x].hdf_name);
err = _add_cgi_env_var(cgi, HTTPVars[x].env_name, buf);
if (err != STATUS_OK) return nerr_pass (err);
x++;
}
err = _parse_cookie(cgi);
if (err != STATUS_OK) return nerr_pass (err);
err = hdf_get_copy (cgi->hdf, "CGI.QueryString", &query, NULL);
if (err != STATUS_OK) return nerr_pass (err);
if (query != NULL)
{
err = _parse_query(cgi, query);
free(query);
if (err != STATUS_OK) return nerr_pass (err);
}
{
char *d = hdf_get_value(cgi->hdf, "Query.debug_pause", NULL);
char *d_p = hdf_get_value(cgi->hdf, "Config.DebugPassword", NULL);
if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
d && d_p && !strcmp(d, d_p)) {
sleep(20);
}
}
#ifdef ENABLE_REMOTE_DEBUG
{
char *display;
display = hdf_get_value (cgi->hdf, "Query.xdisplay", NULL);
if (display)
{
fprintf(stderr, "** Got display %s\n", display);
_launch_debugger(cgi, display);
}
}
#endif
return STATUS_OK;
}
NEOERR *cgi_register_parse_cb(CGI *cgi, const char *method, const char *ctype,
void *rock, CGI_PARSE_CB parse_cb)
{
struct _cgi_parse_cb *my_pcb;
if (method == NULL || ctype == NULL)
return nerr_raise(NERR_ASSERT, "method and type must not be NULL to register cb");
my_pcb = (struct _cgi_parse_cb *) calloc(1, sizeof(struct _cgi_parse_cb));
if (my_pcb == NULL)
return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register parse cb");
my_pcb->method = strdup(method);
my_pcb->ctype = strdup(ctype);
if (my_pcb->method == NULL || my_pcb->ctype == NULL)
{
if (my_pcb->method != NULL)
free(my_pcb->method);
if (my_pcb->ctype != NULL)
free(my_pcb->ctype);
free(my_pcb);
return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register parse cb");
}
if (!strcmp(my_pcb->method, "*"))
my_pcb->any_method = 1;
if (!strcmp(my_pcb->ctype, "*"))
my_pcb->any_ctype = 1;
my_pcb->rock = rock;
my_pcb->parse_cb = parse_cb;
my_pcb->next = cgi->parse_callbacks;
cgi->parse_callbacks = my_pcb;
return STATUS_OK;
}
NEOERR *cgi_parse (CGI *cgi)
{
NEOERR *err;
char *method, *type;
struct _cgi_parse_cb *pcb;
method = hdf_get_value (cgi->hdf, "CGI.RequestMethod", "GET");
type = hdf_get_value (cgi->hdf, "CGI.ContentType", NULL);
/* Walk the registered parse callbacks for a matching one */
pcb = cgi->parse_callbacks;
while (pcb != NULL)
{
if ( (pcb->any_method || !strcasecmp(pcb->method, method)) &&
(pcb->any_ctype || (type && !strcasecmp(pcb->ctype, type))) )
{
err = pcb->parse_cb(cgi, method, type, pcb->rock);
if (err && !nerr_handle(&err, CGIParseNotHandled))
return nerr_pass(err);
}
pcb = pcb->next;
}
/* Fallback to internal methods */
if (!strcmp(method, "POST"))
{
if (type && !strcmp(type, "application/x-www-form-urlencoded"))
{
err = _parse_post_form(cgi);
if (err != STATUS_OK) return nerr_pass (err);
}
else if (type && !strncmp (type, "multipart/form-data", 19))
{
err = parse_rfc2388 (cgi);
if (err != STATUS_OK) return nerr_pass (err);
}
#if 0
else
{
int len, x, r;
char *l;
char buf[4096];
FILE *fp;
fp = fopen("/tmp/upload", "w");
l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
if (l == NULL) return STATUS_OK;
len = atoi (l);
x = 0;
while (x < len)
{
if (len-x > sizeof(buf))
cgiwrap_read (buf, sizeof(buf), &r);
else
cgiwrap_read (buf, len - x, &r);
fwrite (buf, 1, r, fp);
x += r;
}
fclose (fp);
if (err) return nerr_pass(err);
}
#endif
}
else if (!strcmp(method, "PUT"))
{
FILE *fp;
int len, x, r, w;
char *l;
char buf[4096];
int unlink_files = hdf_get_int_value(cgi->hdf, "Config.Upload.Unlink", 1);
err = open_upload(cgi, unlink_files, &fp);
if (err) return nerr_pass(err);
l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
if (l == NULL) return STATUS_OK;
len = atoi (l);
if (len <= 0) return STATUS_OK;
x = 0;
while (x < len)
{
if (len-x > sizeof(buf))
cgiwrap_read (buf, sizeof(buf), &r);
else
cgiwrap_read (buf, len - x, &r);
w = fwrite (buf, sizeof(char), r, fp);
if (w != r)
{
err = nerr_raise_errno(NERR_IO, "Short write on PUT: %d < %d", w, r);
break;
}
x += r;
}
if (err) return nerr_pass(err);
fseek(fp, 0, SEEK_SET);
l = hdf_get_value(cgi->hdf, "CGI.PathInfo", NULL);
if (l) err = hdf_set_value (cgi->hdf, "PUT", l);
if (err) return nerr_pass(err);
if (type) err = hdf_set_value (cgi->hdf, "PUT.Type", type);
if (err) return nerr_pass(err);
err = hdf_set_int_value (cgi->hdf, "PUT.FileHandle", uListLength(cgi->files));
if (err) return nerr_pass(err);
if (!unlink_files)
{
char *name;
err = uListGet(cgi->filenames, uListLength(cgi->filenames)-1,
(void *)&name);
if (err) return nerr_pass(err);
err = hdf_set_value (cgi->hdf, "PUT.FileName", name);
if (err) return nerr_pass(err);
}
}
return STATUS_OK;
}
NEOERR *cgi_init (CGI **cgi, HDF *hdf)
{
NEOERR *err = STATUS_OK;
CGI *mycgi;
if (ExceptionsInit == 0)
{
err = nerr_init();
if (err) return nerr_pass(err);
err = nerr_register(&CGIFinished, "CGIFinished");
if (err) return nerr_pass(err);
err = nerr_register(&CGIUploadCancelled, "CGIUploadCancelled");
if (err) return nerr_pass(err);
err = nerr_register(&CGIUploadCancelled, "CGIParseNotHandled");
if (err) return nerr_pass(err);
ExceptionsInit = 1;
}
*cgi = NULL;
mycgi = (CGI *) calloc (1, sizeof(CGI));
if (mycgi == NULL)
return nerr_raise(NERR_NOMEM, "Unable to allocate space for CGI");
mycgi->time_start = ne_timef();
mycgi->ignore_empty_form_vars = IgnoreEmptyFormVars;
do
{
if (hdf == NULL)
{
err = hdf_init (&(mycgi->hdf));
if (err != STATUS_OK) break;
}
else
{
mycgi->hdf = hdf;
}
err = cgi_pre_parse (mycgi);
if (err != STATUS_OK) break;
} while (0);
if (err == STATUS_OK)
*cgi = mycgi;
else
{
cgi_destroy(&mycgi);
}
return nerr_pass(err);
}
static void _destroy_tmp_file(char *filename)
{
unlink(filename);
free(filename);
}
void cgi_destroy (CGI **cgi)
{
CGI *my_cgi;
if (!cgi || !*cgi)
return;
my_cgi = *cgi;
if (my_cgi->hdf)
hdf_destroy (&(my_cgi->hdf));
if (my_cgi->buf)
free(my_cgi->buf);
if (my_cgi->files)
uListDestroyFunc(&(my_cgi->files), (void (*)(void *))fclose);
if (my_cgi->filenames)
uListDestroyFunc(&(my_cgi->filenames), (void (*)(void *))_destroy_tmp_file);
free (*cgi);
*cgi = NULL;
}
static NEOERR *render_cb (void *ctx, char *buf)
{
STRING *str = (STRING *)ctx;
NEOERR *err;
err = nerr_pass(string_append(str, buf));
return err;
}
static NEOERR *cgi_headers (CGI *cgi)
{
NEOERR *err = STATUS_OK;
HDF *obj, *child;
char *s, *charset = NULL;
if (hdf_get_int_value (cgi->hdf, "Config.NoCache", 0))
{
/* Ok, we try really hard to defeat caches here */
/* this isn't in any HTTP rfc's, it just seems to be a convention */
err = cgiwrap_writef ("Pragma: no-cache\r\n");
if (err != STATUS_OK) return nerr_pass (err);
err = cgiwrap_writef ("Expires: Fri, 01 Jan 1990 00:00:00 GMT\r\n");
if (err != STATUS_OK) return nerr_pass (err);
err = cgiwrap_writef ("Cache-control: no-cache, must-revalidate, no-cache=\"Set-Cookie\", private\r\n");
if (err != STATUS_OK) return nerr_pass (err);
}
obj = hdf_get_obj (cgi->hdf, "cgiout");
if (obj)
{
s = hdf_get_value (obj, "Status", NULL);
if (s)
err = cgiwrap_writef ("Status: %s\r\n", s);
if (err != STATUS_OK) return nerr_pass (err);
s = hdf_get_value (obj, "Location", NULL);
if (s)
err = cgiwrap_writef ("Location: %s\r\n", s);
if (err != STATUS_OK) return nerr_pass (err);
child = hdf_get_obj (cgi->hdf, "cgiout.other");
if (child)
{
child = hdf_obj_child (child);
while (child != NULL)
{
s = hdf_obj_value (child);
err = cgiwrap_writef ("%s\r\n", s);
if (err != STATUS_OK) return nerr_pass (err);
child = hdf_obj_next(child);
}
}
charset = hdf_get_value (obj, "charset", NULL);
s = hdf_get_value (obj, "ContentType", "text/html");
if (charset)
err = cgiwrap_writef ("Content-Type: %s; charset=%s\r\n\r\n", s, charset);
else
err = cgiwrap_writef ("Content-Type: %s\r\n\r\n", s);
if (err != STATUS_OK) return nerr_pass (err);
}
else
{
/* Default */
err = cgiwrap_writef ("Content-Type: text/html\r\n\r\n");
if (err != STATUS_OK) return nerr_pass (err);
}
return STATUS_OK;
}
#if defined(HTML_COMPRESSION)
/* Copy these here from zutil.h, which we aren't supposed to include */
#define DEF_MEM_LEVEL 8
#define OS_CODE 0x03
static NEOERR *cgi_compress (STRING *str, char *obuf, int *olen)
{
z_stream stream;
int err;
stream.next_in = (Bytef*)str->buf;
stream.avail_in = (uInt)str->len;
stream.next_out = (Bytef*)obuf;
stream.avail_out = (uInt)*olen;
if ((uLong)stream.avail_out != *olen)
return nerr_raise(NERR_NOMEM, "Destination too big: %d", *olen);
stream.zalloc = (alloc_func)0;
stream.zfree = (free_func)0;
stream.opaque = (voidpf)0;
/* err = deflateInit(&stream, Z_DEFAULT_COMPRESSION); */
err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
if (err != Z_OK)
return nerr_raise(NERR_SYSTEM, "deflateInit2 returned %d", err);
err = deflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
deflateEnd(&stream);
return nerr_raise(NERR_SYSTEM, "deflate returned %d", err);
}
*olen = stream.total_out;
err = deflateEnd(&stream);
return STATUS_OK;
}
#endif
/* This ws strip function is Dave's version, designed to make debug
* easier, and the output a bit smaller... but not as small as it could
* be: essentially, it strips all empty lines, all extra space at the
* end of the line, except in pre/textarea tags.
*
* Ok, expanding to 3 levels:
* 0 - No stripping
* 1 - Dave's debug stripper (as above)
* 2 - strip all extra white space
*
* We don't currently strip white space in a tag
*
* */
#if 0
static void debug_output(char *header, char *s, int n)
{
int x;
fprintf (stderr, "%s ", header);
for (x = 0; x < n; x++)
{
fprintf (stderr, "%c", s[x]);
}
fprintf(stderr, "\n");
}
#endif
void cgi_html_ws_strip(STRING *str, int level)
{
int ws = 0;
int seen_nonws = level > 1;
int i, o, l;
char *ch;
i = o = 0;
if (str->len) {
ws = isspace(str->buf[0]);
}
while (i < str->len)
{
if (str->buf[i] == '<')
{
str->buf[o++] = str->buf[i++];
if (!strncasecmp(str->buf + i, "textarea", 8))
{
ch = str->buf + i;
do
{
ch = strchr(ch, '<');
if (ch == NULL)
{
memmove(str->buf + o, str->buf + i, str->len - i);
str->len = o + str->len - i;
str->buf[str->len] = '\0';
return;
}
ch++;
} while (strncasecmp(ch, "/textarea>", 10));
ch += 10;
l = ch - str->buf - i;
memmove(str->buf + o, str->buf + i, l);
o += l;
i += l;
}
else if (!strncasecmp(str->buf + i, "pre", 3))
{
ch = str->buf + i;
do
{
ch = strchr(ch, '<');
if (ch == NULL)
{
memmove(str->buf + o, str->buf + i, str->len - i);
str->len = o + str->len - i;
str->buf[str->len] = '\0';
return;
}
ch++;
} while (strncasecmp(ch, "/pre>", 5));
ch += 5;
l = ch - str->buf - i;
memmove(str->buf + o, str->buf + i, l);
o += l;
i += l;
}
else
{
ch = strchr(str->buf + i, '>');
if (ch == NULL)
{
memmove(str->buf + o, str->buf + i, str->len - i);
str->len = o + str->len - i;
str->buf[str->len] = '\0';
return;
}
ch++;
/* debug_output("copying tag", str->buf + i, ch - str->buf - i);
* */
l = ch - str->buf - i;
memmove(str->buf + o, str->buf + i, l);
o += l;
i += l;
}
/* debug_output("result", str->buf, o); */
seen_nonws = 1;
ws = 0;
}
else if (str->buf[i] == '\n')
{
/* This has the effect of erasing all whitespace at the eol plus
* erasing all blank lines */
while (o && isspace(str->buf[o-1])) o--;
str->buf[o++] = str->buf[i++];
ws = level > 1;
seen_nonws = level > 1;
}
else if (seen_nonws && isspace(str->buf[i]))
{
if (ws)
{
i++;
}
else
{
str->buf[o++] = str->buf[i++];
ws = 1;
}
}
else
{
seen_nonws = 1;
ws = 0;
str->buf[o++] = str->buf[i++];
}
}
str->len = o;
str->buf[str->len] = '\0';
}
NEOERR *cgi_output (CGI *cgi, STRING *str)
{
NEOERR *err = STATUS_OK;
double dis;
int is_html = 0;
int use_deflate = 0;
int use_gzip = 0;
int do_debug = 0;
int do_timefooter = 0;
int ws_strip_level = 0;
char *s, *e;
s = hdf_get_value (cgi->hdf, "Query.debug", NULL);
e = hdf_get_value (cgi->hdf, "Config.DebugPassword", NULL);
if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
s && e && !strcmp(s, e)) do_debug = 1;
do_timefooter = hdf_get_int_value (cgi->hdf, "Config.TimeFooter", 1);
ws_strip_level = hdf_get_int_value (cgi->hdf, "Config.WhiteSpaceStrip", 1);
dis = ne_timef();
s = hdf_get_value (cgi->hdf, "cgiout.ContentType", "text/html");
if (!strcasecmp(s, "text/html"))
is_html = 1;
#if defined(HTML_COMPRESSION)
/* Determine whether or not we can compress the output */
if (is_html && hdf_get_int_value (cgi->hdf, "Config.CompressionEnabled", 0))
{
err = hdf_get_copy (cgi->hdf, "HTTP.AcceptEncoding", &s, NULL);
if (err != STATUS_OK) return nerr_pass (err);
if (s)
{
char *next;
e = strtok_r (s, ",", &next);
while (e && !use_deflate)
{
if (strstr(e, "deflate") != NULL)
{
use_deflate = 1;
use_gzip = 0;
}
else if (strstr(e, "gzip") != NULL)
use_gzip = 1;
e = strtok_r (NULL, ",", &next);
}
free (s);
}
s = hdf_get_value (cgi->hdf, "HTTP.UserAgent", NULL);
if (s)
{
if (strstr(s, "MSIE 4") || strstr(s, "MSIE 5") || strstr(s, "MSIE 6"))
{
e = hdf_get_value (cgi->hdf, "HTTP.Accept", NULL);
if (e && !strcmp(e, "*/*"))
{
use_deflate = 0;
use_gzip = 0;
}
}
else
{
if (strncasecmp(s, "mozilla/5.", 10))
{
use_deflate = 0;
use_gzip = 0;
}
}
}
else
{
use_deflate = 0;
use_gzip = 0;
}
if (use_deflate)
{
err = hdf_set_value (cgi->hdf, "cgiout.other.encoding",
"Content-Encoding: deflate");
}
else if (use_gzip)
{
err = hdf_set_value (cgi->hdf, "cgiout.other.encoding",
"Content-Encoding: gzip");
}
if (err != STATUS_OK) return nerr_pass(err);
}
#endif
err = cgi_headers(cgi);
if (err != STATUS_OK) return nerr_pass(err);
if (is_html)
{
char buf[50];
int x;
if (do_timefooter)
{
snprintf (buf, sizeof(buf), "\n<!-- %5.3f:%d -->\n",
dis - cgi->time_start, use_deflate || use_gzip);
err = string_append (str, buf);
if (err != STATUS_OK) return nerr_pass(err);
}
if (ws_strip_level)
{
cgi_html_ws_strip(str, ws_strip_level);
}
if (do_debug)
{
err = string_append (str, "<hr>");
if (err != STATUS_OK) return nerr_pass(err);
x = 0;
while (1)
{
char *k, *v;
err = cgiwrap_iterenv (x, &k, &v);
if (err != STATUS_OK) return nerr_pass(err);
if (k == NULL) break;
err =string_appendf (str, "%s = %s<br>", k, v);
if (err != STATUS_OK) return nerr_pass(err);
free(k);
free(v);
x++;
}
err = string_append (str, "<pre>");
if (err != STATUS_OK) return nerr_pass(err);
err = hdf_dump_str (cgi->hdf, NULL, 0, str);
if (err != STATUS_OK) return nerr_pass(err);
}
}
#if defined(HTML_COMPRESSION)
if (is_html && (use_deflate || use_gzip))
{
char *dest;
static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
char gz_buf[20]; /* gzip header/footer buffer, len of header is 10 bytes */
unsigned int crc = 0;
int len2;
if (use_gzip)
{
crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, (const Bytef *)(str->buf), str->len);
}
len2 = str->len * 2;
dest = (char *) malloc (sizeof(char) * len2);
if (dest != NULL)
{
do {
err = cgi_compress (str, dest, &len2);
if (err == STATUS_OK)
{
if (use_gzip)
{
/* I'm using sprintf instead of cgiwrap_writef since
* the wrapper writef might not handle values with
* embedded NULLs... though I should fix the python one
* now as well */
sprintf(gz_buf, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/,
OS_CODE);
err = cgiwrap_write(gz_buf, 10);
}
if (err != STATUS_OK) break;
err = cgiwrap_write(dest, len2);
if (err != STATUS_OK) break;
if (use_gzip)
{
/* write crc and len in network order */
sprintf(gz_buf, "%c%c%c%c%c%c%c%c",
(0xff & (crc >> 0)),
(0xff & (crc >> 8)),
(0xff & (crc >> 16)),
(0xff & (crc >> 24)),
(0xff & (str->len >> 0)),
(0xff & (str->len >> 8)),
(0xff & (str->len >> 16)),
(0xff & (str->len >> 24)));
err = cgiwrap_write(gz_buf, 8);
if (err != STATUS_OK) break;
}
}
else
{
nerr_log_error (err);
err = cgiwrap_write(str->buf, str->len);
}
} while (0);
free (dest);
}
else
{
err = cgiwrap_write(str->buf, str->len);
}
}
else
#endif
{
err = cgiwrap_write(str->buf, str->len);
}
return nerr_pass(err);
}
NEOERR *cgi_html_escape_strfunc(const char *str, char **ret)
{
return nerr_pass(html_escape_alloc(str, strlen(str), ret));
}
NEOERR *cgi_html_strip_strfunc(const char *str, char **ret)
{
return nerr_pass(html_strip_alloc(str, strlen(str), ret));
}
NEOERR *cgi_text_html_strfunc(const char *str, char **ret)
{
return nerr_pass(convert_text_html_alloc(str, strlen(str), ret));
}
NEOERR *cgi_register_strfuncs(CSPARSE *cs)
{
NEOERR *err;
err = cs_register_esc_strfunc(cs, "url_escape", cgi_url_escape);
if (err != STATUS_OK) return nerr_pass(err);
err = cs_register_esc_strfunc(cs, "html_escape", cgi_html_escape_strfunc);
if (err != STATUS_OK) return nerr_pass(err);
err = cs_register_strfunc(cs, "text_html", cgi_text_html_strfunc);
if (err != STATUS_OK) return nerr_pass(err);
err = cs_register_esc_strfunc(cs, "js_escape", cgi_js_escape);
if (err != STATUS_OK) return nerr_pass(err);
err = cs_register_strfunc(cs, "html_strip", cgi_html_strip_strfunc);
if (err != STATUS_OK) return nerr_pass(err);
err = cs_register_esc_strfunc(cs, "url_validate", cgi_url_validate);
if (err != STATUS_OK) return nerr_pass(err);
return STATUS_OK;
}
NEOERR *cgi_cs_init(CGI *cgi, CSPARSE **cs)
{
NEOERR *err;
*cs = NULL;
do
{
err = cs_init (cs, cgi->hdf);
if (err != STATUS_OK) break;
err = cgi_register_strfuncs(*cs);
if (err != STATUS_OK) break;
} while (0);
if (err && *cs) cs_destroy(cs);
return nerr_pass(err);
}
NEOERR *cgi_display (CGI *cgi, const char *cs_file)
{
NEOERR *err = STATUS_OK;
char *debug;
CSPARSE *cs = NULL;
STRING str;
int do_dump = 0;
char *t;
string_init(&str);
debug = hdf_get_value (cgi->hdf, "Query.debug", NULL);
t = hdf_get_value (cgi->hdf, "Config.DumpPassword", NULL);
if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
debug && t && !strcmp (debug, t)) do_dump = 1;
do
{
err = cs_init (&cs, cgi->hdf);
if (err != STATUS_OK) break;
err = cgi_register_strfuncs(cs);
if (err != STATUS_OK) break;
err = cs_parse_file (cs, cs_file);
if (err != STATUS_OK) break;
if (do_dump)
{
cgiwrap_writef("Content-Type: text/plain\n\n");
hdf_dump_str(cgi->hdf, "", 0, &str);
cs_dump(cs, &str, render_cb);
cgiwrap_writef("%s", str.buf);
break;
}
else
{
err = cs_render (cs, &str, render_cb);
if (err != STATUS_OK) break;
}
err = cgi_output(cgi, &str);
if (err != STATUS_OK) break;
} while (0);
cs_destroy(&cs);
string_clear (&str);
return nerr_pass(err);
}
void cgi_neo_error (CGI *cgi, NEOERR *err)
{
STRING str;
string_init(&str);
cgiwrap_writef("Status: 500\n");
cgiwrap_writef("Content-Type: text/html\n\n");
cgiwrap_writef("<html><body>\nAn error occured:<pre>");
nerr_error_traceback(err, &str);
cgiwrap_write(str.buf, str.len);
cgiwrap_writef("</pre></body></html>\n");
}
void cgi_error (CGI *cgi, const char *fmt, ...)
{
va_list ap;
cgiwrap_writef("Status: 500\n");
cgiwrap_writef("Content-Type: text/html\n\n");
cgiwrap_writef("<html><body>\nAn error occured:<pre>");
va_start (ap, fmt);
cgiwrap_writevf (fmt, ap);
va_end (ap);
cgiwrap_writef("</pre></body></html>\n");
}
void cgi_debug_init (int argc, char **argv)
{
FILE *fp;
char line[4096];
char *v, *k;
Argv0 = argv[0];
if (argc)
{
fp = fopen(argv[1], "r");
if (fp == NULL)
return;
while (fgets(line, sizeof(line), fp) != NULL)
{
v = strchr(line, '=');
if (v != NULL)
{
*v = '\0';
v = neos_strip(v+1);
k = neos_strip(line);
cgiwrap_putenv (line, v);
}
}
fclose(fp);
}
}
void cgi_vredirect (CGI *cgi, int uri, const char *fmt, va_list ap)
{
cgiwrap_writef ("Status: 302\r\n");
cgiwrap_writef ("Content-Type: text/html\r\n");
cgiwrap_writef ("Pragma: no-cache\r\n");
cgiwrap_writef ("Expires: Fri, 01 Jan 1999 00:00:00 GMT\r\n");
cgiwrap_writef ("Cache-control: no-cache, no-cache=\"Set-Cookie\", private\r\n");
if (uri)
{
cgiwrap_writef ("Location: ");
}
else
{
char *host;
int https = 0;
if (!strcmp(hdf_get_value(cgi->hdf, "CGI.HTTPS", "off"), "on"))
{
https = 1;
}
host = hdf_get_value (cgi->hdf, "HTTP.Host", NULL);
if (host == NULL)
host = hdf_get_value (cgi->hdf, "CGI.ServerName", "localhost");
cgiwrap_writef ("Location: %s://%s", https ? "https" : "http", host);
if ((strchr(host, ':') == NULL)) {
int port = hdf_get_int_value(cgi->hdf, "CGI.ServerPort", 80);
if (!((https && port == 443) || (!https && port == 80)))
{
cgiwrap_writef(":%d", port);
}
}
}
cgiwrap_writevf (fmt, ap);
cgiwrap_writef ("\r\n\r\n");
cgiwrap_writef ("Redirect page<br><br>\n");
#if 0
/* Apparently this crashes on some computers... I don't know if its
* legal to reuse the va_list */
cgiwrap_writef (" Destination: <A HREF=\"");
cgiwrap_writevf (fmt, ap);
cgiwrap_writef ("\">");
cgiwrap_writevf (fmt, ap);
cgiwrap_writef ("</A><BR>\n<BR>\n");
#endif
cgiwrap_writef ("There is nothing to see here, please move along...");
}
void cgi_redirect (CGI *cgi, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
cgi_vredirect (cgi, 0, fmt, ap);
va_end(ap);
return;
}
void cgi_redirect_uri (CGI *cgi, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
cgi_vredirect (cgi, 1, fmt, ap);
va_end(ap);
return;
}
char *cgi_cookie_authority (CGI *cgi, const char *host)
{
HDF *obj;
char *domain;
int hlen = 0, dlen = 0;
if (host == NULL)
{
host = hdf_get_value (cgi->hdf, "HTTP.Host", NULL);
}
if (host == NULL) return NULL;
while (host[hlen] && host[hlen] != ':') hlen++;
obj = hdf_get_obj (cgi->hdf, "CookieAuthority");
if (obj == NULL) return NULL;
for (obj = hdf_obj_child (obj);
obj;
obj = hdf_obj_next (obj))
{
domain = hdf_obj_value (obj);
dlen = strlen(domain);
if (hlen >= dlen)
{
if (!strncasecmp (host + hlen - dlen, domain, dlen))
return domain;
}
}
return NULL;
}
/* For more information about Cookies, see:
* The original Netscape Cookie Spec:
* http://wp.netscape.com/newsref/std/cookie_spec.html
*
* HTTP State Management Mechanism
* http://www.ietf.org/rfc/rfc2109.txt
*/
NEOERR *cgi_cookie_set (CGI *cgi, const char *name, const char *value,
const char *path, const char *domain,
const char *time_str, int persistent, int secure)
{
NEOERR *err;
STRING str;
char my_time[256];
if (path == NULL) path = "/";
string_init(&str);
do {
err = string_appendf(&str, "Set-Cookie: %s=%s; path=%s", name, value, path);
if (err) break;
if (persistent)
{
if (time_str == NULL)
{
/* Expires in one year */
time_t exp_date = time(NULL) + 31536000;
strftime (my_time, 48, "%A, %d-%b-%Y 23:59:59 GMT",
gmtime (&exp_date));
time_str = my_time;
}
err = string_appendf(&str, "; expires=%s", time_str);
if (err) break;
}
if (domain)
{
err = string_appendf(&str, "; domain=%s", domain);
if (err) break;
}
if (secure)
{
err = string_append(&str, "; secure");
if (err) break;
}
err = string_append(&str, "\r\n");
} while (0);
if (err)
{
string_clear(&str);
return nerr_pass(err);
}
cgiwrap_write(str.buf, str.len);
string_clear(&str);
return STATUS_OK;
}
/* This will actually issue up to three set cookies, attempting to clear
* the damn thing. */
NEOERR *cgi_cookie_clear (CGI *cgi, const char *name, const char *domain,
const char *path)
{
if (path == NULL) path = "/";
if (domain)
{
if (domain[0] == '.')
{
cgiwrap_writef ("Set-Cookie: %s=; path=%s; domain=%s;"
"expires=Thursday, 01-Jan-1970 00:00:00 GMT\r\n", name, path,
domain + 1);
}
cgiwrap_writef("Set-Cookie: %s=; path=%s; domain=%s;"
"expires=Thursday, 01-Jan-1970 00:00:00 GMT\r\n", name, path,
domain);
}
cgiwrap_writef("Set-Cookie: %s=; path=%s; "
"expires=Thursday, 01-Jan-1970 00:00:00 GMT\r\n", name, path);
return STATUS_OK;
}