diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 |
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 |
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /cgi | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-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/.gitignore | 2 | ||||
| -rw-r--r-- | cgi/Makefile.am | 11 | ||||
| -rw-r--r-- | cgi/cgi.c | 842 | ||||
| -rw-r--r-- | cgi/cgi.h | 170 | ||||
| -rw-r--r-- | cgi/cgicheckbox.c | 41 | ||||
| -rw-r--r-- | cgi/cgicookie.c | 72 | ||||
| -rw-r--r-- | cgi/cgidaemon.c | 376 | ||||
| -rw-r--r-- | cgi/cgidaemond.c | 484 | ||||
| -rw-r--r-- | cgi/cgiextrapath.c | 21 | ||||
| -rw-r--r-- | cgi/cgihasversion.c | 16 | ||||
| -rw-r--r-- | cgi/cgihttpscriptptr.c | 48 | ||||
| -rw-r--r-- | cgi/cgihttpsscriptptr.c | 47 | ||||
| -rw-r--r-- | cgi/cgiinput.c | 154 | ||||
| -rw-r--r-- | cgi/cginocache.c | 23 | ||||
| -rw-r--r-- | cgi/cgiredirect.c | 19 | ||||
| -rw-r--r-- | cgi/cgirelscriptptr.c | 28 | ||||
| -rw-r--r-- | cgi/cgiselect.c | 134 | ||||
| -rw-r--r-- | cgi/cgitextarea.c | 120 | ||||
| -rw-r--r-- | cgi/cgiuseragent.c | 52 | ||||
| -rw-r--r-- | cgi/cgiversion.c | 32 | ||||
| -rw-r--r-- | cgi/configure.in | 155 |
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) |
