summaryrefslogtreecommitdiffstats
path: root/cgi
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /cgi
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'cgi')
-rw-r--r--cgi/.gitignore2
-rw-r--r--cgi/Makefile.am11
-rw-r--r--cgi/cgi.c842
-rw-r--r--cgi/cgi.h170
-rw-r--r--cgi/cgicheckbox.c41
-rw-r--r--cgi/cgicookie.c72
-rw-r--r--cgi/cgidaemon.c376
-rw-r--r--cgi/cgidaemond.c484
-rw-r--r--cgi/cgiextrapath.c21
-rw-r--r--cgi/cgihasversion.c16
-rw-r--r--cgi/cgihttpscriptptr.c48
-rw-r--r--cgi/cgihttpsscriptptr.c47
-rw-r--r--cgi/cgiinput.c154
-rw-r--r--cgi/cginocache.c23
-rw-r--r--cgi/cgiredirect.c19
-rw-r--r--cgi/cgirelscriptptr.c28
-rw-r--r--cgi/cgiselect.c134
-rw-r--r--cgi/cgitextarea.c120
-rw-r--r--cgi/cgiuseragent.c52
-rw-r--r--cgi/cgiversion.c32
-rw-r--r--cgi/configure.in155
21 files changed, 2847 insertions, 0 deletions
diff --git a/cgi/.gitignore b/cgi/.gitignore
new file mode 100644
index 0000000..aed92c0
--- /dev/null
+++ b/cgi/.gitignore
@@ -0,0 +1,2 @@
+/cgi_config.h
+/cgi_config.h.in
diff --git a/cgi/Makefile.am b/cgi/Makefile.am
new file mode 100644
index 0000000..fa3fd70
--- /dev/null
+++ b/cgi/Makefile.am
@@ -0,0 +1,11 @@
+#
+# Copyright 1998 - 2007 Double Precision, Inc. See COPYING for
+# distribution information.
+
+
+noinst_LTLIBRARIES=libcgi.la
+
+libcgi_la_SOURCES=cgi.h cgi.c cgiextrapath.c cgiselect.c cgicheckbox.c\
+ cgiinput.c cgihttpscriptptr.c cgihttpsscriptptr.c cgirelscriptptr.c \
+ cginocache.c cgiredirect.c cgitextarea.c cgiversion.c cgihasversion.c \
+ cgicookie.c cgiuseragent.c cgidaemon.c cgidaemond.c
diff --git a/cgi/cgi.c b/cgi/cgi.c
new file mode 100644
index 0000000..f94bdf0
--- /dev/null
+++ b/cgi/cgi.c
@@ -0,0 +1,842 @@
+/*
+** Copyright 1998 - 2012 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifndef CGIMAXARG
+#define CGIMAXARG 500000
+#endif
+
+#ifndef CGIMAXFORMDATAARG
+#define CGIMAXFORMDATAARG 2000000
+#endif
+
+#if CGIMAXARG < 256
+#error CGIMAXARG too small
+#endif
+
+#if CGIMAXFORMDATAARG < 1024
+#error CGIMAXFORMDATAARG too small
+#endif
+
+#if CGIFORMDATA
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "rfc2045/rfc2045.h"
+
+static void cgi_formdata(unsigned long);
+
+#ifndef HAVE_STRNCASECMP
+extern int strncasecmp(const char *, const char *, size_t);
+#endif
+
+static int cgiformfd;
+static char hascgiformfd=0;
+static struct rfc2045 *rfc2045p=0;
+
+#endif
+
+extern void error(const char *);
+
+static void enomem()
+{
+ error("Out of memory.");
+}
+
+static char *cgi_args=0;
+
+struct cgi_arglist *cgi_arglist=0;
+
+static size_t cgi_maxarg()
+{
+ const char *p=getenv("SQWEBMAIL_MAXARGSIZE");
+ size_t n=0;
+
+ if (p)
+ n=atoi(p);
+
+ if (n < CGIMAXARG)
+ n=CGIMAXARG;
+ return n;
+}
+
+static size_t cgi_maxformarg()
+{
+ const char *p=getenv("SQWEBMAIL_MAXATTSIZE");
+ size_t n=0;
+
+ if (p)
+ n=atoi(p);
+
+ if (n < CGIMAXFORMDATAARG)
+ n=CGIMAXFORMDATAARG;
+ return n;
+}
+
+/*
+** Set up CGI arguments. Initializes cgi_arglist link list.
+**
+** arg1<NUL>value1<NUL>arg2<NUL>value2<NUL> ... argn<NUL>valuen<NUL><NUL>
+*/
+
+static void cgi_setup_1();
+
+void cgi_setup()
+{
+struct cgi_arglist *p;
+
+ cgi_setup_1();
+
+ if (cgi_arglist)
+ cgi_arglist->prev=0;
+
+ /* Initialize the prev pointer */
+
+ for (p=cgi_arglist; p; p=p->next)
+ if (p->next)
+ p->next->prev=p;
+}
+
+
+static void cgi_setup_1()
+{
+char *p=getenv("REQUEST_METHOD"), *q, *r;
+char *args;
+unsigned long cl;
+int c;
+struct cgi_arglist *argp;
+
+ if (p && strcmp(p, "GET") == 0) /* This is a GET post */
+ {
+ args=getenv("QUERY_STRING");
+ if (!args) return;
+ if (strlen(args) > cgi_maxarg()) enomem();
+ cgi_args=malloc(strlen(args)+1); /* Extra insurance */
+ if (!cgi_args) return;
+ strcpy(cgi_args,args);
+ args=cgi_args;
+ }
+ else if (p && strcmp(p, "POST") == 0)
+ {
+ args=getenv("CONTENT_TYPE");
+ if (!args) return;
+
+#if CGIFORMDATA
+
+ if (strncasecmp(args,"multipart/form-data;", 20) == 0)
+ {
+ args=getenv("CONTENT_LENGTH");
+ if (!args) return;
+ cl=atol(args);
+ if (cl > cgi_maxformarg())
+ {
+ printf("Content-Type: text/html\n\n");
+ printf("<html><body><h1>Attachment size (%ld MB) exceeds limit set by system administrator (%ld MB)</h1></body></html>\n",
+ (long)(cl / (1024 * 1024)),
+ (long)(cgi_maxformarg() / (1024 * 1024)));
+ fake_exit(1);
+ }
+ cgi_formdata(cl);
+ return;
+ }
+#endif
+
+ if (strncmp(args, "application/x-www-form-urlencoded", 33))
+ return;
+ args=getenv("CONTENT_LENGTH");
+ if (!args) return;
+ cl=atol(args);
+ if (cl > cgi_maxarg())
+ {
+ printf("Content-Type: text/html\n\n");
+ printf("<html><body><h1>Message size (%ld MB) exceeds limit set by system administrator (%ld MB)</h1></body></html>\n",
+ (long)(cl / (1024 * 1024)),
+ (long)(cgi_maxarg() / (1024 * 1024)));
+ fake_exit(1);
+ }
+ cgi_args=malloc(cl+1); /* Extra insurance */
+ if (!cgi_args) return;
+ q=cgi_args;
+ while (cl)
+ {
+ c=getchar();
+ if (c < 0)
+ {
+ free(cgi_args);
+ cgi_args=0;
+ return;
+ }
+ *q++=c;
+ --cl;
+ }
+ *q=0;
+ args=cgi_args;
+ }
+ else return;
+
+ q=args;
+ while (*q)
+ {
+ argp=malloc(sizeof(*cgi_arglist));
+ if (!argp) enomem();
+ argp->next=cgi_arglist;
+ cgi_arglist=argp;
+ argp->argname=q;
+ argp->argvalue="";
+ p=q;
+ while (*q && *q != '&')
+ q++;
+ if (*q) *q++=0;
+ if ((r=strchr(p, '=')) != 0)
+ {
+ *r++='\0';
+ argp->argvalue=r;
+ cgiurldecode(r);
+ }
+ cgiurldecode(p);
+ }
+}
+
+static char *cgiurlencode_common(const char *buf, const char *punct)
+{
+char *newbuf=0;
+size_t cnt=0;
+int pass;
+const char *p;
+static const char hex[]="0123456789ABCDEF";
+
+ for (pass=0; pass<2; pass++)
+ {
+ if (pass && (newbuf=malloc(cnt+1)) == 0) enomem();
+ cnt=0;
+ for (p=buf; *p; p++)
+ {
+ if (strchr(punct, *p) || *p < 32 || *p >= 127)
+ {
+ if (pass)
+ {
+ newbuf[cnt]='%';
+ newbuf[cnt+1]=hex[
+ ((int)(unsigned char)*p) / 16];
+ newbuf[cnt+2]=hex[ *p & 15 ];
+ }
+ cnt += 3;
+ continue;
+ }
+ if (pass)
+ newbuf[cnt]= *p == ' ' ? '+':*p;
+ ++cnt;
+ }
+ }
+ newbuf[cnt]=0;
+ return (newbuf);
+}
+
+char *cgiurlencode(const char *buf)
+{
+ return (cgiurlencode_common(buf, "\"?;<>&=/:%@+#"));
+}
+
+char *cgiurlencode_noamp(const char *buf)
+{
+ return (cgiurlencode_common(buf, "\"?<>=/:%@+#"));
+}
+
+char *cgiurlencode_noeq(const char *buf)
+{
+ return (cgiurlencode_common(buf, "\"?;<>&/:%@+#"));
+}
+
+void cgi_cleanup()
+{
+#if CGIFORMDATA
+
+ if (hascgiformfd)
+ {
+ close(cgiformfd);
+ hascgiformfd=0;
+ }
+#endif
+
+}
+
+const char *cgi(const char *arg)
+{
+struct cgi_arglist *argp;
+
+ for (argp=cgi_arglist; argp; argp=argp->next)
+ if (strcmp(argp->argname, arg) == 0)
+ return (argp->argvalue);
+ return ("");
+}
+
+char *cgi_multiple(const char *arg, const char *sep)
+{
+struct cgi_arglist *argp;
+size_t l=1;
+char *buf;
+
+ for (argp=cgi_arglist; argp; argp=argp->next)
+ if (strcmp(argp->argname, arg) == 0)
+ l += strlen(argp->argvalue)+strlen(sep);
+
+ buf=malloc(l);
+ if (!buf) return(0);
+ *buf=0;
+
+ /*
+ ** Because the cgi list is build from the tail end up, we go backwards
+ ** now, so that we return options in the same order they were selected.
+ */
+
+ argp=cgi_arglist;
+ while (argp && argp->next)
+ argp=argp->next;
+
+ for (; argp; argp=argp->prev)
+ if (strcmp(argp->argname, arg) == 0)
+ {
+ if (*buf) strcat(buf, sep);
+ strcat(buf, argp->argvalue);
+ }
+ return (buf);
+}
+
+static char *nybble(char *p, int *n)
+{
+ if ( *p >= '0' && *p <= '9')
+ (*n) = (*n) * 16 + (*p++ - '0');
+ else if ( *p >= 'A' && *p <= 'F')
+ (*n) = (*n) * 16 + (*p++ - 'A' + 10);
+ else if ( *p >= 'a' && *p <= 'f')
+ (*n) = (*n) * 16 + (*p++ - 'a' + 10);
+ return (p);
+}
+
+void cgiurldecode(char *q)
+{
+char *p=q;
+int c;
+
+ while (*q)
+ {
+ if (*q == '+')
+ {
+ *p++=' ';
+ q++;
+ continue;
+ }
+ if (*q != '%')
+ {
+ *p++=*q++;
+ continue;
+ }
+ ++q;
+ c=0;
+ q=nybble(q, &c);
+ q=nybble(q, &c);
+
+ if (c && c != '\r')
+ /* Ignore CRs we get in TEXTAREAS */
+ *p++=c;
+ }
+ *p++=0;
+}
+
+void cgi_put(const char *cginame, const char *cgivalue)
+{
+struct cgi_arglist *argp;
+
+ for (argp=cgi_arglist; argp; argp=argp->next)
+ if (strcmp(argp->argname, cginame) == 0)
+ {
+ argp->argvalue=cgivalue;
+ return;
+ }
+
+ argp=malloc(sizeof(*cgi_arglist));
+ if (!argp) enomem();
+ argp->next=cgi_arglist;
+ argp->prev=0;
+ if (argp->next)
+ argp->next->prev=argp;
+ cgi_arglist=argp;
+ argp->argname=cginame;
+ argp->argvalue=cgivalue;
+}
+
+#if CGIFORMDATA
+
+/**************************************************************************/
+
+/* multipart/formdata decoding */
+
+static char *disposition_name=NULL, *disposition_filename=NULL;
+
+static char *formargbuf;
+static char *formargptr;
+
+static int save_formdata(const char *p, size_t l, void *miscptr)
+{
+ memcpy(formargptr, p, l);
+ formargptr += l;
+ return (0);
+}
+
+static void cgiformdecode(struct rfc2045 *p, struct rfc2045id *a, void *b)
+{
+off_t start_pos, end_pos, start_body;
+char buf[512];
+int n;
+off_t dummy;
+
+ a=a;
+ b=b;
+
+ if (disposition_name)
+ free(disposition_name);
+ if (disposition_filename)
+ free(disposition_filename);
+
+ if (rfc2231_udecodeDisposition(p, "name", NULL, &disposition_name) < 0)
+ disposition_name=NULL;
+
+ if (rfc2231_udecodeDisposition(p, "filename", NULL,
+ &disposition_filename) < 0)
+ disposition_filename=NULL;
+
+ if (!p->content_disposition
+ || strcmp(p->content_disposition, "form-data")) return;
+
+ if (!disposition_name || !*disposition_name) return;
+
+ if (!disposition_filename || !*disposition_filename)
+ {
+ rfc2045_mimepos(p, &start_pos, &end_pos, &start_body,
+ &dummy, &dummy);
+
+ if (lseek(cgiformfd, start_body, SEEK_SET) == -1)
+ enomem();
+
+ formargbuf=malloc(end_pos - start_body+1);
+ if (!formargbuf) enomem();
+ formargptr=formargbuf;
+
+ rfc2045_cdecode_start(p, &save_formdata, 0);
+ while (start_body < end_pos)
+ {
+ n=sizeof(buf);
+ if (n > end_pos - start_body)
+ n=end_pos-start_body;
+ n=read(cgiformfd, buf, n);
+ if (n <= 0) enomem();
+ rfc2045_cdecode(p, buf, n);
+ start_body += n;
+ }
+ rfc2045_cdecode_end(p);
+
+ *formargptr=0;
+ {
+ char *name=strdup(disposition_name);
+ char *value=strdup(formargbuf);
+ char *p, *q;
+
+ /* Just like for GET/POSTs, strip CRs. */
+
+ for (p=q=value; *p; p++)
+ {
+ if (*p == '\r') continue;
+ *q++ = *p;
+ }
+ *q++='\0';
+ cgi_put(name, value);
+ }
+ free(formargbuf);
+ }
+}
+
+static const char *cgitempdir="/tmp";
+
+void cgiformdatatempdir(const char *p)
+{
+ cgitempdir=p;
+}
+
+static void cgiformfdw(const char *p, size_t n)
+{
+ while (n)
+ {
+ int k=write(cgiformfd, p, n);
+
+ if (k <= 0) enomem();
+ p += k;
+ n -= k;
+ }
+}
+
+static void cgi_formdata(unsigned long contentlength)
+{
+char pidbuf[MAXLONGSIZE];
+char timebuf[MAXLONGSIZE];
+char cntbuf[MAXLONGSIZE];
+time_t t;
+unsigned long cnt;
+int n;
+char *filename, *p;
+
+static const char fakeheader[]="MIME-Version: 1.0\nContent-Type: ";
+char buf[BUFSIZ];
+
+ sprintf(pidbuf, "%lu", (unsigned long)getpid());
+ time(&t);
+ sprintf(timebuf, "%lu", (unsigned long)t);
+ cnt=0;
+
+ buf[sizeof(buf)-1]=0;
+ if (gethostname(buf, sizeof(buf)-1) != 0)
+ buf[0]='\0';
+
+ do
+ {
+ sprintf(cntbuf, "%lu", (unsigned long)cnt);
+ filename=malloc(strlen(pidbuf)+strlen(timebuf)+strlen(cntbuf)
+ +strlen(cgitempdir)+strlen(buf)+10);
+ if (!filename) enomem();
+ sprintf(filename, "%s/%s.%s_%s.%s", cgitempdir,
+ timebuf, pidbuf, cntbuf, buf);
+ cgiformfd=open(filename, O_RDWR | O_CREAT | O_EXCL, 0644);
+ } while (cgiformfd < 0);
+ unlink(filename); /* !!!MUST WORK!!! */
+ hascgiformfd=1;
+ p=getenv("CONTENT_TYPE");
+ free(filename);
+ cgiformfdw(fakeheader, strlen(fakeheader));
+ cgiformfdw(p, strlen(p));
+ cgiformfdw("\n\n", 2);
+
+ clearerr(stdin);
+
+ while (contentlength)
+ {
+ n=sizeof(buf);
+ if (n > contentlength) n=contentlength;
+
+ n=fread(buf, 1, n, stdin);
+ if (n <= 0)
+ enomem();
+ cgiformfdw(buf, n);
+ contentlength -= n;
+ }
+
+ rfc2045p=rfc2045_alloc();
+ lseek(cgiformfd, 0L, SEEK_SET);
+ while ((n=read(cgiformfd, buf, sizeof(buf))) > 0)
+ rfc2045_parse(rfc2045p, buf, n);
+ rfc2045_parse_partial(rfc2045p);
+ rfc2045_decode(rfc2045p, &cgiformdecode, 0);
+
+}
+
+struct cgigetfileinfo {
+ int (*start_file)(const char *, const char *, void *);
+ int (*file)(const char *, size_t, void *);
+ void (*end_file)(void *);
+ size_t filenum;
+ void *voidarg;
+ } ;
+
+
+static void cgifiledecode(struct rfc2045 *p, struct rfc2045id *a, void *b)
+{
+off_t start_pos, end_pos, start_body;
+char buf[512];
+int n;
+struct cgigetfileinfo *c;
+off_t dummy;
+
+ a=a;
+ c=(struct cgigetfileinfo *)b;
+
+ if (c->filenum == 0) return; /* Already retrieved this one. */
+
+ if (disposition_name)
+ free(disposition_name);
+ if (disposition_filename)
+ free(disposition_filename);
+
+ if (rfc2231_udecodeDisposition(p, "name", NULL, &disposition_name) < 0
+ ||
+ rfc2231_udecodeDisposition(p, "filename", NULL,
+ &disposition_filename) < 0)
+ {
+ disposition_name=disposition_filename=NULL;
+ enomem();
+ }
+
+ if (!p->content_disposition
+ || strcmp(p->content_disposition, "form-data")) return;
+
+ if (!*disposition_name) return;
+
+ if (!*disposition_filename) return;
+
+ rfc2045_mimepos(p, &start_pos, &end_pos, &start_body,
+ &dummy, &dummy);
+
+ if (start_body == end_pos) /* NULL FILE */
+ return;
+
+ if ( --c->filenum ) return; /* Not this one */
+
+ if ( (*c->start_file)(disposition_name, disposition_filename,
+ c->voidarg) )
+ return;
+
+ if (lseek(cgiformfd, start_body, SEEK_SET) == -1)
+ enomem();
+
+ rfc2045_cdecode_start(p, c->file, c->voidarg);
+ while (start_body < end_pos)
+ {
+ n=sizeof(buf);
+ if (n > end_pos - start_body)
+ n=end_pos-start_body;
+ n=read(cgiformfd, buf, n);
+ if (n <= 0) enomem();
+ rfc2045_cdecode(p, buf, n);
+ start_body += n;
+ }
+ rfc2045_cdecode_end(p);
+ (*c->end_file)(c->voidarg);
+}
+
+int cgi_getfiles( int (*start_file)(const char *, const char *, void *),
+ int (*file)(const char *, size_t, void *),
+ void (*end_file)(void *), size_t filenum, void *voidarg)
+{
+ struct cgigetfileinfo gfi;
+
+ gfi.start_file=start_file;
+ gfi.file=file;
+ gfi.end_file=end_file;
+ gfi.filenum=filenum;
+ gfi.voidarg=voidarg;
+
+ if (rfc2045p) rfc2045_decode(rfc2045p, &cgifiledecode, &gfi);
+ if (gfi.filenum) return (-1);
+ return (0);
+}
+
+#endif
+
+/* cookies */
+
+int cgi_set_cookie_url(struct cgi_set_cookie_info *cookie_info,
+ const char *url)
+{
+ const char *p;
+
+ if (cookie_info->domain)
+ free(cookie_info->domain);
+ if (cookie_info->path)
+ free(cookie_info->path);
+
+ cookie_info->secure=0;
+
+ if (strncmp(url, "https://", 8) == 0)
+ cookie_info->secure=1;
+
+ for (p=url; *p; p++)
+ {
+ if (*p == ':')
+ {
+ url= ++p;
+ break;
+ }
+
+ if (*p == '/')
+ break;
+ }
+
+ if (strncmp(url, "//", 2) == 0)
+ {
+ p= url += 2;
+
+ while (*url)
+ {
+ if (*url == '/')
+ break;
+ ++url;
+ }
+
+ if ((cookie_info->domain=malloc(url-p+1)) == NULL)
+ return -1;
+
+ memcpy(cookie_info->domain, p, url-p);
+ cookie_info->domain[url-p]=0;
+ }
+
+ if ((cookie_info->path=strdup(url)) == NULL)
+ return -1;
+ return 0;
+}
+
+void cgi_set_cookies(struct cgi_set_cookie_info *cookies,
+ size_t n_cookies)
+{
+ size_t i;
+ const char *sep="";
+
+ printf("Set-Cookie: ");
+
+ for (i=0; i<n_cookies; i++, cookies++)
+ {
+ printf("%s%s=\"%s\"; ", sep, cookies->name, cookies->value);
+ sep="; ";
+
+ if (cookies->path)
+ printf("Path=\"%s\"; ", cookies->path);
+
+ if (cookies->secure)
+ printf("Secure; ");
+
+ if (cookies->age >= 0)
+ printf("Max-Age=%d; ", cookies->age);
+ printf("Version=1");
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
+/*
+** Parse Cookie: header
+**
+** get_cookie_value() skips over a single cookie name=value, returning # of
+** bytes, excluding quotes (if any), plus one more (for the trailing \0).
+**
+** out_ptr, if not NULL, receives ptr to the next byte after name=value
+**
+** out_value, if not NULL receives the cookie's value, excluding any quotes.
+*/
+
+static size_t get_cookie_value(const char *ptr, const char **out_ptr,
+ char *out_value)
+{
+ int in_quote=0;
+ size_t cnt=1;
+
+ while (*ptr)
+ {
+ if (!in_quote)
+ {
+ if (*ptr == ';' || *ptr == ',' ||
+ isspace((int)(unsigned char)*ptr))
+ break;
+ }
+
+ if (*ptr == '"')
+ {
+ in_quote= ~in_quote;
+ ++ptr;
+ continue;
+ }
+
+ if (out_value)
+ *out_value++ = *ptr;
+ ++cnt;
+ ++ptr;
+ }
+
+ if (out_value)
+ *out_value=0;
+
+ if (out_ptr)
+ *out_ptr=ptr;
+ return cnt;
+}
+
+/*
+** Search for a cookie.
+**
+** Returns NULL and sets errno=ENOENT, if cookie not found.
+**
+** Returns malloc-ed buffer that holds the cookie's value (or NULL if
+** malloc fails).
+*/
+
+char *cgi_get_cookie(const char *cookie_name)
+{
+ size_t cookie_name_len=strlen(cookie_name);
+ const char *cookie=getenv("HTTP_COOKIE");
+
+ if (!cookie)
+ cookie="";
+
+ while (*cookie)
+ {
+ if (isspace((int)(unsigned char)*cookie) ||
+ *cookie == ';' || *cookie == ',')
+ {
+ ++cookie;
+ continue;
+ }
+
+ if (strncmp(cookie, cookie_name, cookie_name_len) == 0 &&
+ cookie[cookie_name_len] == '=')
+ {
+ char *buf;
+
+ cookie += cookie_name_len;
+ ++cookie;
+
+ if ((buf=malloc(get_cookie_value(cookie, NULL, NULL)))
+ == NULL)
+ {
+ return NULL;
+ }
+
+ get_cookie_value(cookie, NULL, buf);
+
+ if (*buf == 0) /* Pretend not found */
+ {
+ free(buf);
+ errno=ENOENT;
+ return NULL;
+ }
+
+ return buf;
+ }
+
+ get_cookie_value(cookie, &cookie, NULL);
+ }
+
+ errno=ENOENT;
+ return NULL;
+}
diff --git a/cgi/cgi.h b/cgi/cgi.h
new file mode 100644
index 0000000..1001368
--- /dev/null
+++ b/cgi/cgi.h
@@ -0,0 +1,170 @@
+/*
+*/
+#ifndef cgi_h
+#define cgi_h
+
+#if HAVE_CONFIG_H
+#include "cgi/cgi_config.h"
+#endif
+#include "../unicode/unicode.h"
+
+#ifdef __cplusplus
+
+extern "C" {
+
+#endif
+
+#include <string.h>
+
+/*
+** Copyright 1998 - 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+extern void fake_exit(int);
+
+void cgi_setup();
+void cgi_cleanup();
+const char *cgi(const char *);
+char *cgi_multiple(const char *, const char *);
+
+char *cgi_cookie(const char *);
+void cgi_setcookie(const char *, const char *);
+
+int cgi_useragent(const char *);
+
+struct cgi_arglist {
+ struct cgi_arglist *next;
+ struct cgi_arglist *prev; /* Used by cgi_multiple */
+ const char *argname;
+ const char *argvalue;
+ } ;
+
+extern struct cgi_arglist *cgi_arglist;
+
+extern void cgiurldecode(char *);
+extern void cgi_put(const char *, const char *);
+
+extern char *cgiurlencode(const char *);
+extern char *cgiurlencode_noamp(const char *);
+extern char *cgiurlencode_noeq(const char *);
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+int cgi_getfiles( int (*)(const char *, const char *, void *),
+ int (*)(const char *, size_t, void *),
+ void (*)(void *), size_t, void *);
+
+extern const char *cgihttpscriptptr();
+extern const char *cgihttpsscriptptr();
+extern const char *cgiextrapath();
+
+extern void cgihttpscriptptr_init();
+extern void cgihttpsscriptptr_init();
+
+extern const char *cgirelscriptptr();
+extern void cginocache();
+extern void cgiredirect(const char *);
+extern void cgiversion(unsigned *, unsigned *);
+extern int cgihasversion(unsigned, unsigned);
+
+struct cgi_set_cookie_info {
+ const char *name;
+ const char *value;
+
+ char *domain;
+ char *path;
+ int age;
+ int secure;
+};
+
+#define cgi_set_cookie_info_init(i) (memset((i), 0, sizeof(*(i))), (i)->age=-1)
+#define cgi_set_cookie_info_free(i) do { if ((i)->path) \
+ free((i)->path); \
+ if ((i)->domain) \
+ free((i)->domain); \
+ } while(0)
+
+#define cgi_set_cookie_session(c,n,v) ( ((c)->name=(n)), ((c)->value)=(v))
+#define cgi_set_cookie_expired(c,n) ( ((c)->name=(n)), ((c)->value)="",\
+ (c)->age=0)
+
+extern int cgi_set_cookie_url(struct cgi_set_cookie_info *i,
+ const char *url);
+
+#define cgi_set_cookie_secure(c) ((c)->secure=1)
+
+extern void cgi_set_cookies(struct cgi_set_cookie_info *cookies,
+ size_t n_cookies);
+
+extern char *cgi_get_cookie(const char *cookie_name);
+
+extern char *cgi_select(const char *name,
+ const char *optvalues,
+ const char *optlabels,
+ const char *default_value,
+ size_t list_size,
+ const char *flags); /* "m" - multiple, "d" -disabled */
+extern char *cgi_checkbox(const char *name,
+ const char *value,
+ const char *flags);
+extern char *cgi_input(const char *name,
+ const unicode_char *value,
+ int size,
+ int maxlength,
+ const char *opts);
+
+extern char *cgi_textarea(const char *name,
+ int rows,
+ int cols,
+ const unicode_char *value,
+ const char *wrap,
+ const char *opts);
+
+extern void cgiformdatatempdir(const char *);
+ /* Specify directory for formdata temp file */
+
+extern void cgi_daemon(int nprocs, const char *lockfile,
+ void (*postinit)(void *),
+ void (*handler)(void *),
+ void *dummy);
+extern void cgi_connectdaemon(const char *sockfilename, int pass_fd);
+
+#define SOCKENVIRONLEN 8192
+
+#define VALIDCGIVAR(p) \
+ (strncmp((p), "DOCUMENT_", 9) == 0 || \
+ strncmp((p), "GATEWAY_", 8) == 0 || \
+ strncmp((p), "HTTP_", 5) == 0 || \
+ strncmp((p), "HTTPS=", 6) == 0 || \
+ strncmp((p), "SSL_", 4) == 0 || \
+ strncmp((p), "QUERY_STRING=", 13) == 0 || \
+ strncmp((p), "SQWEBMAIL_", 10) == 0 || \
+ strncmp((p), "REMOTE_", 7) == 0 || \
+ strncmp((p), "REQUEST_", 8) == 0 || \
+ strncmp((p), "SCRIPT_", 7) == 0 || \
+ strncmp((p), "SERVER_", 7) == 0 || \
+ strncmp((p), "CONTENT_", 8) == 0 || \
+ strncmp((p), "PATH_INFO=", 10) == 0)
+
+#define CGI_PASSFD 0
+
+#if CGI_PASSFD_MSGACCRIGHTS
+#undef CGI_PASSFD
+#define CGI_PASSFD 1
+#endif
+
+#if CGI_PASSFD_MSGCONTROL
+#undef CGI_PASSFD
+#define CGI_PASSFD 1
+#endif
+
+#ifdef __cplusplus
+
+}
+
+#endif
+
+#endif
diff --git a/cgi/cgicheckbox.c b/cgi/cgicheckbox.c
new file mode 100644
index 0000000..87f35af
--- /dev/null
+++ b/cgi/cgicheckbox.c
@@ -0,0 +1,41 @@
+/*
+** Copyright 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+
+#include "cgi.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+char *cgi_checkbox(const char *name,
+ const char *value,
+ const char *flags)
+{
+ char *buf;
+
+ if (!value)
+ value="";
+
+ buf=malloc(strlen(name)+strlen(flags)+200);
+
+ if (!buf)
+ return NULL;
+
+ strcpy(buf, "<input type='checkbox' name='");
+ strcat(buf, name);
+ strcat(buf, "' value='");
+ strcat(buf, value);
+ strcat(buf, "'");
+
+ if (strchr(flags, '*'))
+ strcat(buf, " checked='checked'");
+ if (strchr(flags, 'd'))
+ strcat(buf, " disabled='disabled'");
+ strcat(buf, " />");
+ return buf;
+}
diff --git a/cgi/cgicookie.c b/cgi/cgicookie.c
new file mode 100644
index 0000000..704b7fc
--- /dev/null
+++ b/cgi/cgicookie.c
@@ -0,0 +1,72 @@
+/*
+** Copyright 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+
+#include "cgi.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+extern void error(const char *);
+
+static void enomem()
+{
+ error("Out of memory.");
+}
+
+char *cgi_cookie(const char *p)
+{
+size_t pl=strlen(p);
+const char *c=getenv("HTTP_COOKIE");
+char *buf;
+
+ while (c && *c)
+ {
+ size_t i;
+
+ for (i=0; c[i] && c[i] != '='; i++)
+ ;
+ if (i == pl && strncmp(p, c, i) == 0)
+ {
+ c += i;
+ if (*c) ++c; /* skip over = */
+ for (i=0; c[i] && c[i] != ';'; i++)
+ ;
+
+ buf=malloc(i+1);
+ if (!buf) enomem();
+ memcpy(buf, c, i);
+ buf[i]=0;
+ cgiurldecode(buf);
+ return (buf);
+ }
+ c=strchr(c, ';');
+ if (c)
+ do
+ {
+ ++c;
+ } while (isspace((int)(unsigned char)*c));
+ }
+ buf=malloc(1);
+ if (!buf) enomem();
+ *buf=0;
+ return (buf);
+}
+
+void cgi_setcookie(const char *name, const char *value)
+{
+char *p;
+const char *sn;
+
+ p=cgiurlencode(value);
+ sn=getenv("SCRIPT_NAME");
+ if (!sn || !*sn)
+ sn="/";
+ printf("Set-Cookie: %s=%s; path=%s\n", name, value, sn);
+ free(p);
+}
diff --git a/cgi/cgidaemon.c b/cgi/cgidaemon.c
new file mode 100644
index 0000000..fa42cbf
--- /dev/null
+++ b/cgi/cgidaemon.c
@@ -0,0 +1,376 @@
+/*
+** Copyright 2003-2007 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+/*
+*/
+#include "cgi.h"
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <ctype.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+
+extern char **environ;
+
+static void err(const char *func, const char *msg)
+{
+ cginocache();
+
+ printf("Content-Type: text/html; charset='utf-8'\n\n"
+ "<html><head><title>Internal error</title></head>\n"
+ "<body><h1>Internal Error</h1>\n"
+ "<p>The webmail system is temporarily unavailable. An error"
+ " occured in function %s: %s</p></body></html>\n", func,
+ msg);
+ fflush(stdout);
+ exit(0);
+}
+
+static void connect_err(const char *func)
+{
+ cginocache();
+
+ printf("Content-Type: text/html; charset='us-ascii'\n\n"
+ "<html><head><title>System unavailable</title></head>\n"
+ "<body><h1>System unavailable</h1>\n"
+ "<p>The web page you're trying to access is not available"
+ " at this time. Please try again later.\n"
+ "</p><p>"
+ "(%s: %s)</p></body></html>\n", func, strerror(errno));
+ fflush(stdout);
+ exit(0);
+}
+
+static void sys_err(const char *func)
+{
+ err(func, strerror(errno));
+}
+
+static const char *force_write(int s, const char *p, size_t l)
+{
+ while (l)
+ {
+ int n;
+
+ n=write(s, p, l);
+ if (n <= 0)
+ return ("write");
+ p += n;
+ l -= n;
+ }
+ return NULL;
+}
+
+/* Pass along the CGI environment variables to sqwebmaild */
+
+static void send_environ(int fd, int passfd)
+{
+ char buf[SOCKENVIRONLEN];
+
+ char *p=buf+sizeof(size_t)+1;
+ size_t l=sizeof(buf)-sizeof(size_t)-2;
+ size_t i;
+ const char *cp;
+
+ buf[sizeof(l)]=passfd;
+
+ for (i=0; environ[i]; i++)
+ {
+ size_t m;
+
+ if (!VALIDCGIVAR(environ[i]))
+ continue;
+
+ m=strlen(environ[i])+1;
+
+ if (m > l)
+ err("CGI", "CGI environment exceeds allowed "
+ "maximum size.");
+
+ memcpy(p, environ[i], m);
+ p += m;
+ l -= m;
+ }
+
+ l=p-(buf+sizeof(l)+1);
+ memcpy(buf, &l, sizeof(l));
+
+ cp=force_write(fd, buf, l + sizeof(l)+1);
+ if (cp)
+ sys_err(cp);
+
+ /*
+ ** If the platform supports it, pass the file descriptors
+ ** to sqwebmaild.
+ */
+
+#if CGI_PASSFD
+
+ if (passfd)
+ {
+ struct iovec iov;
+ char dummy;
+
+#if CGI_PASSFD_MSGACCRIGHTS
+
+ int fdbuf[2];
+ struct msghdr msg;
+
+ fdbuf[0]=0;
+ fdbuf[1]=1;
+ memset(&iov, 0, sizeof(iov));
+ msg.msg_accrights=(caddr_t)fdbuf;
+ msg.msg_accrightslen=sizeof(fdbuf);
+#endif
+
+#if CGI_PASSFD_MSGCONTROL
+
+ int fdbuf[2];
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char buf[CMSG_SPACE(sizeof(fdbuf))];
+
+ fdbuf[0]=0;
+ fdbuf[1]=1;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control=buf;
+ msg.msg_controllen=sizeof(buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level=SOL_SOCKET;
+ cmsg->cmsg_type=SCM_RIGHTS;
+ cmsg->cmsg_len=CMSG_LEN(sizeof(fdbuf));
+ memcpy(CMSG_DATA(cmsg), fdbuf, sizeof(fdbuf));
+#endif
+ msg.msg_iov=&iov;
+ msg.msg_iovlen=1;
+ iov.iov_base=&dummy;
+ iov.iov_len=1;
+
+ dummy=0;
+ if (sendmsg(fd, &msg, 0) < 0)
+ sys_err("sendmsg(filedescriptor)");
+ }
+#endif
+
+}
+
+static void passthrough(int s, int passed_fd)
+{
+ char toclientbuf[8192];
+ char tosqbuf[8192];
+
+ char *toclientptr, *tosqptr;
+ size_t toclientlen, tosqlen;
+ int stdin_closed=0;
+
+ toclientptr=NULL;
+ tosqptr=NULL;
+ toclientlen=0;
+ tosqlen=0;
+
+ if (passed_fd)
+ {
+ stdin_closed=1; /* sqwebmaild will read on the fd itself */
+ if (fcntl(s, F_SETFL, O_NDELAY) < 0)
+ sys_err("fcntl");
+ }
+
+ /* When the file descriptor is passed, we will not do any actual I/O,
+ ** so there's no need to set stdin/stdout to nonblock mode
+ */
+ else if (fcntl(0, F_SETFL, O_NDELAY) < 0 ||
+ fcntl(1, F_SETFL, O_NDELAY) < 0 ||
+ fcntl(s, F_SETFL, O_NDELAY) < 0)
+ sys_err("fcntl");
+
+ for (;;)
+ {
+ fd_set fdr, fdw;
+
+ FD_ZERO(&fdr);
+ FD_ZERO(&fdw);
+
+ if (tosqlen)
+ FD_SET(s, &fdw);
+ else if (!stdin_closed)
+ FD_SET(0, &fdr);
+
+ if (toclientlen)
+ FD_SET(1, &fdw);
+ else
+ FD_SET(s, &fdr);
+
+ if (select(s+1, &fdr, &fdw, 0, NULL) <= 0)
+ {
+ fcntl(1, F_SETFL, 0);
+ sys_err("select");
+ }
+
+ if (tosqlen)
+ {
+ if (FD_ISSET(s, &fdw))
+ {
+ int m=write(s, tosqptr, tosqlen);
+
+ if (m <= 0)
+ {
+ fcntl(1, F_SETFL, 0);
+ sys_err("write");
+ }
+
+ tosqptr += m;
+ tosqlen -= m;
+ }
+ }
+ else
+ {
+ if (FD_ISSET(0, &fdr))
+ {
+ int m=read(0, tosqbuf, sizeof(tosqbuf));
+
+ if (m < 0) /* network error */
+ return;
+
+ if (m == 0)
+ stdin_closed=1;
+
+ tosqptr=tosqbuf;
+ tosqlen=m;
+ }
+ }
+
+ if (toclientlen)
+ {
+ if (FD_ISSET(1, &fdw))
+ {
+ int m=write(1, toclientptr, toclientlen);
+
+ if (m <= 0)
+ return; /* Client aborted, nocare */
+
+ toclientptr += m;
+ toclientlen -= m;
+ }
+ }
+ else
+ {
+ if (FD_ISSET(s, &fdr))
+ {
+ int m=read(s, toclientbuf,
+ sizeof(toclientbuf));
+
+ if (m <= 0)
+ return;
+
+ toclientptr=toclientbuf;
+ toclientlen=m;
+ }
+ }
+ }
+}
+
+void cgi_connectdaemon(const char *sockfilename, int pass_fd)
+{
+ int s;
+ struct sockaddr_un ssun;
+ int triedagain=0;
+ int rc;
+
+ /* Connect to sqwebmaild via a socket */
+
+ signal(SIGPIPE, SIG_IGN);
+ if ((s=socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+ sys_err("socket");
+
+ if (fcntl(s, F_SETFL, O_NDELAY) < 0)
+ sys_err("fcntl");
+
+ ssun.sun_family=AF_UNIX;
+ strcpy(ssun.sun_path, sockfilename);
+
+ while ((rc=connect(s, (struct sockaddr *)&ssun, sizeof(ssun))) < 0
+ && errno == EAGAIN)
+ {
+ if (++triedagain > 5)
+ break;
+ sleep(1);
+ ssun.sun_family=AF_UNIX;
+ strcpy(ssun.sun_path, sockfilename);
+ }
+
+ if (rc < 0)
+ {
+ struct timeval tv;
+ fd_set fds;
+
+ int errcode;
+ socklen_t errcode_l;
+
+ if (errno != EINPROGRESS)
+ connect_err("connect");
+
+ tv.tv_sec=30;
+ tv.tv_usec=0;
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ if (select(s+1, 0, &fds, 0, &tv) <= 0)
+ connect_err("select");
+
+
+ errcode_l=sizeof(errcode);
+
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &errcode, &errcode_l)
+ < 0)
+ connect_err("setsockopt");
+
+
+ if (errcode)
+ {
+ errno=errcode;
+ connect_err("connect");
+ }
+ }
+
+ if (triedagain)
+ {
+ fprintf(stderr,
+ "CRIT: Several attempts were necessary to connect to sqwebmaild\n");
+ fprintf(stderr,
+ "CRIT: Consider increasing the number of pre-forked sqwebmaild processes\n");
+ }
+
+ if (fcntl(s, F_SETFL, 0) < 0)
+ sys_err("fcntl");
+
+ send_environ(s, pass_fd);
+ passthrough(s, pass_fd);
+ close(s);
+ exit(0);
+}
diff --git a/cgi/cgidaemond.c b/cgi/cgidaemond.c
new file mode 100644
index 0000000..fd928fb
--- /dev/null
+++ b/cgi/cgidaemond.c
@@ -0,0 +1,484 @@
+/*
+** Copyright 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+static int read_environ(int);
+
+static int start_daemon(const char *lockfilename);
+
+static void run_daemon(int fd, int termfd, int connfd, void (*handler)(void *),
+ void *dummy);
+
+static void run_prefork(int fd, size_t ndaemons, void (*handler)(void *),
+ void *dummy);
+
+void cgi_daemon(int nprocs, const char *lockfile,
+ void (*postinit)(void *), void (*handler)(void *),
+ void *dummy)
+{
+ int fd=start_daemon(lockfile);
+
+ if (postinit)
+ (*postinit)(dummy);
+
+ if (nprocs > 0)
+ run_prefork(fd, nprocs, handler, dummy);
+ else
+ run_daemon(fd, -1, -1, handler, dummy);
+}
+
+/* Start in daemon mode. Return listening socket file descriptor */
+
+static int start_daemon(const char *lockfile)
+{
+ int fd;
+ struct sockaddr_un skun;
+
+ unlink(lockfile);
+
+ fd=socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+
+ skun.sun_family=AF_UNIX;
+ strcpy(skun.sun_path, lockfile);
+ if (bind(fd, (const struct sockaddr *)&skun, sizeof(skun)) ||
+ listen(fd, SOMAXCONN) ||
+ chmod(skun.sun_path, 0777) ||
+ fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ perror(lockfile);
+ close(fd);
+ return (-1);
+ }
+ return fd;
+}
+
+static int prefork(int listenfd, int *allpipes, size_t ndaemos,
+ int *termpipe, void (*handler)(void *), void *dummy);
+
+static void run_prefork(int fd, size_t ndaemons, void (*handler)(void *),
+ void *dummy)
+{
+ int *cpipes; /* Completion pipes from preforked processes */
+ int termpipe[2]; /* Termination pipe to preforked processes */
+ size_t i;
+
+ if ((cpipes=malloc(sizeof(int)*ndaemons)) == NULL)
+ {
+ fprintf(stderr,
+ "CRIT: malloc failed: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ if (pipe(termpipe) < 0)
+ {
+ fprintf(stderr,
+ "CRIT: pipe failed: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+
+ /* Start the initial set of preforked daemons */
+
+ for (i=0; i<ndaemons; i++)
+ cpipes[i]= -1;
+
+ for (i=0; i<ndaemons; i++)
+ cpipes[i]=prefork(fd, cpipes, ndaemons, termpipe, handler,
+ dummy);
+
+
+ for (;;)
+ {
+ fd_set fdr;
+ int maxfd=0;
+
+ FD_ZERO(&fdr);
+
+ for (i=0; i<ndaemons; i++)
+ {
+ if (cpipes[i] >= maxfd)
+ maxfd=cpipes[i]+1;
+
+ FD_SET(cpipes[i], &fdr);
+ }
+
+ if (select(maxfd, &fdr, NULL, NULL, NULL) <= 0)
+ continue;
+
+ /*
+ ** When child process gets a connection, it closes its
+ ** completion pipe, which makes the pipe selectable for
+ ** read.
+ */
+
+ for (i=0; i<ndaemons; i++)
+ {
+ if (FD_ISSET(cpipes[i], &fdr))
+ {
+ close(cpipes[i]);
+ cpipes[i]= -1;
+ cpipes[i]=prefork(fd, cpipes,
+ ndaemons, termpipe, handler,
+ dummy);
+ }
+ }
+ }
+}
+
+/* Start a preforked process */
+
+static int prefork(int listenfd, int *allpipes, size_t ndaemons,
+ int *termpipe, void (*handler)(void *), void *dummy)
+{
+ int newpipe[2];
+ pid_t p;
+ int waitstat;
+ size_t i;
+
+ if (pipe(newpipe) < 0)
+ {
+ fprintf(stderr,
+ "CRIT: pipe failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ while ((p=fork()) < 0)
+ {
+ fprintf(stderr,
+ "CRIT: fork failed: %s\n", strerror(errno));
+ sleep(5);
+ }
+
+ if (p) /* parent */
+ {
+ close(newpipe[1]);
+
+ /* Wait for first child process to go away */
+
+ while (wait(&waitstat) != p)
+ ;
+
+ return (newpipe[0]);
+ }
+
+ close(newpipe[0]);
+ close(termpipe[1]);
+
+ /* New child doesn't need pipes from other children */
+
+ for (i=0; i<ndaemons; i++)
+ if (allpipes[i] >= 0)
+ close(allpipes[i]);
+
+ /* Fork once more, so that the parent process can continue */
+
+ if (fork())
+ exit(0);
+
+ run_daemon(listenfd, termpipe[0], newpipe[1], handler, dummy);
+ return (-1);
+}
+
+static void run_daemon(int fd, int termfd, int acceptedfd,
+ void (*handler)(void *), void *dummy)
+{
+ int cfd;
+
+ for (;;)
+ {
+ fd_set fdr;
+ pid_t p;
+ int maxfd;
+
+ FD_ZERO(&fdr);
+
+ FD_SET(fd, &fdr);
+
+ maxfd=fd;
+
+ if (termfd >= 0)
+ {
+ if (termfd > maxfd)
+ maxfd=termfd;
+
+ FD_SET(termfd, &fdr);
+ }
+
+ if (select(maxfd+1, &fdr, NULL, NULL, NULL) <= 0)
+ continue;
+ if (termfd >= 0 &&
+ FD_ISSET(termfd, &fdr)) /* Terminate all child procs */
+ exit(0);
+
+ if (!FD_ISSET(fd, &fdr))
+ continue;
+
+ cfd=accept(fd, NULL, 0);
+
+ if (cfd < 0)
+ continue;
+
+ if (acceptedfd >= 0) /* preforked daemon */
+ {
+ if (termfd >= 0)
+ close(termfd);
+ close(acceptedfd);
+ break;
+ }
+
+ p=fork();
+
+ if (p < 0)
+ {
+ fprintf(stderr,
+ "CRIT: fork() failed: %s\n", strerror(errno));
+ continue;
+ }
+
+ if (p)
+ {
+ int dummy;
+
+ close(cfd);
+ while (wait(&dummy) != p)
+ continue;
+ continue;
+ }
+
+ /* Child forks once more, parent exits */
+
+ if (fork())
+ exit(0);
+
+ break;
+
+ }
+
+ /* child */
+
+ if (fcntl(cfd, F_SETFL, 0) < 0)
+ {
+ fprintf(stderr,
+ "CRIT: fcntl(): %s\n", strerror(errno));
+ exit(0);
+ }
+
+ close(fd);
+ if (read_environ(cfd))
+ {
+ close(0);
+ close(1);
+ if (dup(cfd) != 0 || dup(cfd) != 1)
+ {
+ fprintf(stderr,
+ "CRIT: dup() did not work as expected\n");
+ exit(0);
+ }
+ close(cfd);
+ }
+
+ if (fcntl(0, F_SETFL, 0) < 0 ||
+ fcntl(1, F_SETFL, 0) < 0)
+ {
+ fprintf(stderr,
+ "CRIT: fcntl() failed: %s\n", strerror(errno));
+ exit(0);
+ }
+
+ (*handler)(dummy);
+ exit(0);
+}
+
+
+/* Read environment from the sqwebmail stub */
+
+static void force_read(int cfd, char *p, size_t l)
+{
+ int m;
+
+ while (l)
+ {
+ m=read(cfd, p, l);
+ if (m <= 0)
+ {
+ fprintf(stderr,
+ "WARN: socket closed while reading"
+ " environment.\n");
+ exit(0);
+ }
+
+ p += m;
+ l -= m;
+ }
+}
+
+/* Receive CGI environment */
+
+static int read_environ(int cfd)
+{
+ static char buf[SOCKENVIRONLEN];
+ size_t l;
+ char *p;
+ int passfd;
+
+ force_read(cfd, buf, 1+sizeof(l));
+
+ memcpy(&l, buf, sizeof(l));
+ passfd=buf[sizeof(l)];
+
+ if (l >= sizeof(buf))
+ {
+ fprintf(stderr,
+ "WARN: invalid environment received via socket.\n");
+ exit(0);
+ }
+
+ alarm(10); /* Just in case - punt */
+ force_read(cfd, buf, l);
+ buf[l]=0;
+ alarm(0);
+
+ /* Vet environment strings for only known good strings */
+
+ p=buf;
+ while (p < buf+l)
+ {
+ if (strchr(p, '=') == NULL ||
+ !VALIDCGIVAR(p))
+ {
+ fprintf(stderr,
+ "WARN: invalid environment received"
+ " via socket: %s\n" , p);
+ exit(0);
+ }
+
+ putenv(p);
+
+ while (*p++)
+ ;
+ }
+
+ /* Receive file descriptors, if supported by the platform */
+
+#if CGI_PASSFD
+
+ if (passfd)
+ {
+ struct iovec iov;
+ char dummy;
+
+#if CGI_PASSFD_MSGACCRIGHTS
+
+ int fdbuf[2];
+ struct msghdr msg;
+#endif
+
+#if CGI_PASSFD_MSGCONTROL
+
+ int fdbuf[2];
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char buf[CMSG_SPACE(sizeof(fdbuf))];
+#endif
+
+#if CGI_PASSFD_MSGACCRIGHTS
+ memset(&iov, 0, sizeof(iov));
+ msg.msg_accrights=(caddr_t)fdbuf;
+ msg.msg_accrightslen=sizeof(fdbuf);
+#endif
+
+
+#if CGI_PASSFD_MSGCONTROL
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control=buf;
+ msg.msg_controllen=sizeof(buf);
+#endif
+
+ msg.msg_iov=&iov;
+ msg.msg_iovlen=1;
+
+ iov.iov_base=&dummy;
+ iov.iov_len=1;
+
+ if (recvmsg(cfd, &msg, 0) <= 0)
+ {
+ perror("Internal error - recvmsg() failed");
+ exit(0);
+ }
+
+#if CGI_PASSFD_MSGACCRIGHTS
+
+ if (msg.msg_accrightslen < sizeof(fdbuf))
+ {
+ perror("Internal error - malformed recvmsg()");
+ exit(0);
+ }
+
+#endif
+
+#if CGI_PASSFD_MSGCONTROL
+ if (msg.msg_controllen < sizeof(buf) ||
+ (cmsg = CMSG_FIRSTHDR(&msg))->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS ||
+ cmsg->cmsg_len != CMSG_LEN(sizeof(fdbuf)))
+ {
+ perror("Internal error - malformed recvmsg()");
+ exit(0);
+ }
+
+ memcpy(fdbuf, CMSG_DATA(cmsg), sizeof(fdbuf));
+#endif
+ close(0);
+ close(1);
+ if (dup(fdbuf[0]) != 0 || dup(fdbuf[1]) != 1)
+ fprintf(stderr,
+ "CRIT: dup() did not work as expected in"
+ " read_environ()\n");
+ close(fdbuf[0]);
+ close(fdbuf[1]);
+ return 0;
+ }
+#endif
+
+ return 1;
+}
diff --git a/cgi/cgiextrapath.c b/cgi/cgiextrapath.c
new file mode 100644
index 0000000..3cd3aa6
--- /dev/null
+++ b/cgi/cgiextrapath.c
@@ -0,0 +1,21 @@
+/*
+** Copyright 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+#include <stdlib.h>
+#include <unistd.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+const char *cgiextrapath()
+{
+ const char *pi=getenv("PATH_INFO");
+
+ if (!pi) pi="";
+ return pi;
+}
diff --git a/cgi/cgihasversion.c b/cgi/cgihasversion.c
new file mode 100644
index 0000000..a323677
--- /dev/null
+++ b/cgi/cgihasversion.c
@@ -0,0 +1,16 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+
+int cgihasversion(unsigned major, unsigned minor)
+{
+unsigned vmajor, vminor;
+
+ cgiversion(&vmajor, &vminor);
+ return (vmajor > major || (vmajor == major && vminor >= minor));
+}
diff --git a/cgi/cgihttpscriptptr.c b/cgi/cgihttpscriptptr.c
new file mode 100644
index 0000000..9f5ee25
--- /dev/null
+++ b/cgi/cgihttpscriptptr.c
@@ -0,0 +1,48 @@
+/*
+** Copyright 1998 - 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static char *scriptptr=0;
+
+extern void error(const char *);
+
+void cgihttpscriptptr_init()
+{
+ /* Reinitialisation required when running as fastcgi */
+ if (scriptptr) {
+ free(scriptptr);
+ scriptptr=0;
+ }
+}
+
+const char *cgihttpscriptptr()
+{
+ if (!scriptptr)
+ {
+ char *p=getenv("SCRIPT_NAME");
+ char *h=getenv("HTTP_HOST");
+ char *q;
+
+ if (!h) h="";
+ if (!p) p="";
+
+ q=malloc(strlen(p)+strlen(h)+sizeof("http://"));
+ if (!q) error("Out of memory.");
+ sprintf(q, "http:%s%s%s", (*h ? "//":""), h, p);
+ scriptptr=q;
+ }
+ return (scriptptr);
+}
diff --git a/cgi/cgihttpsscriptptr.c b/cgi/cgihttpsscriptptr.c
new file mode 100644
index 0000000..93f5339
--- /dev/null
+++ b/cgi/cgihttpsscriptptr.c
@@ -0,0 +1,47 @@
+/*
+** Copyright 1998 - 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static char *scriptptr=0;
+extern void error(const char *);
+
+void cgihttpsscriptptr_init()
+{
+ /* Reinitialisation required when running as fastcgi */
+ if (scriptptr) {
+ free(scriptptr);
+ scriptptr=0;
+ }
+}
+
+const char *cgihttpsscriptptr()
+{
+ if (!scriptptr)
+ {
+ char *p=getenv("SCRIPT_NAME");
+ char *h=getenv("HTTP_HOST");
+ char *q;
+
+ if (!h) h="";
+ if (!p) p="";
+
+ q=malloc(strlen(p)+strlen(h)+sizeof("https://"));
+ if (!q) error("Out of memory.");
+ sprintf(q, "https:%s%s%s", (*h ? "//":""), h, p);
+ scriptptr=q;
+ }
+ return (scriptptr);
+}
diff --git a/cgi/cgiinput.c b/cgi/cgiinput.c
new file mode 100644
index 0000000..64769de
--- /dev/null
+++ b/cgi/cgiinput.c
@@ -0,0 +1,154 @@
+/*
+** Copyright 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+
+#include "cgi.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+void cgi_output_unicode_escapes(const unicode_char *value,
+ const char *escapes,
+ void (*output_func)(const char *, size_t,
+ void *),
+ void *output_arg)
+{
+ while (value && *value)
+ {
+ size_t i;
+
+ for (i=0; value[i]; i++)
+ {
+ if (value[i] > 127 ||
+ strchr(escapes, value[i]))
+ break;
+ }
+
+ while (i)
+ {
+ char buf[100];
+
+ size_t n=sizeof(buf);
+ size_t j;
+
+ if (n > i)
+ n=i;
+
+ for (j=0; j<n; j++)
+ buf[j]=value[j];
+
+ (*output_func)(buf, j, output_arg);
+
+ value += j;
+ i -= j;
+ }
+
+ if (*value)
+ {
+ char buf[100];
+
+ sprintf(buf, "&#%lu;", (unsigned long)value[i]);
+
+ (*output_func)(buf, 0, output_arg);
+ ++value;
+ }
+ }
+}
+
+
+static void do_cgi_input(const char *name,
+ const unicode_char *value,
+ int size,
+ int maxlength,
+ const char *flags,
+
+ void (*output_func)(const char *, size_t,
+ void *),
+ void *output_arg)
+{
+ (*output_func)("<input name='", 0, output_arg);
+ (*output_func)(name, 0, output_arg);
+ (*output_func)("'", 0, output_arg);
+
+ if (strchr(flags, 'r'))
+ (*output_func)(" readonly='readonly'", 0, output_arg);
+ if (strchr(flags, 'd'))
+ (*output_func)(" disabled='disabled'", 0, output_arg);
+
+ (*output_func)("'", 0, output_arg);
+
+ if (size)
+ {
+ char buf[100];
+
+ sprintf(buf, " size=%d", size);
+
+ (*output_func)(buf, 0, output_arg);
+ }
+
+ if (maxlength)
+ {
+ char buf[100];
+
+ sprintf(buf, " maxlength=%d", maxlength);
+
+ (*output_func)(buf, 0, output_arg);
+ }
+
+ (*output_func)(" value='", 0, output_arg);
+
+ cgi_output_unicode_escapes(value, "<>'&", output_func, output_arg);
+
+ (*output_func)("' />", 0, output_arg);
+}
+
+static void cnt_bytes(const char *str, size_t cnt, void *arg)
+{
+ if (!cnt)
+ cnt=strlen(str);
+
+ *(size_t *)arg += cnt;
+}
+
+static void save_bytes(const char *str, size_t cnt, void *arg)
+{
+ char **p=(char **)arg;
+
+ if (!cnt)
+ cnt=strlen(str);
+
+ memcpy(*p, str, cnt);
+
+ *p += cnt;
+}
+
+char *cgi_input(const char *name,
+ const unicode_char *value,
+ int size,
+ int maxlength,
+ const char *flags)
+{
+ size_t cnt=1;
+ char *buf;
+ char *ptr;
+
+ if (!flags)
+ flags="";
+
+ do_cgi_input(name, value, size, maxlength, flags, cnt_bytes, &cnt);
+
+ buf=malloc(cnt);
+
+ if (!buf)
+ return NULL;
+
+ ptr=buf;
+ do_cgi_input(name, value, size, maxlength, flags, save_bytes, &ptr);
+ *ptr=0;
+ return buf;
+}
diff --git a/cgi/cginocache.c b/cgi/cginocache.c
new file mode 100644
index 0000000..43c9481
--- /dev/null
+++ b/cgi/cginocache.c
@@ -0,0 +1,23 @@
+/*
+** Copyright 1998 - 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+#include <stdio.h>
+
+void cginocache()
+{
+ if (cgi_useragent("MSIE"))
+ {
+ printf("Cache-Control: private\n");
+ printf("Pragma: private\n");
+ }
+ else
+ {
+ printf("Cache-Control: no-store\n");
+ printf("Pragma: no-cache\n");
+ }
+}
diff --git a/cgi/cgiredirect.c b/cgi/cgiredirect.c
new file mode 100644
index 0000000..7651159
--- /dev/null
+++ b/cgi/cgiredirect.c
@@ -0,0 +1,19 @@
+/*
+** Copyright 1998 - 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+#include <stdio.h>
+
+void cgiredirect(const char *buf)
+{
+ if (cgihasversion(1,1))
+ printf("Status: 303 Moved\n");
+ /* Alas, Communicator can't handle it */
+ cginocache();
+ printf("Location: %s\n\n", buf);
+ printf("URI: %s\n", buf);
+}
diff --git a/cgi/cgirelscriptptr.c b/cgi/cgirelscriptptr.c
new file mode 100644
index 0000000..edce813
--- /dev/null
+++ b/cgi/cgirelscriptptr.c
@@ -0,0 +1,28 @@
+/*
+** Copyright 1999-2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+extern void error(const char *);
+
+const char *cgirelscriptptr()
+{
+ const char *p=getenv("HTTPS");
+
+ if (p && strcasecmp(p, "on") == 0)
+ return (cgihttpsscriptptr());
+
+ return (cgihttpscriptptr());
+}
diff --git a/cgi/cgiselect.c b/cgi/cgiselect.c
new file mode 100644
index 0000000..647e1fa
--- /dev/null
+++ b/cgi/cgiselect.c
@@ -0,0 +1,134 @@
+/*
+** Copyright 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+
+#include "cgi.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+static void do_cgi_select(const char *name,
+ const char *optvalues,
+ const char *optlabels,
+ const char *default_value,
+ size_t list_size,
+ const char *flags,
+
+ void (*output_func)(const char *, size_t, void *),
+ void *output_arg)
+{
+ (*output_func)("<select name='", 0, output_arg);
+ (*output_func)(name, 0, output_arg);
+ (*output_func)("'", 0, output_arg);
+
+ if (strchr(flags, 'm'))
+ (*output_func)(" multiple='multiple'", 0, output_arg);
+ if (strchr(flags, 'd'))
+ (*output_func)(" disabled='disabled'", 0, output_arg);
+
+ (*output_func)("'>", 0, output_arg);
+
+ if (!optvalues)
+ optvalues="";
+
+ while (*optlabels)
+ {
+ const char *label_start=optlabels;
+ const char *value_start=optvalues;
+
+ while (*optlabels)
+ {
+ if (*optlabels == '\n')
+ break;
+ ++optlabels;
+ }
+
+ while (*optvalues)
+ {
+ if (*optvalues == '\n')
+ break;
+ ++optvalues;
+ }
+
+ (*output_func)("<option", 0, output_arg);
+
+ if (*value_start)
+ {
+ if (default_value &&
+ optvalues - value_start == strlen(default_value) &&
+ strncmp(value_start, default_value,
+ optvalues-value_start) == 0)
+ {
+ (*output_func)(" selected='selected'", 0,
+ output_arg);
+ }
+
+ (*output_func)(" value='", 0, output_arg);
+ (*output_func)(value_start, optvalues-value_start,
+ output_arg);
+ (*output_func)("'", 0, output_arg);
+ }
+ (*output_func)(">", 0, output_arg);
+ (*output_func)(label_start, optlabels-label_start, output_arg);
+ (*output_func)("</option>", 0, output_arg);
+ if (*optlabels)
+ ++optlabels;
+ if (*optvalues)
+ ++optvalues;
+ }
+
+ (*output_func)("</select>", 0, output_arg);
+}
+
+static void cnt_bytes(const char *str, size_t cnt, void *arg)
+{
+ if (!cnt)
+ cnt=strlen(str);
+
+ *(size_t *)arg += cnt;
+}
+
+static void save_bytes(const char *str, size_t cnt, void *arg)
+{
+ char **p=(char **)arg;
+
+ if (!cnt)
+ cnt=strlen(str);
+
+ memcpy(*p, str, cnt);
+ *p += cnt;
+}
+
+char *cgi_select(const char *name,
+ const char *optvalues,
+ const char *optlabels,
+ const char *default_value,
+ size_t list_size,
+ const char *flags)
+{
+ size_t cnt=1;
+ char *buf;
+ char *ptr;
+
+ if (!flags)
+ flags="";
+
+ do_cgi_select(name, optvalues, optlabels, default_value,
+ list_size, flags, cnt_bytes, &cnt);
+
+ buf=malloc(cnt);
+
+ if (!buf)
+ return NULL;
+
+ ptr=buf;
+ do_cgi_select(name, optvalues, optlabels, default_value,
+ list_size, flags, save_bytes, &ptr);
+ *ptr=0;
+ return buf;
+}
diff --git a/cgi/cgitextarea.c b/cgi/cgitextarea.c
new file mode 100644
index 0000000..e9d814b
--- /dev/null
+++ b/cgi/cgitextarea.c
@@ -0,0 +1,120 @@
+/*
+** Copyright 2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+
+#include "cgi.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+extern void cgi_output_unicode_escapes(const unicode_char *value,
+ const char *escapes,
+ void (*output_func)(const char *,
+ size_t,
+ void *),
+ void *output_arg);
+
+static void do_cgi_textarea(const char *name,
+ int rows,
+ int cols,
+ const unicode_char *value,
+ const char *opts,
+ const char *wrap,
+ void (*output_func)(const char *, size_t,
+ void *),
+ void *output_arg)
+{
+ (*output_func)("<textarea name='", 0, output_arg);
+ (*output_func)(name, 0, output_arg);
+ (*output_func)("'", 0, output_arg);
+
+ if (strchr(opts, 'r'))
+ (*output_func)(" readonly='readonly'", 0, output_arg);
+ if (strchr(opts, 'd'))
+ (*output_func)(" disabled='disabled'", 0, output_arg);
+
+ (*output_func)("'", 0, output_arg);
+
+ if (rows)
+ {
+ char buf[100];
+
+ sprintf(buf, " rows='%d'", rows);
+
+ (*output_func)(buf, 0, output_arg);
+ }
+
+ if (cols)
+ {
+ char buf[100];
+
+ sprintf(buf, " cols='%d'", cols);
+
+ (*output_func)(buf, 0, output_arg);
+ }
+
+ if (wrap)
+ {
+ (*output_func)(" wrap='", 0, output_arg);
+ (*output_func)(wrap, 0, output_arg);
+ (*output_func)("'", 0, output_arg);
+ }
+
+ (*output_func)(">", 0, output_arg);
+
+ cgi_output_unicode_escapes(value, "<>'&", output_func, output_arg);
+
+ (*output_func)("</textarea>", 0, output_arg);
+}
+
+static void cnt_bytes(const char *str, size_t cnt, void *arg)
+{
+ if (!cnt)
+ cnt=strlen(str);
+
+ *(size_t *)arg += cnt;
+}
+
+static void save_bytes(const char *str, size_t cnt, void *arg)
+{
+ char **p=(char **)arg;
+
+ if (!cnt)
+ cnt=strlen(str);
+
+ memcpy(*p, str, cnt);
+
+ *p += cnt;
+}
+
+char *cgi_textarea(const char *name,
+ int rows,
+ int cols,
+ const unicode_char *value,
+ const char *wrap,
+ const char *opts)
+{
+ size_t cnt=1;
+ char *buf;
+ char *ptr;
+
+ if (!opts)
+ opts="";
+
+ do_cgi_textarea(name, rows, cols, value, opts, wrap, cnt_bytes, &cnt);
+
+ buf=malloc(cnt);
+
+ if (!buf)
+ return NULL;
+
+ ptr=buf;
+ do_cgi_textarea(name, rows, cols, value, opts, wrap, save_bytes, &ptr);
+ *ptr=0;
+ return buf;
+}
diff --git a/cgi/cgiuseragent.c b/cgi/cgiuseragent.c
new file mode 100644
index 0000000..e1aa0a2
--- /dev/null
+++ b/cgi/cgiuseragent.c
@@ -0,0 +1,52 @@
+/*
+** Copyright 2000 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+
+#include "cgi.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+extern void error(const char *);
+
+int cgi_useragent(const char *p)
+{
+ const char *c=getenv("HTTP_USER_AGENT");
+
+ for ( ; c && *c; c++)
+ {
+ size_t i;
+
+ if (isalpha((int)(unsigned char)*c))
+ continue;
+
+ for (i=0; p[i]; i++)
+ {
+ int a,b;
+
+ a=(unsigned char)p[i];
+ b=(unsigned char)c[i+1];
+ if (!b)
+ break;
+
+ a=toupper(a);
+ b=toupper(b);
+ if (a != b)
+ break;
+ }
+
+ if (p[i] == 0)
+ {
+ int b=(unsigned char)c[i+2];
+
+ if (b == 0 || !isalpha(b))
+ return (1);
+ }
+ }
+ return (0);
+}
diff --git a/cgi/cgiversion.c b/cgi/cgiversion.c
new file mode 100644
index 0000000..265b297
--- /dev/null
+++ b/cgi/cgiversion.c
@@ -0,0 +1,32 @@
+/*
+** Copyright 1998 - 1999 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+/*
+*/
+#include "cgi.h"
+#include <stdlib.h>
+#include <ctype.h>
+
+void cgiversion(unsigned *major, unsigned *minor)
+{
+const char *p=getenv("SERVER_PROTOCOL");
+
+ *major=0;
+ *minor=0;
+ if (!p) return;
+ if ( toupper(*p++) != 'H' ||
+ toupper(*p++) != 'T' ||
+ toupper(*p++) != 'T' ||
+ toupper(*p++) != 'P' ||
+ *p++ != '/') return;
+
+ while (isdigit(*p))
+ *major= *major * 10 + (*p++ - '0');
+ if (*p++ == '.')
+ {
+ while (isdigit(*p))
+ *minor= *minor * 10 + (*p++ - '0');
+ }
+}
diff --git a/cgi/configure.in b/cgi/configure.in
new file mode 100644
index 0000000..9169322
--- /dev/null
+++ b/cgi/configure.in
@@ -0,0 +1,155 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl
+dnl Copyright 1998 - 2005 Double Precision, Inc. See COPYING for
+dnl distribution information.
+
+AC_INIT(cgi, 0.10, [courier-users@lists.sourceforge.net])
+
+>confdefs.h # Kill PACKAGE_ macros
+
+AC_CONFIG_SRCDIR(cgi.c)
+AC_CONFIG_AUX_DIR(../..)
+AM_INIT_AUTOMAKE([foreign no-define])
+
+AM_CONFIG_HEADER(cgi_config.h)
+
+dnl Checks for programs.
+AC_USE_SYSTEM_EXTENSIONS
+AC_PROG_CC
+AC_PROG_AWK
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_LIBTOOL
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h sys/time.h sys/wait.h sys/select.h sys/uio.h unistd.h)
+AC_HEADER_TIME
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+AC_CACHE_CHECK([for socklen_t],
+ cgi_cv_hassocklen_t,
+
+AC_COMPILE_IFELSE([
+AC_LANG_SOURCE( [
+#include <sys/types.h>
+#include <sys/socket.h>
+
+socklen_t sl_t;
+],[
+ accept(0, 0, &sl_t);
+])],
+ cgi_cv_hassocklen_t=yes,
+ cgi_cv_hassocklen_t=no)
+)
+
+socklen_t="int"
+
+if test $cgi_cv_hassocklen_t = yes
+then
+ :
+else
+ AC_DEFINE_UNQUOTED(socklen_t, int, [ Default definition for socklen_t ])
+fi
+
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(strdup strncasecmp)
+
+dnl Other checks
+
+AC_ARG_WITH(formdata,
+ [ --with-formdata Compile support for multipart/formdata],
+ AC_DEFINE_UNQUOTED(CGIFORMDATA, 1,
+ [ Whether to generate code to handle multipart/formdata ]))
+
+AC_ARG_WITH(maxargsize,
+ [ --with-maxargsize=nbytes Limit maximum size of CGI args],
+ CFLAGS="$CFLAGS -DCGIMAXARG=$withval")
+
+AC_ARG_WITH(maxformargsize,
+ [ --with-maxformargsize=nbytes Maximum size of multipart/formdata uploads],
+ CFLAGS="$CFLAGS -DCGIMAXFORMDATAARG=$withval")
+
+AC_TRY_RUN( [
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+int main()
+{
+FILE *fp=fopen("conftestval", "w");
+
+ if (!fp) exit(1);
+ fprintf(fp, "-%lu\n", ULONG_MAX);
+ fclose(fp);
+ return (0);
+}
+]
+ , [ MAXLONGSIZE=`wc -c conftestval | awk ' { print $1 } ' ` ],
+ [
+ AC_MSG_ERROR(Unable to run test program.)
+ ] ,
+ [
+ MAXLONGSIZE=60
+ AC_MSG_WARN([Cross compiling, setting MAXLONGSIZE to $MAXLONGSIZE])
+ ]
+ )
+
+AC_CACHE_CHECK([how to pass file descriptors],
+ ac_cv_sqwebmail_passfd,
+
+AC_TRY_COMPILE( [
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+int fd; struct msghdr msg; ],
+[
+ msg.msg_accrights=(caddr_t)fd;
+ msg.msg_accrightslen=sizeof(fd);
+],
+ ac_cv_sqwebmail_passfd=msg_accrights,
+
+ AC_TRY_COMPILE( [
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+struct msghdr msg; struct cmsghdr cmsg; char buf;
+],
+[
+ msg.msg_control = &buf;
+ msg.msg_controllen = 1;
+],
+ ac_cv_sqwebmail_passfd=msg_control,
+ ac_cv_sqwebmail_passfd=none)))
+
+if test "$ac_cv_sqwebmail_passfd" = "msg_accrights"
+then
+ AC_DEFINE_UNQUOTED(CGI_PASSFD_MSGACCRIGHTS,1,
+ [Pass file descriptors in msg_accrights])
+fi
+
+if test "$ac_cv_sqwebmail_passfd" = "msg_control"
+then
+ AC_DEFINE_UNQUOTED(CGI_PASSFD_MSGCONTROL,1,
+ [Pass file descriptors in msg_control])
+fi
+
+
+if test "$GCC" = yes ; then
+ CFLAGS="$CFLAGS -Wall"
+fi
+
+CFLAGS="$CFLAGS -I.. -I$srcdir/.."
+AC_SYS_LARGEFILE
+
+AC_DEFINE_UNQUOTED(MAXLONGSIZE, $MAXLONGSIZE, [ Calculate max size of long ])
+AC_OUTPUT(Makefile)