eris/fnord.c

2140 lines
61 KiB
C
Raw Normal View History

2012-02-13 20:54:27 -07:00
/*
* simple httpd to be started from tcpserver
*/
2011-08-16 14:36:11 -06:00
#define _FILE_OFFSET_BITS 64
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
2012-02-13 20:54:27 -07:00
#include <stdio.h>
2011-08-16 14:36:11 -06:00
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <grp.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <dirent.h>
#include <sys/mman.h>
#include <limits.h>
2012-02-13 20:54:27 -07:00
/*
* Some things I use for debugging
*/
#ifdef DUMP
2012-02-13 20:54:27 -07:00
#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
#else
2012-02-13 20:54:27 -07:00
#define DUMPf(fmt, args...)
#endif
#define DUMP() DUMPf("")
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v)
#define DUMP_s(v) DUMPf("%s = %s", #v, v)
#define DUMP_c(v) DUMPf("%s = %c", #v, v)
#define DUMP_p(v) DUMPf("%s = %p", #v, v)
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to enable support for CGI
*/
2011-08-16 14:36:11 -06:00
// #define CGI
#ifdef CGI
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to enable support for "index.cgi" That is:
* if "index.html" is not present then look for "index.cgi"
*/
2011-08-16 14:36:11 -06:00
#define INDEX_CGI
#endif
2012-02-13 20:54:27 -07:00
/*
* the following switch will make fnord normalize the Host: HTTP header
* from "foo" to "foo:80"
*/
2011-08-16 14:36:11 -06:00
#define NORMALIZE_HOST
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to enable support for autogenerated
* directory-listings for directories without index
*/
/*
* #define DIR_LIST
*/
2011-08-16 14:36:11 -06:00
#ifdef DIR_LIST
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to enable support for system symlink
* dereferencingr. HOPE YOU KNOW WHAT YOU'RE LINKING ! e.g.: if a file
* foo is a symlink to /etc/passwd and you don't have a chroot enviroment
* then the system-wide /etc/passwd is provided !!! If the symlink is
* dangling OR this option is not active the symlink is provided as a new
* http-uri. e.g.: is foo a symlink to /etc/passwd than the clinet gets a
* href to http://<vhost>/etc/passwd
*/
/*
* #define SYSTEM_SYMLINK_DEREF
*/
2011-08-16 14:36:11 -06:00
#endif
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to get full-host redirection. If a file is
* not found locally, and $REDIRECT_HOST is set, fnord will issue a
* redirect to strcat($REDIRECT_HOST,uri). Otherwise, if $REDIRECT_URI is
* set, fnord will issue a redirect to $REDIRECT_URI. Only if those fail
* will a 404 error be returned.
*/
2011-08-16 14:36:11 -06:00
#define OLD_STYLE_REDIRECT
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to get full-host redirection. if the
* virtual host directory/symlink is a broken symlink, fnord will issue a
* redirect. If the contents of the symlink starts with an equal sign
* ('='), fnord will throw away the URI part.
*/
2011-08-16 14:36:11 -06:00
#define REDIRECT
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to make fnord chroot to the current
* working directory and drop privileges
*/
2011-08-16 14:36:11 -06:00
#define CHROOT
2012-02-13 20:54:27 -07:00
/*
* uncomment the following line to make fnord support connection
* keep-alive
*/
2011-08-16 14:36:11 -06:00
#define KEEPALIVE
2012-02-13 20:54:27 -07:00
/*
* the following is the time in seconds that fnord should wait for a valid
* HTTP request
*/
2011-08-16 14:36:11 -06:00
#define READTIMEOUT 20
2012-02-13 20:54:27 -07:00
/*
* the following is the time in seconds that fnord should wait before
* aborting a request when trying to write the answer
*/
2011-08-16 14:36:11 -06:00
#define WRITETIMEOUT 20
2012-02-13 20:54:27 -07:00
#define CGI_TIMEOUT (5*60) /* 5 minutes time-out for CGI to complete */
2011-08-16 14:36:11 -06:00
2012-02-13 20:54:27 -07:00
/*
* defining USE_SENDFILE enables zero-copy TCP on Linux for static files.
* I measured over 320 meg per second with apache bench over localhost
* with sendfile and keep-alive. However, sendfile does not work with
* large files and may be considered cheating ;-) Also, sendfile is a
* blocking operation. Thus, no timeout handling.
*/
2011-08-16 14:36:11 -06:00
#define USE_SENDFILE
#ifndef __linux__
#undef USE_SENDFILE
#endif
#ifdef USE_SENDFILE
#include <sys/sendfile.h>
#endif
#ifndef O_NDELAY
#define O_NDELAY O_NONBLOCK
#endif
#define USE_MMAP
#ifndef _POSIX_MAPPED_FILES
#undef USE_MMAP
#endif
enum { UNKNOWN, GET, HEAD, POST } method;
#ifdef TCP_CORK
2012-02-13 20:54:27 -07:00
static int corked;
2011-08-16 14:36:11 -06:00
#endif
2012-02-13 20:54:27 -07:00
static long retcode = 404; /* used for logging code */
char *host = "?"; /* Host: header */
char *port; /* also Host: header, :80 part */
char *args; /* URL behind ? (for CGIs) */
char *url; /* string between GET and HTTP/1.0,
* demangled */
char *ua = "?"; /* user-agent */
char *refer; /* Referrer: header */
char *accept_enc; /* Accept-Encoding */
int httpversion; /* 0 == 1.0, 1 == 1.1 */
2011-08-16 14:36:11 -06:00
#ifdef KEEPALIVE
2012-02-13 20:54:27 -07:00
int keepalive = 0; /* should we keep the connection alive? */
int rootdir; /* fd of root directory, so we can fchdir
* back for keep-alive */
2011-08-16 14:36:11 -06:00
#endif
#ifdef CGI
2012-02-13 20:54:27 -07:00
char *cookie; /* Referrer: header */
char *uri; /* copy of url before demangling */
char *content_type;
char *content_len;
char *auth_type;
char *post_miss;
unsigned long post_mlen;
unsigned long post_len = 0;
2011-08-16 14:36:11 -06:00
#endif
#if _FILE_OFFSET_BITS == 64
2012-02-13 20:54:27 -07:00
static unsigned long long rangestart,
rangeend; /* for ranged queries */
2011-08-16 14:36:11 -06:00
#else
2012-02-13 20:54:27 -07:00
static unsigned long rangestart,
rangeend; /* for ranged queries */
2011-08-16 14:36:11 -06:00
#define scan_range scan_ulong
#define buffer_putrange buffer_putulong
#endif
static const char days[] = "SunMonTueWedThuFriSat";
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
#define MAXHEADERLEN 8192
2012-02-13 20:54:27 -07:00
char *remote_ip;
2011-08-16 14:36:11 -06:00
#ifdef CGI
2012-02-13 20:54:27 -07:00
char *remote_port;
char *remote_ident;
2011-08-16 14:36:11 -06:00
#endif
2012-02-13 20:54:27 -07:00
static void
sanitize(char *ua)
{ /* replace strings with underscores for
* logging */
int j;
for (j = 0; ua[j]; ++j)
if (isspace(ua[j]))
ua[j] = '_';
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
static void
dolog(off_t len)
{ /* write a log line to stderr */
sanitize(host);
sanitize(ua);
if (refer)
sanitize(refer);
fprintf(stderr, "%s %d %d %s %s %s %s\n",
remote_ip ? remote_ip : "0.0.0.0",
retcode, len, host, ua, refer, url);
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
/*
* output an error message and exit
*/
static void
badrequest(long code, const char *httpcomment, const char *message)
{
retcode = code;
dolog(0);
buffer_puts(buffer_1, "HTTP/1.0 ");
buffer_putulong(buffer_1, code);
buffer_putspace(buffer_1);
buffer_puts(buffer_1, httpcomment);
buffer_puts(buffer_1, "\r\nConnection: close\r\n");
if (message[0]) {
buffer_puts(buffer_1, "Content-Length: ");
buffer_putulong(buffer_1, strlen(message));
buffer_puts(buffer_1, "\r\nContent-Type: text/html\r\n\r\n");
buffer_puts(buffer_1, message);
} else {
buffer_puts(buffer_1, "\r\n");
}
buffer_flush(buffer_1);
exit(0);
2011-08-16 14:36:11 -06:00
}
#ifdef CGI
#define CGIENVLEN 21
static const char *cgivars[CGIENVLEN] = {
2012-02-13 20:54:27 -07:00
"GATEWAY_INTERFACE=",
"SERVER_PROTOCOL=",
"SERVER_SOFTWARE=",
"SERVER_NAME=",
"SERVER_PORT=",
"REQUEST_METHOD=",
"REQUEST_URI=",
"SCRIPT_NAME=",
"REMOTE_ADDR=",
"REMOTE_PORT=",
"REMOTE_IDENT=",
"HTTP_USER_AGENT=",
"HTTP_COOKIE=",
"HTTP_REFERER=",
"HTTP_ACCEPT_ENCODING=",
"AUTH_TYPE=",
"CONTENT_TYPE=",
"CONTENT_LENGTH=",
"QUERY_STRING=",
"PATH_INFO=",
"PATH_TRANSLATED="
2011-08-16 14:36:11 -06:00
};
2012-02-13 20:54:27 -07:00
static int
iscgivar(const char *s)
{
register unsigned int i = 0;
for (; i < CGIENVLEN; i++)
if (str_start(s, cgivars[i]))
return 1;
return 0;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
static unsigned int
elen(register const char *const *e)
{
register unsigned int i = 0;
while (e[i])
i++;
return i;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
static void
do_cgi(const char *pathinfo, const char *const *envp)
{
const char *method_name[] = { "?", "GET", "HEAD", "POST" };
char cgi_env_buf[MAXHEADERLEN * 2 + PATH_MAX + 200];
register unsigned int en = elen(envp);
char *tmp = cgi_env_buf;
char **cgi_arg;
register int i;
char **cgi_env =
(char **) alloca((CGIENVLEN + en + 1) * sizeof(char *));
cgi_env[0] = "GATEWAY_INTERFACE=CGI/1.1";
cgi_env[1] = "SERVER_PROTOCOL=HTTP/1.0";
cgi_env[2] = "SERVER_SOFTWARE=" FNORD;
cgi_env[3] = tmp;
tmp += str_copy(tmp, "SERVER_NAME=");
tmp += str_copy(tmp, host);
*tmp = 0;
++tmp;
cgi_env[4] = tmp;
tmp += str_copy(tmp, "SERVER_PORT=");
tmp += str_copy(tmp, port);
*tmp = 0;
++tmp;
cgi_env[5] = tmp;
tmp += str_copy(tmp, "REQUEST_METHOD=");
tmp += str_copy(tmp, method_name[method]);
*tmp = 0;
++tmp;
cgi_env[6] = tmp;
tmp += str_copy(tmp, "REQUEST_URI=");
tmp += str_copy(tmp, uri);
*tmp = 0;
2011-08-16 14:36:11 -06:00
++tmp;
2012-02-13 20:54:27 -07:00
cgi_env[7] = tmp;
tmp += str_copy(tmp, "SCRIPT_NAME=");
tmp += str_copy(tmp, url);
*tmp = 0;
++tmp;
i = 7;
if (remote_ip) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "REMOTE_ADDR=");
tmp += str_copy(tmp, remote_ip);
*tmp = 0;
++tmp;
}
if (remote_port) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "REMOTE_PORT=");
tmp += str_copy(tmp, remote_port);
*tmp = 0;
++tmp;
}
if (remote_ident) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "REMOTE_IDENT=");
tmp += str_copy(tmp, remote_ident);
*tmp = 0;
++tmp;
}
if (ua) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "HTTP_USER_AGENT=");
tmp += str_copy(tmp, ua);
*tmp = 0;
++tmp;
}
if (cookie) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "HTTP_COOKIE=");
tmp += str_copy(tmp, cookie);
*tmp = 0;
++tmp;
}
if (refer) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "HTTP_REFERER=");
tmp += str_copy(tmp, refer);
*tmp = 0;
++tmp;
}
if (accept_enc) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "HTTP_ACCEPT_ENCODING=");
tmp += str_copy(tmp, accept_enc);
*tmp = 0;
++tmp;
}
if (auth_type) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "AUTH_TYPE=");
tmp += str_copy(tmp, auth_type);
*tmp = 0;
++tmp;
}
if (content_type) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "CONTENT_TYPE=");
tmp += str_copy(tmp, content_type);
*tmp = 0;
++tmp;
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "CONTENT_LENGTH=");
DUMP_s(content_len);
tmp += str_copy(tmp, content_len);
*tmp = 0;
++tmp;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
if (args) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "QUERY_STRING=");
tmp += str_copy(tmp, args);
*tmp = 0;
++tmp;
}
if (pathinfo) {
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "PATH_INFO=");
tmp += str_copy(tmp, pathinfo);
*tmp = 0;
++tmp;
cgi_env[++i] = tmp;
tmp += str_copy(tmp, "PATH_TRANSLATED=");
tmp +=
realpath(pathinfo, tmp) ? str_len(tmp) : str_copy(tmp,
pathinfo);
++tmp;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
{
unsigned int j = 0;
for (; j < en; j++)
if (!iscgivar(envp[j]))
cgi_env[++i] = (char *) envp[j];
}
cgi_env[++i] = 0;
/*
* argv
*/
if (args && (args[str_chr(args, '=')] == 0)) {
int n = 3;
for (i = 0; args[i]; ++i)
if (args[i] == '+')
++n;
cgi_arg = alloca(n * sizeof(char *));
cgi_arg[n = 1] = args;
for (i = 0; args[i]; ++i) {
if (args[i] == '+') {
args[i] = 0;
++i;
cgi_arg[++n] = args + i;
}
}
cgi_arg[++n] = 0;
} else {
cgi_arg = alloca(2 * sizeof(char *));
cgi_arg[1] = 0;
}
i = strrchr(url, '/') - url;
strncpy(tmp, url + 1, i);
tmp[i] = 0;
chdir(tmp);
/*
* program name
*/
cgi_arg[0] = tmp;
tmp[0] = '.';
tmp[str_copy(tmp + 1, url + i) + 1] = 0;
/*
* start cgi
*/
execve(cgi_arg[0], cgi_arg, cgi_env);
raise(SIGQUIT); /* gateway unavailable. */
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
static void
cgi_child(int sig)
{
int n,
pid = waitpid(0, &n, WNOHANG);
if (pid > 0) {
if (WIFSIGNALED(n)) {
if (WTERMSIG(n) == SIGALRM)
badrequest(504, "Gateway Time-out",
"Gateway has hit the Time-out.");
else
badrequest(502, "Bad Gateway",
"Gateway broken or unavailable.");
}
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
signal(SIGCHLD, cgi_child);
}
static void
cgi_send_correct_http(const char *s, unsigned int sl)
{
unsigned int i;
char ch = 0;
for (i = 0; i < sl; ++i) {
if ((s[i] == '\r') && (s[i + 1] == '\n')) {
++i;
goto out_nl;
} else {
if (s[i] != '\n')
buffer_put(buffer_1, s + i, 1);
else {
out_nl:buffer_put(buffer_1, "\r\n", 2);
if (ch == '\n') {
++i;
break;
}
}
}
ch = s[i];
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
buffer_put(buffer_1, s + i, sl - i);
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
static void
start_cgi(int nph, const char *pathinfo, const char *const *envp)
{
size_t size = 0;
int n;
int pid;
char ibuf[8192],
obuf[8192];
int fd[2],
df[2];
if (pipe(fd) || pipe(df)) {
badrequest(500, "Internal Server Error",
"Server Resource problem.");
}
if ((pid = fork())) {
if (pid > 0) {
struct pollfd pfd[2];
int nr = 1;
int startup = 1;
signal(SIGCHLD, cgi_child);
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
close(df[0]);
close(fd[1]);
pfd[0].fd = fd[0];
pfd[0].events = POLLIN;
pfd[0].revents = 0;
pfd[1].fd = df[1];
pfd[1].events = POLLOUT;
pfd[1].revents = 0;
if (post_len)
++nr; /* have post data */
else
close(df[1]); /* no post data */
while (poll(pfd, nr, -1) != -1) {
/*
* read from cgi
*/
if (pfd[0].revents & POLLIN) {
if (!(n = read(fd[0], ibuf, sizeof(ibuf))))
break;
if (n < 0)
goto cgi_500;
/*
* startup
*/
if (startup) {
startup = 0;
if (nph) { /* NPH-CGI */
buffer_put(buffer_1, ibuf, n);
scan_ulong(ibuf + 9, &retcode); /* only
* get
* error
* code /
* str_len("HTTP/x.x
* ")==9 */
} else { /* CGI */
if (byte_diff(ibuf, 10, "Location: ") == 0) {
retcode = 302;
buffer_puts(buffer_1,
"HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n");
signal(SIGCHLD, SIG_IGN);
cgi_send_correct_http(ibuf, n);
buffer_flush(buffer_1);
dolog(0);
exit(0);
} else {
retcode = 200;
buffer_puts(buffer_1,
"HTTP/1.0 200 OK\r\nServer: "
FNORD
"\r\nPragma: no-cache\r\nConnection: close\r\n");
signal(SIGCHLD, SIG_IGN);
cgi_send_correct_http(ibuf, n);
}
}
}
/*
* non startup
*/
else {
buffer_put(buffer_1, ibuf, n);
}
size += n;
if (pfd[0].revents & POLLHUP)
break;
}
/*
* write to cgi the post data
*/
else if (nr > 1 && pfd[1].revents & POLLOUT) {
if (post_miss) {
write(df[1], post_miss, post_mlen);
post_miss = 0;
} else if (post_mlen < post_len) {
n = read(0, obuf, sizeof(obuf));
if (n < 1)
goto cgi_500;
post_mlen += n;
write(df[1], obuf, n);
} else {
--nr;
close(df[1]);
}
} else if (pfd[0].revents & POLLHUP)
break;
else {
cgi_500:if (startup)
badrequest(500, "Internal Server Error",
"Looks like the CGI crashed.");
else {
buffer_puts(buffer_1, "\n\n");
buffer_puts(buffer_1,
"Looks like the CGI crashed.");
buffer_puts(buffer_1, "\n\n");
break;
}
}
}
buffer_flush(buffer_1);
dolog(size);
2011-08-16 14:36:11 -06:00
#ifdef TCP_CORK
2012-02-13 20:54:27 -07:00
{
int zero = 0;
setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero));
}
2011-08-16 14:36:11 -06:00
#endif
2012-02-13 20:54:27 -07:00
}
} else {
close(df[1]);
close(fd[0]);
2011-08-16 14:36:11 -06:00
2012-02-13 20:54:27 -07:00
dup2(df[0], 0);
dup2(fd[1], 1);
2011-08-16 14:36:11 -06:00
2012-02-13 20:54:27 -07:00
close(df[0]);
close(fd[1]);
2011-08-16 14:36:11 -06:00
2012-02-13 20:54:27 -07:00
alarm(CGI_TIMEOUT);
do_cgi(pathinfo, envp);
}
exit(0);
2011-08-16 14:36:11 -06:00
}
#endif
2012-02-13 20:54:27 -07:00
static int
fromhex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else {
c |= ' ';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
}
return -1;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
/*
* header(buf,buflen,"User-Agent")="Mozilla"
*/
static char *
header(char *buf, int buflen, const char *hname)
{
int slen = str_len(hname);
int i;
char *c;
DUMPf("buflen %d, slen %d", buflen, slen);
for (i = 0; i < buflen - slen - 2; ++i) {
DUMPf("[%.*s] [%s]", slen, buf + i, hname);
if (!strncasecmp(buf + i, hname, slen)) {
DUMP();
if (i && (buf[i - 1] && buf[i - 1] != '\n'))
continue;
DUMP();
if (buf[i + slen] != ':' || buf[i + slen + 1] != ' ')
continue;
DUMP();
c = buf + i + slen + 2;
i += slen + 2;
for (; i < buflen; ++i) {
DUMP_c(buf[i]);
if (buf[i] == 0 || buf[i] == '\n' || buf[i] == '\r') {
buf[i] = 0;
break;
}
}
while (*c == ' ' || *c == '\t')
++c;
return c;
}
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
return 0;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
static char *encoding = 0;
static char *mimetype = "application/octet-stream";
static struct mimeentry {
const char *name,
*type;
} mimetab[] = {
{
"html", "text/html; charset=UTF-8"}, {
"htm", "text/html; charset=UTF-8"}, {
"txt", "text/plain; charset=UTF-8"}, {
"css", "text/css"}, {
"ps", "application/postscript"}, {
"pdf", "application/pdf"}, {
"js", "application/javascript"}, {
"gif", "image/gif"}, {
"png", "image/png"}, {
"jpeg", "image/jpeg"}, {
"jpg", "image/jpeg"}, {
"svg", "image/svg+xml"}, {
"mpeg", "video/mpeg"}, {
"mpg", "video/mpeg"}, {
"avi", "video/x-msvideo"}, {
"mov", "video/quicktime"}, {
"qt", "video/quicktime"}, {
"mp3", "audio/mpeg"}, {
"ogg", "audio/ogg"}, {
"wav", "audio/x-wav"}, {
"epub", "application/epub+zip"}, {
"dvi", "application/x-dvi"}, {
"pac", "application/x-ns-proxy-autoconfig"}, {
"sig", "application/pgp-signature"}, {
"swf", "application/x-shockwave-flash"}, {
"torrent", "application/x-bittorrent"}, {
"tar", "application/x-tar"}, {
"zip", "application/zip"}, {
"dtd", "text/xml"}, {
"xml", "text/xml"}, {
"xbm", "image/x-xbitmap"}, {
"xpm", "image/x-xpixmap"}, {
"xwd", "image/x-xwindowdump"}, {
"ico", "image/x-icon"}, {
0}};
/*
* try to find out MIME type and content encoding. This is called twice,
* once for the actual URL and once for URL.gz. If the actual URL already
* ende with .gz, return application/octet-stream to make sure the client
* can download the file even if he does not support gzip encoding
*/
static void
getmimetype(char *url, int explicit)
{
char save;
int ext;
ext = str_len(url);
while (ext > 0 && url[ext] != '.' && url[ext] != '/')
--ext;
if (url[ext] == '.') {
++ext;
if (str_equal(url + ext, "bz2"))
goto octetstream;
if (str_equal(url + ext, "gz")) {
if (!encoding) {
if (explicit)
goto octetstream;
encoding = "gzip";
save = url[ext - 1];
url[ext - 1] = 0;
getmimetype(url, explicit);
url[ext - 1] = save;
} else
octetstream:
mimetype = "application/octet-stream";
} else {
int i;
for (i = 0; mimetab[i].name; ++i)
if (str_equal(mimetab[i].name, url + ext)) {
mimetype = (char *) mimetab[i].type;
break;
}
}
2011-08-16 14:36:11 -06:00
}
}
2012-02-13 20:54:27 -07:00
static int
matchcommalist(const char *needle, const char *haystack)
{
/*
* needle: "text/html", haystack: the accept header, "text/html,
* text/plain\r\n"
*/
/*
* return nonzero if match was found
*/
int len = str_len(needle);
if (!byte_equal(needle, len, haystack))
return 0;
switch (haystack[len]) {
case ';':
case ',':
case '\r':
case '\n':
case 0:
return 1;
}
return 0;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
static int
findincommalist(const char *needle, const char *haystack)
{
const char *accept;
for (accept = haystack; accept;) {
/*
* format: foo/bar,
*/
const char *tmp = accept;
int final;
while (*tmp) {
if (*tmp == ';')
break;
else if (*tmp == ',')
break;
++tmp;
}
final = (*tmp == 0 || *tmp == ';');
if (matchcommalist("*/*", accept))
break;
if (matchcommalist(haystack, accept))
break;
accept = tmp + 1;
if (final)
return 0;
2011-08-16 14:36:11 -06:00
}
2012-02-13 20:54:27 -07:00
return 1;
2011-08-16 14:36:11 -06:00
}
2011-08-16 15:16:03 -06:00
/*
* timerfc function Copyright 1996, Michiel Boland.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
2012-02-13 20:54:27 -07:00
static time_t
timerfc(const char *s)
2011-08-16 15:16:03 -06:00
{
2012-02-13 20:54:27 -07:00
static const int daytab[2][12] = {
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
};
unsigned sec,
min,
hour,
day,
mon,
year;
char month[3];
int c;
unsigned n;
char flag;
char state;
char isctime;
enum { D_START, D_END, D_MON, D_DAY, D_YEAR, D_HOUR, D_MIN, D_SEC };
sec = 60;
min = 60;
hour = 24;
day = 32;
year = 1969;
isctime = 0;
month[0] = 0;
state = D_START;
n = 0;
flag = 1;
do {
c = *s++;
switch (state) {
case D_START:
if (c == ' ') {
state = D_MON;
isctime = 1;
} else if (c == ',')
state = D_DAY;
break;
case D_MON:
if (isalpha(c)) {
if (n < 3)
month[n++] = c;
} else {
if (n < 3)
return -1;
n = 0;
state = isctime ? D_DAY : D_YEAR;
}
break;
case D_DAY:
if (c == ' ' && flag);
else if (isdigit(c)) {
flag = 0;
n = 10 * n + (c - '0');
} else {
day = n;
n = 0;
state = isctime ? D_HOUR : D_MON;
}
break;
case D_YEAR:
if (isdigit(c))
n = 10 * n + (c - '0');
else {
year = n;
n = 0;
state = isctime ? D_END : D_HOUR;
}
break;
case D_HOUR:
if (isdigit(c))
n = 10 * n + (c - '0');
else {
hour = n;
n = 0;
state = D_MIN;
}
break;
case D_MIN:
if (isdigit(c))
n = 10 * n + (c - '0');
else {
min = n;
n = 0;
state = D_SEC;
}
break;
case D_SEC:
if (isdigit(c))
n = 10 * n + (c - '0');
else {
sec = n;
n = 0;
state = isctime ? D_YEAR : D_END;
}
break;
}
} while (state != D_END && c);
switch (month[0]) {
case 'A':
mon = (month[1] == 'p') ? 4 : 8;
break;
case 'D':
mon = 12;
break;
case 'F':
mon = 2;
break;
case 'J':
mon = (month[1] == 'a') ? 1 : ((month[2] == 'l') ? 7 : 6);
break;
case 'M':
mon = (month[2] == 'r') ? 3 : 5;
break;
case 'N':
mon = 11;
break;
case 'O':
mon = 10;
break;
case 'S':
mon = 9;
break;
default:
return -1;
}
if (year <= 100)
year += (year < 70) ? 2000 : 1900;
--mon;
--day;
if (sec >= 60 || min >= 60 || hour >= 60 || day >= 31)
return -1;
if (year < 1970)
return 0;
return sec + 60L * (min + 60L * (hour + 24L * (day +
daytab[year % 4 == 0
&& (year % 100
|| year %
400 ==