eris/eris.c

1796 lines
52 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>
2012-02-23 22:53:26 -07:00
#include <sys/select.h>
2011-08-16 14:36:11 -06:00
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <dirent.h>
#include <sys/mman.h>
#include <limits.h>
2012-02-23 22:53:26 -07:00
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
2012-02-13 20:54:27 -07:00
/*
* Some things I use for debugging
*/
#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
#define DUMP() DUMPf("")
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
2012-02-17 17:50:05 -07:00
#define DUMP_u(v) DUMPf("%s = %u", #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-17 17:50:05 -07:00
#define DUMP_buf(v, l) DUMPf("%s = %.*s", #v, (int)(l), v)
2012-02-13 20:54:27 -07:00
/*
* the following is the time in seconds that fnord should wait for a valid
* HTTP request
*/
2012-02-17 08:24:04 -07:00
#define READTIMEOUT 2
2011-08-16 14:36:11 -06:00
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-23 22:53:26 -07:00
#ifdef __linux__
2012-02-13 20:54:27 -07:00
/*
* defining USE_SENDFILE enables zero-copy TCP on Linux for static files.
2012-02-23 22:53:26 -07:00
* I (Fefe) measured over 320 meg per second with apache bench over localhost
* with sendfile and keep-alive.
2012-02-13 20:54:27 -07:00
*/
2011-08-16 14:36:11 -06:00
#define USE_SENDFILE
#include <sys/sendfile.h>
#endif
2012-02-23 22:53:26 -07:00
/*
* Memory-mapping may result in a performance boost. thttpd does it,
* but for a different reason (to cache frequently-accessed files).
* XXX: Some performance testing is in order here.
*/
#ifdef _POSIX_MAPPED_FILES
2011-08-16 14:36:11 -06:00
#define USE_MMAP
#endif
2012-02-23 22:53:26 -07:00
enum { UNKNOWN, GET, HEAD, POST } method;
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) */
2012-02-24 17:03:09 -07:00
char *path; /* string between GET and HTTP/1.0, *
2012-02-13 20:54:27 -07:00
* demangled */
2012-02-24 17:03:09 -07:00
char rpath[PATH_MAX];
2012-02-13 20:54:27 -07:00
char *ua = "?"; /* user-agent */
char *refer; /* Referrer: header */
char *accept_enc; /* Accept-Encoding */
int httpversion; /* 0 == 1.0, 1 == 1.1 */
int keepalive = 0; /* should we keep the connection alive? */
int rootdir; /* fd of root directory, so we can fchdir
2012-02-13 22:49:10 -07:00
* * back for keep-alive */
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;
unsigned long post_len = 0;
2011-08-16 14:36:11 -06:00
#if _FILE_OFFSET_BITS == 64
2012-02-13 20:54:27 -07:00
static unsigned long long rangestart,
rangeend; /* for ranged queries */
2012-02-13 22:49:10 -07:00
#define strtorange strtoull
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 */
2012-02-13 22:49:10 -07:00
#define strtorange strtoul
2011-08-16 14:36:11 -06:00
#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;
char *remote_port;
char *remote_ident;
2011-08-16 14:36:11 -06:00
2012-02-23 22:53:26 -07:00
#define BUFFER_SIZE 8192
char stdout_buf[BUFFER_SIZE];
2012-02-15 16:34:21 -07:00
2012-02-24 17:03:09 -07:00
/*
* TCP_CORK is a Linux extension to work around a TCP problem.
* http://www.baus.net/on-tcp_cork has a good description.
* XXX: Since we do our own buffering, TCP_CORK may not be helping
* with anything. This needs testing.
*/
void
cork(int enable)
{
#ifdef TCP_CORK
static int corked = 0;
if (enable != corked) {
setsockopt(1, IPPROTO_TCP, TCP_CORK, &enable, sizeof(enable));
corked = enable;
}
#endif
}
2012-02-13 20:54:27 -07:00
static void
sanitize(char *ua)
2012-02-13 22:49:10 -07:00
{ /* replace strings with underscores for *
2012-02-13 20:54:27 -07:00
* logging */
int j;
if (!ua)
return;
2012-02-13 20:54:27 -07:00
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);
2012-02-13 22:49:10 -07:00
sanitize(refer);
2012-02-13 20:54:27 -07:00
2012-02-14 17:23:32 -07:00
fprintf(stderr, "%s %ld %lu %s %s %s %s\n",
2012-02-13 20:54:27 -07:00
remote_ip ? remote_ip : "0.0.0.0",
2012-02-24 17:03:09 -07:00
retcode, (unsigned long) len, host, ua, refer, path);
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);
2012-02-14 17:23:32 -07:00
printf("HTTP/1.0 %ld %s\r\nConnection: close\r\n", code, httpcomment);
2012-02-17 17:50:05 -07:00
if (message) {
2012-02-14 17:23:32 -07:00
printf("Content-Length: %lu\r\nContent-Type: text/html\r\n\r\n",
2012-02-17 17:50:05 -07:00
(unsigned long) (strlen(message) * 2) + 15);
printf("<title>%s</title>%s", message, message);
2012-02-13 20:54:27 -07:00
} else {
2012-02-14 17:23:32 -07:00
fputs("\r\n", stdout);
2012-02-13 20:54:27 -07:00
}
2012-02-13 22:49:10 -07:00
fflush(stdout);
2012-02-13 20:54:27 -07:00
exit(0);
2011-08-16 14:36:11 -06:00
}
#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)
{
int sl = strlen(s);
2012-02-13 20:54:27 -07:00
register unsigned int i = 0;
2012-02-14 17:23:32 -07:00
2012-02-13 20:54:27 -07:00
for (; i < CGIENVLEN; i++)
if (!strncmp(s, cgivars[i], sl))
2012-02-13 20:54:27 -07:00
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-17 17:50:05 -07:00
/*
* Read header block. Try to read from stdin until \r?\n\r?\n is
* encountered. Read no more than *buflen bytes. Convert bare \n to
* \r\n. Preserve state across calls, provided buf is the same. Returns:
* 1 found 0 not found, call again -1 EOF or other error
*/
static int
2012-02-23 22:53:26 -07:00
read_header(FILE *f, char *buf, size_t * buflen)
{
2012-02-23 22:53:26 -07:00
/*
* I'm not crazy about all these static variables. But the idea,
* which seems to work, is that you pass in things like it was
* a read call, and then just keep passing that same stuff in over
* and over until it returns 0.
*
* Further down this winds up looking pretty nice. In here, sort
* of gross.
*/
2012-02-17 17:50:05 -07:00
static char *lastbuf = NULL;
static int found = 0;
static int bare = 1; /* LF here would be bare */
2012-02-23 22:53:26 -07:00
static int bufsize = 0;
2012-02-17 17:50:05 -07:00
2012-02-24 17:03:09 -07:00
if (! buf) {
lastbuf = NULL;
return 0;
}
2012-02-17 17:50:05 -07:00
if (lastbuf != buf) {
lastbuf = buf;
bare = 1;
found = 0;
2012-02-23 22:53:26 -07:00
bufsize = *buflen;
*buflen = 0;
2012-02-17 17:50:05 -07:00
}
2012-02-17 17:50:05 -07:00
while (*buflen + bare < bufsize) {
2012-02-23 22:53:26 -07:00
int c = fgetc(f);
2012-02-17 17:50:05 -07:00
switch (c) {
case EOF:
if (errno == EWOULDBLOCK) {
return 0;
} else {
return -1;
}
break;
case '\r':
bare = 0;
break;
case '\n':
if (bare) {
buf[(*buflen)++] = '\r';
bare = 1;
}
found += 1;
break;
default:
found = 0;
bare = 1;
break;
}
2012-02-17 17:50:05 -07:00
buf[(*buflen)++] = c;
2012-02-17 17:50:05 -07:00
if (found == 2) {
return 1;
}
}
2012-02-17 17:50:05 -07:00
return 0;
}
2012-02-17 17:50:05 -07:00
char *
2012-02-14 17:23:32 -07:00
env_append(const char *key, const char *val)
{
static char buf[MAXHEADERLEN * 2 + PATH_MAX + 200];
static char *p = buf;
char *ret = p;
2012-02-14 17:23:32 -07:00
if (!key) {
2012-02-14 17:23:32 -07:00
p = buf;
return NULL;
}
p = stpcpy(p, key);
*(p++) = '=';
if (val) {
p = stpcpy(p, val) + 1;
} else {
*(p++) = 0;
}
return ret;
}
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" };
register unsigned int en = elen(envp);
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;
2012-02-14 17:23:32 -07:00
i = 3;
env_append(NULL, NULL);
cgi_env[i++] = env_append("SERVER_NAME", host);
cgi_env[i++] = env_append("SERVER_PORT", port);
cgi_env[i++] = env_append("REQUEST_METHOD", method_name[method]);
cgi_env[i++] = env_append("REQUEST_URI", uri);
2012-02-24 17:03:09 -07:00
cgi_env[i++] = env_append("SCRIPT_NAME", path);
if (remote_ip)
cgi_env[i++] = env_append("REMOTE_ADDR", remote_ip);
if (remote_port)
cgi_env[i++] = env_append("REMOTE_PORT", remote_port);
if (remote_ident)
cgi_env[i++] = env_append("REMOTE_IDENT", remote_ident);
if (ua)
cgi_env[i++] = env_append("HTTP_USER_AGENT", ua);
if (cookie)
cgi_env[i++] = env_append("HTTP_COOKIE", cookie);
if (refer)
cgi_env[i++] = env_append("HTTP_REFERER", refer);
if (accept_enc)
cgi_env[i++] = env_append("HTTP_ACCEPT_ENCODING", accept_enc);
if (auth_type)
cgi_env[i++] = env_append("AUTH_TYPE", auth_type);
if (content_type)
cgi_env[i++] = env_append("CONTENT_TYPE", content_type);
if (content_type)
cgi_env[i++] = env_append("CONTENT_LENGTH", content_len);
if (args)
cgi_env[i++] = env_append("QUERY_STRING", args);
2012-02-13 20:54:27 -07:00
if (pathinfo) {
char *rp = realpath(pathinfo, NULL);
2012-02-14 17:23:32 -07:00
cgi_env[i++] = env_append("PATH_INFO", pathinfo);
cgi_env[i++] = env_append("PATH_TRANSLATED", rp ? rp : pathinfo);
if (rp)
free(rp);
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
*/
2012-02-13 22:49:10 -07:00
if (args && (strchr(args, '=') == 0)) {
2012-02-13 20:54:27 -07:00
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;
}
2012-02-14 17:23:32 -07:00
{
char tmp[PATH_MAX];
2012-02-13 20:54:27 -07:00
2012-02-24 17:03:09 -07:00
i = strrchr(rpath, '/') - rpath;
2012-02-23 22:53:26 -07:00
if (i) {
2012-02-24 17:03:09 -07:00
strncpy(tmp, rpath + 1, i);
2012-02-23 22:53:26 -07:00
tmp[i] = 0;
chdir(tmp);
}
2012-02-14 17:23:32 -07:00
}
2012-02-13 20:54:27 -07:00
{
char tmp[PATH_MAX];
2012-02-14 17:23:32 -07:00
/*
* program name
*/
cgi_arg[0] = tmp;
tmp[0] = '.';
2012-02-24 17:03:09 -07:00
strcpy(tmp + 1, rpath + i);
2012-02-14 17:23:32 -07:00
/*
* start cgi
*/
execve(cgi_arg[0], cgi_arg, cgi_env);
raise(SIGQUIT); /* gateway unavailable. */
2012-02-14 17:23:32 -07:00
}
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
start_cgi(int nph, const char *pathinfo, const char *const *envp)
{
2012-02-23 22:53:26 -07:00
// XXX: Is it safe to reuse headerbuf from main?
2012-02-13 20:54:27 -07:00
size_t size = 0;
int pid;
2012-02-23 22:53:26 -07:00
char cgiheader[BUFFER_SIZE];
size_t cgiheaderlen = BUFFER_SIZE;
int cin[2];
int cout[2];
FILE *cinf;
2012-02-13 20:54:27 -07:00
if (pipe(cin) || pipe(cout) || !(cinf = fdopen(cin[0], "rb"))) {
2012-02-13 20:54:27 -07:00
badrequest(500, "Internal Server Error",
"Server Resource problem.");
}
pid = fork();
if (-1 == pid) {
badrequest(500, "Internal Server Error",
"Unable to fork.");
}
if (pid) {
/* Parent */
int passthru = nph;
2012-02-13 20:54:27 -07:00
fcntl(cin[0], F_SETFL, O_NONBLOCK);
signal(SIGCHLD, cgi_child);
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
2012-02-13 20:54:27 -07:00
close(cin[1]);
close(cout[0]);
2012-02-13 20:54:27 -07:00
alarm(CGI_TIMEOUT);
while (1) {
int nfds;
fd_set rfds, wfds;
2012-02-23 22:53:26 -07:00
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(cin[0], &rfds);
nfds = cin[0];
2012-02-23 22:53:26 -07:00
if (post_len) {
/* have post data */
FD_SET(cout[1], &wfds);
if (cout[1] > nfds) {
nfds = cout[1];
2012-02-23 22:53:26 -07:00
}
} else if (cout[1] >= 0) {
close(cout[1]); /* no post data */
cout[1] = -1;
}
if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) {
break;
2012-02-23 22:53:26 -07:00
}
2012-02-13 20:54:27 -07:00
if (FD_ISSET(cin[0], &rfds)) {
if (passthru) {
size_t len;
/* Re-use this big buffer */
len = fread(cgiheader, 1, sizeof cgiheader, cinf);
if (0 == len) {
/* CGI is done */
break;
}
fwrite(cgiheader, 1, len, stdout);
size += len;
} else {
int ret;
ret = read_header(cinf, cgiheader, &cgiheaderlen);
if (0 == ret) {
/* Call read_header again */
} else if (-1 == ret) {
/* EOF or error */
badrequest(500, "CGI Error",
"CGI output too weird");
2012-02-23 22:53:26 -07:00
} else {
/* Entire header is in memory now */
passthru = 1;
/* XXX: I think we need to look for Location:
* anywhere, but fnord got away with checking
* only the first header field, so I will too.
*/
if (memcmp(cgiheader, "Location: ", 10) == 0) {
retcode = 302;
printf
("HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n");
2012-02-23 22:53:26 -07:00
fwrite(cgiheader, 1, cgiheaderlen, stdout);
dolog(0);
exit(0);
2012-02-13 20:54:27 -07:00
}
retcode = 200;
printf("HTTP/1.0 200 OK\r\nServer: "
FNORD
"\r\nPragma: no-cache\r\nConnection: close\r\n");
signal(SIGCHLD, SIG_IGN);
fwrite(cgiheader, 1, cgiheaderlen, stdout);
2012-02-13 20:54:27 -07:00
}
}
} else if (FD_ISSET(cout[1], &wfds)) {
/*
* write to cgi the post data
*/
if (post_len) {
size_t len;
char buf[BUFFER_SIZE];
size_t nmemb = min(BUFFER_SIZE, post_len);
len = fread(buf, 1, nmemb, stdin);
if (len < 1) {
break;
2012-02-13 20:54:27 -07:00
}
post_len -= len;
write(cout[1], buf, len);
} else {
close(cout[1]);
2012-02-13 20:54:27 -07:00
}
}
}
alarm(0);
2012-02-13 20:54:27 -07:00
fflush(stdout);
dolog(size);
2012-02-24 17:03:09 -07:00
cork(0);
2012-02-13 20:54:27 -07:00
} else {
/* Child */
2011-08-16 14:36:11 -06:00
close(cout[1]);
close(cin[0]);
2011-08-16 14:36:11 -06:00
dup2(cout[0], 0);
dup2(cin[1], 1);
close(cout[0]);
close(cin[1]);
2011-08-16 14:36:11 -06:00
2012-02-13 20:54:27 -07:00
do_cgi(pathinfo, envp);
}
exit(0);
2011-08-16 14:36:11 -06:00
}
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)
{
2012-02-13 22:49:10 -07:00
int slen = strlen(hname);
2012-02-13 20:54:27 -07:00
int i;
char *c;
for (i = 0; i < buflen - slen - 2; ++i) {
if (!strncasecmp(buf + i, hname, slen)) {
if (i && (buf[i - 1] && buf[i - 1] != '\n'))
continue;
if (buf[i + slen] != ':' || buf[i + slen + 1] != ' ')
continue;
c = buf + i + slen + 2;
i += slen + 2;
for (; i < buflen; ++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 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, 0}};
static const char *default_mimetype = "application/octet-stream";
2012-02-13 20:54:27 -07:00
/*
* Determine MIME type from file extension
2012-02-13 20:54:27 -07:00
*/
static const char *
getmimetype(char *url)
2012-02-13 20:54:27 -07:00
{
char *ext = strrchr(url, '.');
2011-08-16 14:36:11 -06:00
2012-02-17 17:50:05 -07:00
if (ext) {
int i;
2012-02-17 17:50:05 -07:00
ext++;
for (i = 0; mimetab[i].name; ++i) {
if (!strcmp(mimetab[i].name, ext)) {
return mimetab[i].type;
}
2012-02-13 20:54:27 -07:00
}
2011-08-16 14:36:11 -06:00
}
return default_mimetype;
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 22:49:10 -07:00
static time_t
2012-02-13 20:54:27 -07:00
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}
};
2012-02-27 22:07:22 -07:00
enum { D_START, D_END, D_MON, D_DAY, D_YEAR, D_HOUR, D_MIN, D_SEC };
unsigned sec = 60,
min = 60,
hour = 24,
day = 32,
2012-02-13 20:54:27 -07:00
mon,
2012-02-27 22:07:22 -07:00
year = 1969;
char month[3] = {0, 0, 0};
2012-02-13 20:54:27 -07:00
int c;
2012-02-27 22:07:22 -07:00
unsigned n = 0;
char flag = 1;
char state = D_START;
char isctime = 0;
2012-02-13 20:54:27 -07:00
do {
c = *s++;
switch (state) {
2012-02-17 17:50:05 -07:00
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;
2012-02-13 20:54:27 -07:00
break;
2012-02-17 17:50:05 -07:00
case 'D':
mon = 12;