Add shttpd (http://shttpd.sourceforge.net/) to the list of third-party tools

This commit is contained in:
Charles Kerr
2008-05-12 18:33:52 +00:00
parent 911bce88c9
commit e317d52ddd
38 changed files with 7865 additions and 1 deletions

View File

@@ -298,6 +298,7 @@ AC_CONFIG_FILES([Makefile
third-party/Makefile
third-party/miniupnp/Makefile
third-party/libnatpmp/Makefile
third-party/shttpd/Makefile
macosx/Makefile
wx/Makefile
wx/images/Makefile

View File

@@ -1,7 +1,8 @@
SUBDIRS = \
libevent \
libnatpmp \
miniupnp \
libnatpmp
shttpd
EXTRA_DIST = \
macosx-libevent-config.h

5
third-party/shttpd/LICENSE vendored Normal file
View File

@@ -0,0 +1,5 @@
"THE BEER-WARE LICENSE" (Revision 42):
Sergey Lyubka wrote this software. As long as you retain this notice you
can do whatever you want with this stuff. If we meet some day, and you think
this stuff is worth it, you can buy me a beer in return.

67
third-party/shttpd/Makefile vendored Normal file
View File

@@ -0,0 +1,67 @@
SRCS= string.c shttpd.c log.c auth.c md5.c cgi.c config.c io_ssi.c \
io_file.c io_socket.c io_ssl.c io_emb.c io_dir.c io_cgi.c
HDRS= defs.h llist.h shttpd.h std_includes.h io.h md5.h ssl.h \
compat_unix.h compat_win32.h compat_rtems.h config.h
OBJS= $(SRCS:%.c=%.o)
PROG= shttpd
# Possible flags: (in brackets are rough numbers for 'gcc -O2' on i386)
# -DHAVE_MD5 - use system md5 library (-2kb)
# -DNDEBUG - strip off all debug code (-5kb)
# -D_DEBUG - build debug version (very noisy) (+6kb)
# -DNO_CGI - disable CGI support (-5kb)
# -DNO_SSL - disable SSL functionality (-2kb)
# -DNO_AUTH - disable authorization support (-4kb)
# -DCONFIG=\"file\" - use `file' as the default config file
# -DNO_SSI - disable SSI support (-4kb)
# XXX Note for the windows users. In order to build shttpd, MSVS6 is needed.
# Follow these steps:
# 1. Add c:\path_to_msvs6\bin to the system Path environment variable.
# 2. Add two new system environment variables:
# LIB=c:\path_to_msvs6\lib
# INCLUDE=c:\path_to_msvs6\include
# 3. start console, go to shttpd-VERSION\src\ directory
# 4. type "nmake msvc"
# 5. go to shttpd-VERSION\examples , type "nmake msvc"
VC6= ..\..\.. # MSVC installation path
CL_FLAGS= /MD /TC /nologo /DNDEBUG /Os # MSVC compiler flags
all:
@echo "make (unix|msvc|mingw|rtems)"
@echo on Linux, do \'LIBS=-ldl make unix\'
.c.o:
$(CC) -c $(CFLAGS) $< -o $@
unix: $(OBJS)
$(AR) -r lib$(PROG).a $(OBJS) && ranlib lib$(PROG).a
$(CC) $(CFLAGS) compat_unix.c standalone.c \
-o $(PROG) $(LIBS) -L. -l$(PROG)
rtems:
$(CC) -c $(CFLAGS) -DEMBEDDED $(SRCS) compat_rtems.c
$(AR) -r lib$(PROG).a *.o && ranlib lib$(PROG).a
#cl $(SRCS) compat_win32.c /c $(CL_FLAGS) /DEMBEDDED
#lib *.obj /out:shttpd.lib
msvc:
$(VC6)\bin\cl /I $(VC6)\include \
$(SRCS) compat_win32.c standalone.c $(CL_FLAGS) \
/link /out:$(PROG).exe /LIBPATH:$(VC6)\lib ws2_32.lib user32.lib
mingw:
$(CC) -c $(CFLAGS) -DEMBEDDED $(SRCS) compat_win32.c
$(AR) -r lib$(PROG).a *.o && ranlib lib$(PROG).a
$(CC) $(CFLAGS) $(SRCS) compat_win32.c standalone.c \
-o $(PROG) $(LIBS) -lws2_32 -lcomdlg32 -lcomctl32
man:
cat shttpd.1 | tbl | groff -man -Tascii | col -b > shttpd.1.txt
cat shttpd.1 | tbl | groff -man -Tascii | less
clean:
rm -rf *.o *.core $(PROG) lib$(PROG).a

28
third-party/shttpd/Makefile.am vendored Normal file
View File

@@ -0,0 +1,28 @@
# FIXME: build the compat_*.c files conditionally
noinst_LIBRARIES = libshttpd.a
libshttpd_a_SOURCES = \
string.c shttpd.c log.c auth.c md5.c cgi.c config.c io_ssi.c \
io_file.c io_socket.c io_ssl.c io_emb.c io_dir.c io_cgi.c \
compat_unix.c
noinst_HEADERS = \
defs.h llist.h shttpd.h std_includes.h io.h md5.h ssl.h \
compat_unix.h compat_win32.h compat_rtems.h config.h
extra_DIST = \
README \
LICENSE
# Possible flags: (in brackets are rough numbers for 'gcc -O2' on i386)
# -DHAVE_MD5 - use system md5 library (-2kb)
# -DNDEBUG - strip off all debug code (-5kb)
# -D_DEBUG - build debug version (very noisy) (+6kb)
# -DNO_CGI - disable CGI support (-5kb)
# -DNO_SSL - disable SSL functionality (-2kb)
# -DNO_AUTH - disable authorization support (-4kb)
# -DCONFIG=\"file\" - use `file' as the default config file
# -DNO_SSI - disable SSI support (-4kb)

3
third-party/shttpd/README vendored Normal file
View File

@@ -0,0 +1,3 @@
http://shttpd.sourceforge.net/
Sergey Lyubka wrote this software.
This snapshot si from shttpd-1.39

403
third-party/shttpd/auth.c vendored Normal file
View File

@@ -0,0 +1,403 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
#if !defined(NO_AUTH)
/*
* Stringify binary data. Output buffer must be twice as big as input,
* because each byte takes 2 bytes in string representation
*/
static void
bin2str(char *to, const unsigned char *p, size_t len)
{
const char *hex = "0123456789abcdef";
for (;len--; p++) {
*to++ = hex[p[0] >> 4];
*to++ = hex[p[0] & 0x0f];
}
}
/*
* Return stringified MD5 hash for list of vectors.
* buf must point to at least 32-bytes long buffer
*/
static void
md5(char *buf, ...)
{
unsigned char hash[16];
const struct vec *v;
va_list ap;
MD5_CTX ctx;
int i;
MD5Init(&ctx);
va_start(ap, buf);
for (i = 0; (v = va_arg(ap, const struct vec *)) != NULL; i++) {
assert(v->len >= 0);
if (v->len == 0)
continue;
if (i > 0)
MD5Update(&ctx, (unsigned char *) ":", 1);
MD5Update(&ctx,(unsigned char *)v->ptr,(unsigned int)v->len);
}
va_end(ap);
MD5Final(hash, &ctx);
bin2str(buf, hash, sizeof(hash));
}
/*
* Compare to vectors. Return 1 if they are equal
*/
static int
vcmp(const struct vec *v1, const struct vec *v2)
{
return (v1->len == v2->len && !memcmp(v1->ptr, v2->ptr, v1->len));
}
struct digest {
struct vec user;
struct vec uri;
struct vec nonce;
struct vec cnonce;
struct vec resp;
struct vec qop;
struct vec nc;
};
static const struct auth_keyword {
size_t offset;
struct vec vec;
} known_auth_keywords[] = {
{offsetof(struct digest, user), {"username=", 9}},
{offsetof(struct digest, cnonce), {"cnonce=", 7}},
{offsetof(struct digest, resp), {"response=", 9}},
{offsetof(struct digest, uri), {"uri=", 4}},
{offsetof(struct digest, qop), {"qop=", 4}},
{offsetof(struct digest, nc), {"nc=", 3}},
{offsetof(struct digest, nonce), {"nonce=", 6}},
{0, {NULL, 0}}
};
static void
parse_authorization_header(const struct vec *h, struct digest *dig)
{
const unsigned char *p, *e, *s;
struct vec *v, vec;
const struct auth_keyword *kw;
(void) memset(dig, 0, sizeof(*dig));
p = (unsigned char *) h->ptr + 7;
e = (unsigned char *) h->ptr + h->len;
while (p < e) {
/* Skip spaces */
while (p < e && (*p == ' ' || *p == ','))
p++;
/* Skip to "=" */
for (s = p; s < e && *s != '='; )
s++;
s++;
/* Is it known keyword ? */
for (kw = known_auth_keywords; kw->vec.len > 0; kw++)
if (kw->vec.len <= s - p &&
!memcmp(p, kw->vec.ptr, kw->vec.len))
break;
if (kw->vec.len == 0)
v = &vec; /* Dummy placeholder */
else
v = (struct vec *) ((char *) dig + kw->offset);
if (*s == '"') {
p = ++s;
while (p < e && *p != '"')
p++;
} else {
p = s;
while (p < e && *p != ' ' && *p != ',')
p++;
}
v->ptr = (char *) s;
v->len = p - s;
if (*p == '"')
p++;
DBG(("auth field [%.*s]", v->len, v->ptr));
}
}
/*
* Check the user's password, return 1 if OK
*/
static int
check_password(int method, const struct vec *ha1, const struct digest *digest)
{
char a2[32], resp[32];
struct vec vec_a2;
/* XXX Due to a bug in MSIE, we do not compare the URI */
/* Also, we do not check for authentication timeout */
if (/*strcmp(dig->uri, c->ouri) != 0 || */
digest->resp.len != 32 /*||
now - strtoul(dig->nonce, NULL, 10) > 3600 */)
return (0);
md5(a2, &known_http_methods[method], &digest->uri, NULL);
vec_a2.ptr = a2;
vec_a2.len = sizeof(a2);
md5(resp, ha1, &digest->nonce, &digest->nc,
&digest->cnonce, &digest->qop, &vec_a2, NULL);
return (!memcmp(resp, digest->resp.ptr, 32));
}
static FILE *
open_auth_file(struct shttpd_ctx *ctx, const char *path)
{
char name[FILENAME_MAX];
const char *p, *e;
FILE *fp = NULL;
int fd;
if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
/* Use global passwords file */
my_snprintf(name, sizeof(name), "%s",
ctx->options[OPT_AUTH_GPASSWD]);
} else {
/* Try to find .htpasswd in requested directory */
for (p = path, e = p + strlen(p) - 1; e > p; e--)
if (IS_DIRSEP_CHAR(*e))
break;
assert(IS_DIRSEP_CHAR(*e));
(void) my_snprintf(name, sizeof(name), "%.*s/%s",
(int) (e - p), p, HTPASSWD);
}
if ((fd = my_open(name, O_RDONLY, 0)) == -1) {
DBG(("open_auth_file: open(%s)", name));
} else if ((fp = fdopen(fd, "r")) == NULL) {
DBG(("open_auth_file: fdopen(%s)", name));
(void) close(fd);
}
return (fp);
}
/*
* Parse the line from htpasswd file. Line should be in form of
* "user:domain:ha1". Fill in the vector values. Return 1 if successful.
*/
static int
parse_htpasswd_line(const char *s, struct vec *user,
struct vec *domain, struct vec *ha1)
{
user->len = domain->len = ha1->len = 0;
for (user->ptr = s; *s != '\0' && *s != ':'; s++, user->len++);
if (*s++ != ':')
return (0);
for (domain->ptr = s; *s != '\0' && *s != ':'; s++, domain->len++);
if (*s++ != ':')
return (0);
for (ha1->ptr = s; *s != '\0' && !isspace(* (unsigned char *) s);
s++, ha1->len++);
DBG(("parse_htpasswd_line: [%.*s] [%.*s] [%.*s]", user->len, user->ptr,
domain->len, domain->ptr, ha1->len, ha1->ptr));
return (user->len > 0 && domain->len > 0 && ha1->len > 0);
}
/*
* Authorize against the opened passwords file. Return 1 if authorized.
*/
static int
authorize(struct conn *c, FILE *fp)
{
struct vec *auth_vec = &c->ch.auth.v_vec;
struct vec *user_vec = &c->ch.user.v_vec;
struct vec user, domain, ha1;
struct digest digest;
int ok = 0;
char line[256];
if (auth_vec->len > 20 &&
!my_strncasecmp(auth_vec->ptr, "Digest ", 7)) {
parse_authorization_header(auth_vec, &digest);
*user_vec = digest.user;
while (fgets(line, sizeof(line), fp) != NULL) {
if (!parse_htpasswd_line(line, &user, &domain, &ha1))
continue;
DBG(("[%.*s] [%.*s] [%.*s]", user.len, user.ptr,
domain.len, domain.ptr, ha1.len, ha1.ptr));
if (vcmp(user_vec, &user) &&
!memcmp(c->ctx->options[OPT_AUTH_REALM],
domain.ptr, domain.len)) {
ok = check_password(c->method, &ha1, &digest);
break;
}
}
}
return (ok);
}
int
check_authorization(struct conn *c, const char *path)
{
FILE *fp = NULL;
int len, n, authorized = 1;
const char *p, *s = c->ctx->options[OPT_PROTECT];
char protected_path[FILENAME_MAX];
FOR_EACH_WORD_IN_LIST(s, len) {
if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
continue;
if (!memcmp(c->uri, s, p - s)) {
n = s + len - p + 1;
if (n > (int) sizeof(protected_path) - 1)
n = sizeof(protected_path) - 1;
my_strlcpy(protected_path, p + 1, n);
if ((fp = fopen(protected_path, "r")) == NULL)
elog(E_LOG, c, "check_auth: cannot open %s: %s",
protected_path, strerror(errno));
break;
}
}
if (fp == NULL)
fp = open_auth_file(c->ctx, path);
if (fp != NULL) {
authorized = authorize(c, fp);
(void) fclose(fp);
}
return (authorized);
}
int
is_authorized_for_put(struct conn *c)
{
FILE *fp;
int ret = 0;
if ((fp = fopen(c->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {
ret = authorize(c, fp);
(void) fclose(fp);
}
return (ret);
}
void
send_authorization_request(struct conn *c)
{
char buf[512];
(void) my_snprintf(buf, sizeof(buf), "Unauthorized\r\n"
"WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
"nonce=\"%lu\"", c->ctx->options[OPT_AUTH_REALM],
(unsigned long) current_time);
send_server_error(c, 401, buf);
}
/*
* Edit the passwords file.
*/
int
edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass)
{
int ret = EXIT_SUCCESS, found = 0;
struct vec u, d, p;
char line[512], tmp[FILENAME_MAX], ha1[32];
FILE *fp = NULL, *fp2 = NULL;
(void) my_snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
/* Create the file if does not exist */
if ((fp = fopen(fname, "a+")))
(void) fclose(fp);
/* Open the given file and temporary file */
if ((fp = fopen(fname, "r")) == NULL)
elog(E_FATAL, 0, "Cannot open %s: %s", fname, strerror(errno));
else if ((fp2 = fopen(tmp, "w+")) == NULL)
elog(E_FATAL, 0, "Cannot open %s: %s", tmp, strerror(errno));
p.ptr = pass;
p.len = strlen(pass);
/* Copy the stuff to temporary file */
while (fgets(line, sizeof(line), fp) != NULL) {
u.ptr = line;
if ((d.ptr = strchr(line, ':')) == NULL)
continue;
u.len = d.ptr - u.ptr;
d.ptr++;
if (strchr(d.ptr, ':') == NULL)
continue;
d.len = strchr(d.ptr, ':') - d.ptr;
if ((int) strlen(user) == u.len &&
!memcmp(user, u.ptr, u.len) &&
(int) strlen(domain) == d.len &&
!memcmp(domain, d.ptr, d.len)) {
found++;
md5(ha1, &u, &d, &p, NULL);
(void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
} else {
(void) fprintf(fp2, "%s", line);
}
}
/* If new user, just add it */
if (found == 0) {
u.ptr = user; u.len = strlen(user);
d.ptr = domain; d.len = strlen(domain);
md5(ha1, &u, &d, &p, NULL);
(void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
}
/* Close files */
(void) fclose(fp);
(void) fclose(fp2);
/* Put the temp file in place of real file */
(void) my_remove(fname);
(void) my_rename(tmp, fname);
return (ret);
}
#endif /* NO_AUTH */

274
third-party/shttpd/cgi.c vendored Normal file
View File

@@ -0,0 +1,274 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
#if !defined(NO_CGI)
struct env_block {
char buf[ENV_MAX]; /* Environment buffer */
int len; /* Space taken */
char *vars[CGI_ENV_VARS]; /* Point into the buffer */
int nvars; /* Number of variables */
};
/*
* UNIX socketpair() implementation. Why? Because Windows does not have it.
* Return 0 on success, -1 on error.
*/
static int
my_socketpair(struct conn *c, int sp[2])
{
struct sockaddr_in sa;
int sock, ret = -1;
socklen_t len = sizeof(sa);
(void) memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(0);
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
} else if (bind(sock, (struct sockaddr *) &sa, len) != 0) {
elog(E_LOG, c, "mysocketpair: bind(): %d", ERRNO);
(void) closesocket(sock);
} else if (listen(sock, 1) != 0) {
elog(E_LOG, c, "mysocketpair: listen(): %d", ERRNO);
(void) closesocket(sock);
} else if (getsockname(sock, (struct sockaddr *) &sa, &len) != 0) {
elog(E_LOG, c, "mysocketpair: getsockname(): %d", ERRNO);
(void) closesocket(sock);
} else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
(void) closesocket(sock);
} else if (connect(sp[0], (struct sockaddr *) &sa, len) != 0) {
elog(E_LOG, c, "mysocketpair: connect(): %d", ERRNO);
(void) closesocket(sock);
(void) closesocket(sp[0]);
} else if ((sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) == -1) {
elog(E_LOG, c, "mysocketpair: accept(): %d", ERRNO);
(void) closesocket(sock);
(void) closesocket(sp[0]);
} else {
/* Success */
ret = 0;
(void) closesocket(sock);
}
#ifndef _WIN32
(void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
(void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
#endif /* _WIN32*/
return (ret);
}
static void
addenv(struct env_block *block, const char *fmt, ...)
{
int n, space;
va_list ap;
space = sizeof(block->buf) - block->len - 2;
assert(space >= 0);
va_start(ap, fmt);
n = vsnprintf(block->buf + block->len, space, fmt, ap);
va_end(ap);
if (n > 0 && n < space && block->nvars < CGI_ENV_VARS - 2) {
block->vars[block->nvars++] = block->buf + block->len;
block->len += n + 1; /* Include \0 terminator */
}
}
static void
add_http_headers_to_env(struct env_block *b, const char *s, int len)
{
const char *p, *v, *e = s + len;
int space, n, i, ch;
/* Loop through all headers in the request */
while (s < e) {
/* Find where this header ends. Remember where value starts */
for (p = s, v = NULL; p < e && *p != '\n'; p++)
if (v == NULL && *p == ':')
v = p;
/* 2 null terminators and "HTTP_" */
space = (sizeof(b->buf) - b->len) - (2 + 5);
assert(space >= 0);
/* Copy header if enough space in the environment block */
if (v > s && p > v + 2 && space > p - s) {
/* Store var */
if (b->nvars < (int) NELEMS(b->vars) - 1)
b->vars[b->nvars++] = b->buf + b->len;
(void) memcpy(b->buf + b->len, "HTTP_", 5);
b->len += 5;
/* Copy header name. Substitute '-' to '_' */
n = v - s;
for (i = 0; i < n; i++) {
ch = s[i] == '-' ? '_' : s[i];
b->buf[b->len++] = toupper(ch);
}
b->buf[b->len++] = '=';
/* Copy header value */
v += 2;
n = p[-1] == '\r' ? (p - v) - 1 : p - v;
for (i = 0; i < n; i++)
b->buf[b->len++] = v[i];
/* Null-terminate */
b->buf[b->len++] = '\0';
}
s = p + 1; /* Shift to the next header */
}
}
static void
prepare_environment(const struct conn *c, const char *prog,
struct env_block *blk)
{
const struct headers *h = &c->ch;
const char *s, *root = c->ctx->options[OPT_ROOT];
size_t len;
blk->len = blk->nvars = 0;
/* Prepare the environment block */
addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
addenv(blk, "%s", "REDIRECT_STATUS=200"); /* PHP */
addenv(blk, "SERVER_PORT=%d", c->loc_port);
addenv(blk, "SERVER_NAME=%s", c->ctx->options[OPT_AUTH_REALM]);
addenv(blk, "SERVER_ROOT=%s", root);
addenv(blk, "DOCUMENT_ROOT=%s", root);
addenv(blk, "REQUEST_METHOD=%s", known_http_methods[c->method].ptr);
addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(c->sa.u.sin.sin_addr));
addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
addenv(blk, "REQUEST_URI=%s", c->uri);
addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
addenv(blk, "SCRIPT_FILENAME=%s", prog); /* PHP */
addenv(blk, "PATH_TRANSLATED=%s", prog);
if (h->ct.v_vec.len > 0)
addenv(blk, "CONTENT_TYPE=%.*s",
h->ct.v_vec.len, h->ct.v_vec.ptr);
if (c->query != NULL)
addenv(blk, "QUERY_STRING=%s", c->query);
if (c->path_info != NULL)
addenv(blk, "PATH_INFO=/%s", c->path_info);
if (h->cl.v_big_int > 0)
addenv(blk, "CONTENT_LENGTH=%lu", h->cl.v_big_int);
if ((s = getenv("PATH")) != NULL)
addenv(blk, "PATH=%s", s);
#ifdef _WIN32
if ((s = getenv("COMSPEC")) != NULL)
addenv(blk, "COMSPEC=%s", s);
if ((s = getenv("SYSTEMROOT")) != NULL)
addenv(blk, "SYSTEMROOT=%s", s);
#else
if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
addenv(blk, "LD_LIBRARY_PATH=%s", s);
#endif /* _WIN32 */
if ((s = getenv("PERLLIB")) != NULL)
addenv(blk, "PERLLIB=%s", s);
if (h->user.v_vec.len > 0) {
addenv(blk, "REMOTE_USER=%.*s",
h->user.v_vec.len, h->user.v_vec.ptr);
addenv(blk, "%s", "AUTH_TYPE=Digest");
}
/* Add user-specified variables */
s = c->ctx->options[OPT_CGI_ENVIRONMENT];
FOR_EACH_WORD_IN_LIST(s, len)
addenv(blk, "%.*s", len, s);
/* Add all headers as HTTP_* variables */
add_http_headers_to_env(blk, c->headers,
c->rem.headers_len - (c->headers - c->request));
blk->vars[blk->nvars++] = NULL;
blk->buf[blk->len++] = '\0';
assert(blk->nvars < CGI_ENV_VARS);
assert(blk->len > 0);
assert(blk->len < (int) sizeof(blk->buf));
/* Debug stuff to view passed environment */
DBG(("%s: %d vars, %d env size", prog, blk->nvars, blk->len));
{
int i;
for (i = 0 ; i < blk->nvars; i++)
DBG(("[%s]", blk->vars[i] ? blk->vars[i] : "null"));
}
}
int
run_cgi(struct conn *c, const char *prog)
{
struct env_block blk;
char dir[FILENAME_MAX], *p;
int ret, pair[2];
prepare_environment(c, prog, &blk);
pair[0] = pair[1] = -1;
/* CGI must be executed in its own directory */
(void) my_snprintf(dir, sizeof(dir), "%s", prog);
for (p = dir + strlen(dir) - 1; p > dir; p--)
if (*p == '/') {
*p++ = '\0';
break;
}
if (my_socketpair(c, pair) != 0) {
ret = -1;
} else if (spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) {
ret = -1;
(void) closesocket(pair[0]);
(void) closesocket(pair[1]);
} else {
ret = 0;
c->loc.chan.sock = pair[0];
}
return (ret);
}
void
do_cgi(struct conn *c)
{
DBG(("running CGI: [%s]", c->uri));
assert(c->loc.io.size > CGI_REPLY_LEN);
memcpy(c->loc.io.buf, CGI_REPLY, CGI_REPLY_LEN);
c->loc.io.head = c->loc.io.tail = c->loc.io.total = CGI_REPLY_LEN;
c->loc.io_class = &io_cgi;
c->loc.flags = FLAG_R;
if (c->method == METHOD_POST)
c->loc.flags |= FLAG_W;
}
#endif /* !NO_CGI */

198
third-party/shttpd/compat_rtems.c vendored Normal file
View File

@@ -0,0 +1,198 @@
/**********************************************************************
*
* rtems shttpd management
*
* FILE NAME : rtems_shttpd.c
*
* AUTHOR : Steven Johnson
*
* DESCRIPTION : Defines the interface functions to the shttp daemon
*
* REVISION : $Id: compat_rtems.c,v 1.2 2006/11/12 03:29:17 infidel Exp $
*
* COMMENTS :
*
**********************************************************************/
/**********************************************************************
* INCLUDED MODULES
**********************************************************************/
#include <rtems.h>
#include "defs.h"
#define MAX_WEB_BASE_PATH_LENGTH 256
#define MIN_SHTTPD_STACK (8*1024)
typedef struct RTEMS_HTTPD_ARGS {
rtems_shttpd_init init_callback;
rtems_shttpd_addpages addpages_callback;
char webroot[MAX_WEB_BASE_PATH_LENGTH];
} RTEMS_HTTPD_ARGS;
static int rtems_webserver_running = FALSE; //not running.
static rtems_task rtems_httpd_daemon(rtems_task_argument args )
{
RTEMS_HTTPD_ARGS *httpd_args = (RTEMS_HTTPD_ARGS*)args;
struct shttpd_ctx *ctx;
if (httpd_args != NULL)
if (httpd_args->init_callback != NULL)
httpd_args->init_callback();
/**************************************
* Initialize the web server
*/
/*
* Initialize SHTTPD context.
* Set WWW root to current WEB_ROOT_PATH.
*/
ctx = shttpd_init(NULL, "document_root", httpd_args->webroot, NULL);
if (httpd_args != NULL)
if (httpd_args->addpages_callback != NULL)
httpd_args->addpages_callback(ctx);
/* Finished with args, so free them */
if (httpd_args != NULL)
free(httpd_args);
/* Open listening socket */
shttpd_listen(ctx, 9000);
rtems_webserver_running = TRUE;
/* Serve connections infinitely until someone kills us */
while (rtems_webserver_running)
shttpd_poll(ctx, 1000);
/* Unreached, because we will be killed by a signal */
shttpd_fini(ctx);
rtems_task_delete( RTEMS_SELF );
}
rtems_status_code rtems_initialize_webserver(rtems_task_priority initial_priority,
rtems_unsigned32 stack_size,
rtems_mode initial_modes,
rtems_attribute attribute_set,
rtems_shttpd_init init_callback,
rtems_shttpd_addpages addpages_callback,
char *webroot
)
{
rtems_status_code sc;
rtems_id tid;
RTEMS_HTTPD_ARGS *args;
if (stack_size < MIN_SHTTPD_STACK)
stack_size = MIN_SHTTPD_STACK;
args = malloc(sizeof(RTEMS_HTTPD_ARGS));
if (args != NULL)
{
args->init_callback = init_callback;
args->addpages_callback = addpages_callback;
strncpy(args->webroot,webroot,MAX_WEB_BASE_PATH_LENGTH);
sc = rtems_task_create(rtems_build_name('H', 'T', 'P', 'D'),
initial_priority,
stack_size,
initial_modes,
attribute_set,
&tid);
if (sc == RTEMS_SUCCESSFUL)
{
sc = rtems_task_start(tid, rtems_httpd_daemon, (rtems_task_argument)args);
}
}
else
{
sc = RTEMS_NO_MEMORY;
}
return sc;
}
void rtems_terminate_webserver(void)
{
rtems_webserver_running = FALSE; //not running, so terminate
}
int rtems_webserver_ok(void)
{
return rtems_webserver_running;
}
void
set_close_on_exec(int fd)
{
// RTEMS Does not have a functional "execve"
// so technically this call does not do anything,
// but it doesnt hurt either.
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
}
int
my_stat(const char *path, struct stat *stp)
{
return (stat(path, stp));
}
int
my_open(const char *path, int flags, int mode)
{
return (open(path, flags, mode));
}
int
my_remove(const char *path)
{
return (remove(path));
}
int
my_rename(const char *path1, const char *path2)
{
return (rename(path1, path2));
}
int
my_mkdir(const char *path, int mode)
{
return (mkdir(path, mode));
}
char *
my_getcwd(char *buffer, int maxlen)
{
return (getcwd(buffer, maxlen));
}
int
set_non_blocking_mode(int fd)
{
int ret = -1;
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
DBG(("nonblock: fcntl(F_GETFL): %d", ERRNO));
} else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
DBG(("nonblock: fcntl(F_SETFL): %d", ERRNO));
} else {
ret = 0; /* Success */
}
return (ret);
}
#if !defined(NO_CGI)
int
spawn_process(struct conn *c, const char *prog, char *envblk, char **envp)
{
return (-1); // RTEMS does not have subprocess support as standard.
}
#endif

60
third-party/shttpd/compat_rtems.h vendored Normal file
View File

@@ -0,0 +1,60 @@
/**
* @file rtems/rtems-shttpd.h
*/
#ifndef _rtems_rtems_webserver_h
#define _rtems_rtems_webserver_h
#include "shttpd.h"
#include <rtems.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dirent.h>
/* RTEMS is an Real Time Embedded operating system, for operation in hardware.
It does not have SSL or CGI support, as it does not have dynamic library
loading or sub-processes. */
#define EMBEDDED
#define NO_SSL
#define NO_CGI
#define DIRSEP '/'
#define O_BINARY 0
#define ERRNO errno
/* RTEMS version is Thread Safe */
#define InitializeCriticalSection(x) rtems_semaphore_create( \
rtems_build_name('H','T','P','X'), \
1, /* Not Held Yet.*/ \
RTEMS_FIFO | \
RTEMS_BINARY_SEMAPHORE, \
0, \
x);
#define EnterCriticalSection(x) rtems_semaphore_obtain(*(x),RTEMS_WAIT,RTEMS_NO_TIMEOUT)
#define LeaveCriticalSection(x) rtems_semaphore_release(*(x))
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*rtems_shttpd_addpages)(struct shttpd_ctx *ctx);
typedef void (*rtems_shttpd_init)(void);
rtems_status_code rtems_initialize_webserver(rtems_task_priority initial_priority,
rtems_unsigned32 stack_size,
rtems_mode initial_modes,
rtems_attribute attribute_set,
rtems_shttpd_init init_callback,
rtems_shttpd_addpages addpages_callback,
char *webroot
);
void rtems_terminate_webserver(void);
int rtems_webserver_ok(void);
#ifdef __cplusplus
}
#endif
#endif

128
third-party/shttpd/compat_unix.c vendored Normal file
View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
void
set_close_on_exec(int fd)
{
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
}
int
my_stat(const char *path, struct stat *stp)
{
return (stat(path, stp));
}
int
my_open(const char *path, int flags, int mode)
{
return (open(path, flags, mode));
}
int
my_remove(const char *path)
{
return (remove(path));
}
int
my_rename(const char *path1, const char *path2)
{
return (rename(path1, path2));
}
int
my_mkdir(const char *path, int mode)
{
return (mkdir(path, mode));
}
char *
my_getcwd(char *buffer, int maxlen)
{
return (getcwd(buffer, maxlen));
}
int
set_non_blocking_mode(int fd)
{
int ret = -1;
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
DBG(("nonblock: fcntl(F_GETFL): %d", ERRNO));
} else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
DBG(("nonblock: fcntl(F_SETFL): %d", ERRNO));
} else {
ret = 0; /* Success */
}
return (ret);
}
#ifndef NO_CGI
int
spawn_process(struct conn *c, const char *prog, char *envblk,
char *envp[], int sock, const char *dir)
{
int ret;
pid_t pid;
const char *p, *interp = c->ctx->options[OPT_CGI_INTERPRETER];
envblk = NULL; /* unused */
if ((pid = vfork()) == -1) {
ret = -1;
elog(E_LOG, c, "redirect: fork: %s", strerror(errno));
} else if (pid == 0) {
/* Child */
(void) chdir(dir);
(void) dup2(sock, 0);
(void) dup2(sock, 1);
(void) closesocket(sock);
/* If error file is specified, send errors there */
if (c->ctx->error_log)
(void) dup2(fileno(c->ctx->error_log), 2);
if ((p = strrchr(prog, '/')) != NULL)
p++;
else
p = prog;
/* Execute CGI program */
if (interp == NULL) {
(void) execle(p, p, NULL, envp);
elog(E_FATAL, c, "redirect: exec(%s)", prog);
} else {
(void) execle(interp, interp, p, NULL, envp);
elog(E_FATAL, c, "redirect: exec(%s %s)",
interp, prog);
}
/* UNREACHED */
exit(EXIT_FAILURE);
} else {
/* Parent */
ret = 0;
(void) closesocket(sock);
}
return (ret);
}
#endif /* !NO_CGI */

33
third-party/shttpd/compat_unix.h vendored Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2004-2007 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/mman.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <pwd.h>
#include <unistd.h>
#include <dirent.h>
#include <dlfcn.h>
#define SSL_LIB "libssl.so"
#define DIRSEP '/'
#define IS_DIRSEP_CHAR(c) ((c) == '/')
#define O_BINARY 0
#define closesocket(a) close(a)
#define ERRNO errno
#define NO_GUI
#define InitializeCriticalSection(x) /* FIXME UNIX version is not MT safe */
#define EnterCriticalSection(x)
#define LeaveCriticalSection(x)

442
third-party/shttpd/compat_win32.c vendored Normal file
View File

@@ -0,0 +1,442 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static void
fix_directory_separators(char *path)
{
for (; *path != '\0'; path++) {
if (*path == '/')
*path = '\\';
if (*path == '\\')
while (path[1] == '\\' || path[1] == '/')
(void) memmove(path + 1,
path + 2, strlen(path + 2) + 1);
}
}
static int
protect_against_code_disclosure(const char *path)
{
WIN32_FIND_DATA data;
HANDLE handle;
const char *p;
/*
* Protect against CGI code disclosure under Windows.
* This is very nasty hole. Windows happily opens files with
* some garbage in the end of file name. So fopen("a.cgi ", "r")
* actually opens "a.cgi", and does not return an error! And since
* "a.cgi " does not have valid CGI extension, this leads to
* the CGI code disclosure.
* To protect, here we delete all fishy characters from the
* end of file name.
*/
if ((handle = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE)
return (FALSE);
FindClose(handle);
for (p = path + strlen(path); p > path && p[-1] != '\\';)
p--;
if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
strcmp(data.cFileName, p) != 0)
return (FALSE);
return (TRUE);
}
int
my_open(const char *path, int flags, int mode)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
my_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
if (protect_against_code_disclosure(buf) == FALSE)
return (-1);
return (_wopen(wbuf, flags));
}
int
my_stat(const char *path, struct stat *stp)
{
char buf[FILENAME_MAX], *p;
wchar_t wbuf[FILENAME_MAX];
my_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
p = buf + strlen(buf) - 1;
while (p > buf && *p == '\\' && p[-1] != ':')
*p-- = '\0';
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
return (_wstat(wbuf, (struct _stat *) stp));
}
int
my_remove(const char *path)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
my_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
return (_wremove(wbuf));
}
int
my_rename(const char *path1, const char *path2)
{
char buf1[FILENAME_MAX];
char buf2[FILENAME_MAX];
wchar_t wbuf1[FILENAME_MAX];
wchar_t wbuf2[FILENAME_MAX];
my_strlcpy(buf1, path1, sizeof(buf1));
my_strlcpy(buf2, path2, sizeof(buf2));
fix_directory_separators(buf1);
fix_directory_separators(buf2);
MultiByteToWideChar(CP_UTF8, 0, buf1, -1, wbuf1, sizeof(wbuf1));
MultiByteToWideChar(CP_UTF8, 0, buf2, -1, wbuf2, sizeof(wbuf2));
return (_wrename(wbuf1, wbuf2));
}
int
my_mkdir(const char *path, int mode)
{
char buf[FILENAME_MAX];
wchar_t wbuf[FILENAME_MAX];
my_strlcpy(buf, path, sizeof(buf));
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
return (_wmkdir(wbuf));
}
static char *
wide_to_utf8(const wchar_t *str)
{
char *buf = NULL;
if (str) {
int nchar = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
if (nchar > 0) {
buf = malloc(nchar);
if (!buf)
errno = ENOMEM;
else if (!WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, nchar, NULL, NULL)) {
free(buf);
buf = NULL;
errno = EINVAL;
}
} else
errno = EINVAL;
} else
errno = EINVAL;
return buf;
}
char *
my_getcwd(char *buffer, int maxlen)
{
char *result = NULL;
wchar_t *wbuffer, *wresult;
if (buffer) {
/* User-supplied buffer */
wbuffer = malloc(maxlen * sizeof(wchar_t));
if (wbuffer == NULL)
return NULL;
} else
/* Dynamically allocated buffer */
wbuffer = NULL;
wresult = _wgetcwd(wbuffer, maxlen);
if (wresult) {
int err = errno;
if (buffer) {
/* User-supplied buffer */
int n = WideCharToMultiByte(CP_UTF8, 0, wresult, -1, buffer, maxlen, NULL, NULL);
if (n == 0)
err = ERANGE;
free(wbuffer);
result = buffer;
} else {
/* Buffer allocated by _wgetcwd() */
result = wide_to_utf8(wresult);
err = errno;
free(wresult);
}
errno = err;
}
return result;
}
DIR *
opendir(const char *name)
{
DIR *dir = NULL;
char path[FILENAME_MAX];
wchar_t wpath[FILENAME_MAX];
if (name == NULL || name[0] == '\0') {
errno = EINVAL;
} else if ((dir = malloc(sizeof(*dir))) == NULL) {
errno = ENOMEM;
} else {
my_snprintf(path, sizeof(path), "%s/*", name);
fix_directory_separators(path);
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath));
dir->handle = FindFirstFileW(wpath, &dir->info);
if (dir->handle != INVALID_HANDLE_VALUE) {
dir->result.d_name[0] = '\0';
} else {
free(dir);
dir = NULL;
}
}
return (dir);
}
int
closedir(DIR *dir)
{
int result = -1;
if (dir != NULL) {
if (dir->handle != INVALID_HANDLE_VALUE)
result = FindClose(dir->handle) ? 0 : -1;
free(dir);
}
if (result == -1)
errno = EBADF;
return (result);
}
struct dirent *
readdir(DIR *dir)
{
struct dirent *result = 0;
if (dir && dir->handle != INVALID_HANDLE_VALUE) {
if(!dir->result.d_name ||
FindNextFileW(dir->handle, &dir->info)) {
result = &dir->result;
WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,
-1, result->d_name,
sizeof(result->d_name), NULL, NULL);
}
} else {
errno = EBADF;
}
return (result);
}
int
set_non_blocking_mode(int fd)
{
unsigned long on = 1;
return (ioctlsocket(fd, FIONBIO, &on));
}
void
set_close_on_exec(int fd)
{
fd = 0; /* Do nothing. There is no FD_CLOEXEC on Windows */
}
#if !defined(NO_CGI)
struct threadparam {
SOCKET s;
HANDLE hPipe;
big_int_t content_len;
};
/*
* Thread function that reads POST data from the socket pair
* and writes it to the CGI process.
*/
static void//DWORD WINAPI
stdoutput(void *arg)
{
struct threadparam *tp = arg;
int n, sent, stop = 0;
big_int_t total = 0;
DWORD k;
char buf[BUFSIZ];
size_t max_recv;
max_recv = min(sizeof(buf), tp->content_len - total);
while (!stop && max_recv > 0 && (n = recv(tp->s, buf, max_recv, 0)) > 0) {
for (sent = 0; !stop && sent < n; sent += k)
if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
stop++;
total += n;
max_recv = min(sizeof(buf), tp->content_len - total);
}
CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */
free(tp);
}
/*
* Thread function that reads CGI output and pushes it to the socket pair.
*/
static void
stdinput(void *arg)
{
struct threadparam *tp = arg;
static int ntotal;
int k, stop = 0;
DWORD n, sent;
char buf[BUFSIZ];
while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
ntotal += n;
for (sent = 0; !stop && sent < n; sent += k)
if ((k = send(tp->s, buf + sent, n - sent, 0)) <= 0)
stop++;
}
CloseHandle(tp->hPipe);
/*
* Windows is a piece of crap. When this thread closes its end
* of the socket pair, the other end (get_cgi() function) may loose
* some data. I presume, this happens if get_cgi() is not fast enough,
* and the data written by this end does not "push-ed" to the other
* end socket buffer. So after closesocket() the remaining data is
* gone. If I put shutdown() before closesocket(), that seems to
* fix the problem, but I am not sure this is the right fix.
* XXX (submitted by James Marshall) we do not do shutdown() on UNIX.
* If fork() is called from user callback, shutdown() messes up things.
*/
shutdown(tp->s, 2);
closesocket(tp->s);
free(tp);
_endthread();
}
static void
spawn_stdio_thread(int sock, HANDLE hPipe, void (*func)(void *),
big_int_t content_len)
{
struct threadparam *tp;
DWORD tid;
tp = malloc(sizeof(*tp));
assert(tp != NULL);
tp->s = sock;
tp->hPipe = hPipe;
tp->content_len = content_len;
_beginthread(func, 0, tp);
}
int
spawn_process(struct conn *c, const char *prog, char *envblk,
char *envp[], int sock, const char *dir)
{
HANDLE a[2], b[2], h[2], me;
DWORD flags;
char *p, cmdline[FILENAME_MAX], line[FILENAME_MAX];
FILE *fp;
STARTUPINFOA si;
PROCESS_INFORMATION pi;
me = GetCurrentProcess();
flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
/* FIXME add error checking code here */
CreatePipe(&a[0], &a[1], NULL, 0);
CreatePipe(&b[0], &b[1], NULL, 0);
DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags);
DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags);
(void) memset(&si, 0, sizeof(si));
(void) memset(&pi, 0, sizeof(pi));
/* XXX redirect CGI errors to the error log file */
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = si.hStdError = h[1];
si.hStdInput = h[0];
/* If CGI file is a script, try to read the interpreter line */
if (c->ctx->options[OPT_CGI_INTERPRETER] == NULL) {
if ((fp = fopen(prog, "r")) != NULL) {
(void) fgets(line, sizeof(line), fp);
if (memcmp(line, "#!", 2) != 0)
line[2] = '\0';
/* Trim whitespaces from interpreter name */
for (p = &line[strlen(line) - 1]; p > line &&
isspace(*p); p--)
*p = '\0';
(void) fclose(fp);
}
(void) my_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
line + 2, line[2] == '\0' ? "" : " ", prog);
} else {
(void) my_snprintf(cmdline, sizeof(cmdline), "%s %s",
c->ctx->options[OPT_CGI_INTERPRETER], prog);
}
(void) my_snprintf(line, sizeof(line), "%s", dir);
fix_directory_separators(line);
fix_directory_separators(cmdline);
/*
* Spawn reader & writer threads before we create CGI process.
* Otherwise CGI process may die too quickly, loosing the data
*/
spawn_stdio_thread(sock, b[0], stdinput, 0);
spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len);
if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
elog(E_LOG, c,"redirect: CreateProcess(%s): %d",cmdline,ERRNO);
return (-1);
} else {
CloseHandle(h[0]);
CloseHandle(h[1]);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
return (0);
}
#endif /* !NO_CGI */

83
third-party/shttpd/compat_win32.h vendored Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2004-2007 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
/* Tip from Justin Maximilian, suppress errors from winsock2.h */
#define _WINSOCKAPI_
#include <windows.h>
#include <winsock2.h>
#include <commctrl.h>
#include <winnls.h>
#include <shlobj.h>
#include <shellapi.h>
#ifndef _WIN32_WCE
#include <process.h>
#include <direct.h>
#include <io.h>
#else /* _WIN32_WCE */
/* Windows CE-specific definitions */
#define NO_CGI /* WinCE has no pipes */
#define NO_GUI /* temporarily until it is fixed */
#pragma comment(lib,"ws2")
/* WinCE has both Unicode and ANSI versions of GetProcAddress */
#undef GetProcAddress
#define GetProcAddress GetProcAddressA
#include "compat_wince.h"
#endif /* _WIN32_WCE */
#define ERRNO GetLastError()
#define NO_SOCKLEN_T
#define SSL_LIB L"libssl32.dll"
#define DIRSEP '\\'
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
#define O_NONBLOCK 0
#define EWOULDBLOCK WSAEWOULDBLOCK
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define mkdir(x,y) _mkdir(x)
#define popen(x,y) _popen(x, y)
#define pclose(x) _pclose(x)
#define dlopen(x,y) LoadLibraryW(x)
#define dlsym(x,y) (void *) GetProcAddress(x,y)
#define _POSIX_
#ifdef __LCC__
#include <stdint.h>
#endif /* __LCC__ */
#ifdef _MSC_VER /* MinGW already has these */
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef __int64 uint64_t;
#define S_ISDIR(x) ((x) & _S_IFDIR)
#endif /* _MSC_VER */
/*
* POSIX dirent interface
*/
struct dirent {
char d_name[FILENAME_MAX];
};
typedef struct DIR {
HANDLE handle;
WIN32_FIND_DATAW info;
struct dirent result;
char *name;
} DIR;
extern DIR *opendir(const char *name);
extern int closedir(DIR *dir);
extern struct dirent *readdir(DIR *dir);

1593
third-party/shttpd/compat_wince.c vendored Normal file

File diff suppressed because it is too large Load Diff

145
third-party/shttpd/compat_wince.h vendored Normal file
View File

@@ -0,0 +1,145 @@
#ifndef INCLUDE_WINCE_COMPAT_H
#define INCLUDE_WINCE_COMPAT_H
#ifdef __cplusplus
extern "C" {
#endif
/*** ANSI C library ***/
/* Missing ANSI C definitions */
#define BUFSIZ 4096
#define ENOMEM ERROR_NOT_ENOUGH_MEMORY
#define EBADF ERROR_INVALID_HANDLE
#define EINVAL ERROR_INVALID_PARAMETER
#define ENOENT ERROR_FILE_NOT_FOUND
#define ERANGE ERROR_INSUFFICIENT_BUFFER
#define EINTR WSAEINTR
/*
* Because we need a per-thread errno, we define a function
* pointer that we can call to return a pointer to the errno
* for the current thread. Then we define a macro for errno
* that dereferences this function's result.
*
* This makes it syntactically just like the "real" errno.
*
* Using a function pointer allows us to use a very fast
* function when there are no threads running and a slower
* function when there are multiple threads running.
*/
void __WinCE_Errno_New_Thread(int *Errno_Pointer);
void __WinCE_Errno_Thread_Exit(void);
extern int *(*__WinCE_Errno_Pointer_Function)(void);
#define errno (*(*__WinCE_Errno_Pointer_Function)())
char *strerror(int errnum);
struct tm {
int tm_sec; /* seconds after the minute - [0,59] */
int tm_min; /* minutes after the hour - [0,59] */
int tm_hour; /* hours since midnight - [0,23] */
int tm_mday; /* day of the month - [1,31] */
int tm_mon; /* months since January - [0,11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday - [0,6] */
int tm_yday; /* days since January 1 - [0,365] */
int tm_isdst; /* daylight savings time flag */
};
struct tm *gmtime(const time_t *TimeP); /* for future use */
struct tm *localtime(const time_t *TimeP);
time_t mktime(struct tm *tm);
time_t time(time_t *TimeP);
size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *tim_p);
int _wrename(const wchar_t *oldname, const wchar_t *newname);
int _wremove(const wchar_t *filename);
/* Environment variables are not supported */
#define getenv(x) (NULL)
/* Redefine fileno so that it returns an integer */
#undef fileno
#define fileno(f) (int)_fileno(f)
/* Signals are not supported */
#define signal(num, handler) (0)
#define SIGTERM 0
#define SIGINT 0
/*** POSIX API ***/
/* Missing POSIX definitions */
#define FILENAME_MAX MAX_PATH
struct _stat {
unsigned long st_size;
unsigned long st_ino;
int st_mode;
unsigned long st_atime;
unsigned long st_mtime;
unsigned long st_ctime;
unsigned short st_dev;
unsigned short st_nlink;
unsigned short st_uid;
unsigned short st_gid;
};
#define S_IFMT 0170000
#define S_IFDIR 0040000
#define S_IFREG 0100000
#define S_IEXEC 0000100
#define S_IWRITE 0000200
#define S_IREAD 0000400
#define _S_IFDIR S_IFDIR /* MSVCRT compatibilit */
int _fstat(int handle, struct _stat *buffer);
int _wstat(const wchar_t *path, struct _stat *buffer);
#define stat _stat /* NOTE: applies to _stat() and also struct _stat */
#define fstat _fstat
#define O_RDWR (1<<0)
#define O_RDONLY (2<<0)
#define O_WRONLY (3<<0)
#define O_MODE_MASK (3<<0)
#define O_TRUNC (1<<2)
#define O_EXCL (1<<3)
#define O_CREAT (1<<4)
#define O_BINARY 0
int _wopen(const wchar_t *filename, int oflag, ...);
int _close(int handle);
int _write(int handle, const void *buffer, unsigned int count);
int _read(int handle, void *buffer, unsigned int count);
long _lseek(int handle, long offset, int origin);
#define close _close
#define write _write
#define read _read
#define lseek _lseek
/* WinCE has only a Unicode version of this function */
FILE *fdopen(int handle, const char *mode);
int _wmkdir(const wchar_t *dirname);
/* WinCE has no concept of current directory so we return a constant path */
wchar_t *_wgetcwd(wchar_t *buffer, int maxlen);
#define freopen(path, mode, stream) assert(0)
#ifdef __cplusplus
}
#endif
#endif /* INCLUDE_WINCE_COMPAT_H */

307
third-party/shttpd/config.c vendored Normal file
View File

@@ -0,0 +1,307 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int isbyte(int n) { return (n >= 0 && n <= 255); }
static void
set_acl(struct shttpd_ctx *ctx, const char *s)
{
struct acl *acl = NULL;
char flag;
int len, a, b, c, d, n, mask;
struct llhead *lp, *tmp;
/* Delete the old ACLs if any */
LL_FOREACH_SAFE(&ctx->acl, lp, tmp)
free(LL_ENTRY(lp, struct acl, link));
FOR_EACH_WORD_IN_LIST(s, len) {
mask = 32;
if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
elog(E_FATAL, NULL, "[%s]: subnet must be "
"[+|-]x.x.x.x[/x]", s);
} else if (flag != '+' && flag != '-') {
elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
} else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
elog(E_FATAL, NULL, "bad ip address: [%s]", s);
} else if ((acl = malloc(sizeof(*acl))) == NULL) {
elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
} else if (sscanf(s + n, "/%d", &mask) == 0) {
/* Do nothing, no mask specified */
} else if (mask < 0 || mask > 32) {
elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
}
acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
acl->flag = flag;
LL_TAIL(&ctx->acl, &acl->link);
}
}
#ifndef NO_SSL
/*
* Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
*/
static void
set_ssl(struct shttpd_ctx *ctx, const char *pem)
{
SSL_CTX *CTX;
void *lib;
struct ssl_func *fp;
/* Load SSL library dynamically */
if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL)
elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB);
for (fp = ssl_sw; fp->name != NULL; fp++)
if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL)
elog(E_FATAL, NULL,"set_ssl: cannot find %s", fp->name);
/* Initialize SSL crap */
SSL_library_init();
if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
elog(E_FATAL, NULL, "SSL_CTX_new error");
else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
elog(E_FATAL, NULL, "cannot open %s", pem);
else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
elog(E_FATAL, NULL, "cannot open %s", pem);
ctx->ssl_ctx = CTX;
}
#endif /* NO_SSL */
static void
open_log_file(FILE **fpp, const char *path)
{
if (*fpp != NULL)
(void) fclose(*fpp);
if (path == NULL) {
*fpp = NULL;
} else if ((*fpp = fopen(path, "a")) == NULL) {
elog(E_FATAL, NULL, "cannot open log file %s: %s",
path, strerror(errno));
}
}
static void
set_alog(struct shttpd_ctx *ctx, const char *path)
{
open_log_file(&ctx->access_log, path);
}
static void
set_elog(struct shttpd_ctx *ctx, const char *path)
{
open_log_file(&ctx->error_log, path);
}
static void show_cfg_page(struct shttpd_arg *arg);
static void
set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
{
free_list(&ctx->registered_uris, &registered_uri_destructor);
if (uri != NULL) {
shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
}
}
static void
set_ports(struct shttpd_ctx *ctx, const char *p)
{
int len, is_ssl;
free_list(&ctx->listeners, &listener_destructor);
FOR_EACH_WORD_IN_LIST(p, len) {
is_ssl = p[len - 1] == 's' ? 1 : 0;
if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
elog(E_FATAL, NULL,
"Cannot open socket on port %d", atoi(p));
}
}
static const struct opt {
int index; /* Index in shttpd_ctx */
const char *name; /* Option name in config file */
const char *description; /* Description */
const char *default_value; /* Default option value */
void (*setter)(struct shttpd_ctx *, const char *);
} known_options[] = {
{OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
{OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
{OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
{OPT_DIR_LIST, "dir_list", "Directory listing", "1", NULL},
{OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
{OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
#ifndef NO_CGI
{OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
{OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
{OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
#endif /* NO_CGI */
{OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
#ifndef NO_AUTH
{OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
{OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
{OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
#endif /* !NO_AUTH */
{OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
{OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
{OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
#ifndef NO_SSL
{OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
#endif /* NO_SSL */
{OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
{OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
#ifdef _WIN32
#else
{OPT_INETD, "inetd", "Inetd mode", "0", NULL},
{OPT_UID, "uid", "\tRun as user", NULL, NULL},
#endif /* _WIN32 */
{-1, NULL, NULL, NULL, NULL}
};
void shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
{
const struct opt *o;
for (o = known_options; o->name != NULL; o++)
if (!strcmp(opt, o->name))
break;
if (o->name == NULL)
elog(E_FATAL, NULL, "no such option: [%s]", opt);
/* Call option setter first, so it can use both new and old values */
if (o->setter != NULL)
o->setter(ctx, val);
/* Free old value if any */
if (ctx->options[o->index] != NULL)
free(ctx->options[o->index]);
/* Set new option value */
ctx->options[o->index] = val ? my_strdup(val) : NULL;
}
static void
show_cfg_page(struct shttpd_arg *arg)
{
struct shttpd_ctx *ctx = arg->user_data;
char opt_name[20], value[BUFSIZ];
const struct opt *o;
opt_name[0] = value[0] = '\0';
if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
if (arg->flags & SHTTPD_MORE_POST_DATA)
return;
(void) shttpd_get_var("o", arg->in.buf, arg->in.len,
opt_name, sizeof(opt_name));
(void) shttpd_get_var("v", arg->in.buf, arg->in.len,
value, sizeof(value));
shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
}
shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
"<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
shttpd_printf(arg, "%s", "<table border=1"
"<tr><th>Option</th><th>Description</th>"
"<th colspan=2>Value</th></tr>");
if (opt_name[0] != '\0' && value[0] != '\0')
shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
opt_name, value[0] ? value : "NULL");
for (o = known_options; o->name != NULL; o++) {
shttpd_printf(arg,
"<form method=post><tr><td>%s</td><td>%s</td>"
"<input type=hidden name=o value='%s'>"
"<td><input type=text name=v value='%s'></td>"
"<td><input type=submit value=save></td></form></tr>",
o->name, o->description, o->name,
ctx->options[o->index] ? ctx->options[o->index] : "");
}
shttpd_printf(arg, "%s", "</table></body></html>");
arg->flags |= SHTTPD_END_OF_OUTPUT;
}
/*
* Show usage string and exit.
*/
void
usage(const char *prog)
{
const struct opt *o;
(void) fprintf(stderr,
"SHTTPD version %s (c) Sergey Lyubka\n"
"usage: %s [options] [config_file]\n", VERSION, prog);
#if !defined(NO_AUTH)
fprintf(stderr, " -A <htpasswd_file> <realm> <user> <passwd>\n");
#endif /* NO_AUTH */
for (o = known_options; o->name != NULL; o++) {
(void) fprintf(stderr, " -%s\t%s", o->name, o->description);
if (o->default_value != NULL)
fprintf(stderr, " (default: %s)", o->default_value);
fputc('\n', stderr);
}
exit(EXIT_FAILURE);
}
struct shttpd_ctx *shttpd_init(void)
{
struct shttpd_ctx *ctx;
struct tm *tm;
const struct opt *o;
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
elog(E_FATAL, NULL, "cannot allocate shttpd context");
/* Set default values */
for (o = known_options; o->name != NULL; o++) {
ctx->options[o->index] = o->default_value == NULL ?
NULL : my_strdup(o->default_value);
}
current_time = ctx->start_time = time(NULL);
tm = localtime(&current_time);
tz_offset = 0;
#if 0
tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
#endif
InitializeCriticalSection(&ctx->mutex);
LL_INIT(&ctx->connections);
LL_INIT(&ctx->registered_uris);
LL_INIT(&ctx->error_handlers);
LL_INIT(&ctx->acl);
LL_INIT(&ctx->ssi_funcs);
LL_INIT(&ctx->listeners);
#ifdef _WIN32
{WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
#endif /* _WIN32 */
return (ctx);
}

28
third-party/shttpd/config.h vendored Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef CONFIG_HEADER_DEFINED
#define CONFIG_HEADER_DEFINED
#define VERSION "1.39" /* Version */
#define CONFIG_FILE "shttpd.conf" /* Configuration file */
#define HTPASSWD ".htpasswd" /* Passwords file name */
#define URI_MAX 16384 /* Default max request size */
#define LISTENING_PORTS "80" /* Default listening ports */
#define INDEX_FILES "index.html,index.htm,index.php,index.cgi"
#define CGI_EXT "cgi,pl,php" /* Default CGI extensions */
#define SSI_EXT "shtml,shtm" /* Default SSI extensions */
#define REALM "mydomain.com" /* Default authentication realm */
#define DELIM_CHARS " ," /* Separators for lists */
#define EXPIRE_TIME 3600 /* Expiration time, seconds */
#define ENV_MAX 4096 /* Size of environment block */
#define CGI_ENV_VARS 64 /* Maximum vars passed to CGI */
#endif /* CONFIG_HEADER_DEFINED */

382
third-party/shttpd/defs.h vendored Normal file
View File

@@ -0,0 +1,382 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef DEFS_HEADER_DEFINED
#define DEFS_HEADER_DEFINED
#include "std_includes.h"
#include "llist.h"
#include "io.h"
#include "shttpd.h"
#include "md5.h"
#include "config.h"
#define NELEMS(ar) (sizeof(ar) / sizeof(ar[0]))
#ifdef _DEBUG
#define DBG(x) do { printf x ; putchar('\n'); fflush(stdout); } while (0)
#else
#define DBG(x)
#endif /* DEBUG */
#ifdef EMBEDDED
#include "shttpd.h"
#endif /* EMBEDDED */
/*
* Darwin prior to 7.0 and Win32 do not have socklen_t
*/
#ifdef NO_SOCKLEN_T
typedef int socklen_t;
#endif /* NO_SOCKLEN_T */
/*
* For parsing. This guy represents a substring.
*/
struct vec {
const char *ptr;
int len;
};
#if !defined(_WIN32)
enum {FALSE, TRUE};
#endif /* _WIN32 */
enum {METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD};
enum {HDR_DATE, HDR_INT, HDR_STRING}; /* HTTP header types */
enum {E_FATAL = 1, E_LOG = 2}; /* Flags for elog() function */
typedef unsigned long big_int_t; /* Type for Content-Length */
/*
* Unified socket address
*/
struct usa {
socklen_t len;
union {
struct sockaddr sa;
struct sockaddr_in sin;
} u;
};
/*
* This thing is aimed to hold values of any type.
* Used to store parsed headers' values.
*/
union variant {
char *v_str;
int v_int;
big_int_t v_big_int;
time_t v_time;
void (*v_func)(void);
void *v_void;
struct vec v_vec;
};
/*
* This is used only in embedded configuration. This structure holds a
* registered URI, associated callback function with callback data.
* For non-embedded compilation shttpd_callback_t is not defined, so
* we use union variant to keep the compiler silent.
*/
struct registered_uri {
struct llhead link;
const char *uri;
union variant callback;
void *callback_data;
};
/*
* User may want to handle certain errors. This structure holds the
* handlers for corresponding error codes.
*/
struct error_handler {
struct llhead link;
int code;
union variant callback;
void *callback_data;
};
struct http_header {
int len; /* Header name length */
int type; /* Header type */
size_t offset; /* Value placeholder */
const char *name; /* Header name */
};
/*
* This guy holds parsed HTTP headers
*/
struct headers {
union variant cl; /* Content-Length: */
union variant ct; /* Content-Type: */
union variant connection; /* Connection: */
union variant ims; /* If-Modified-Since: */
union variant user; /* Remote user name */
union variant auth; /* Authorization */
union variant useragent; /* User-Agent: */
union variant referer; /* Referer: */
union variant cookie; /* Cookie: */
union variant location; /* Location: */
union variant range; /* Range: */
union variant status; /* Status: */
union variant transenc; /* Transfer-Encoding: */
};
/* Must go after union variant definition */
#include "ssl.h"
/*
* The communication channel
*/
union channel {
int fd; /* Regular static file */
int sock; /* Connected socket */
struct {
int sock; /* XXX important. must be first */
SSL *ssl; /* shttpd_poll() assumes that */
} ssl; /* SSL-ed socket */
struct {
DIR *dirp;
char *path;
} dir; /* Opened directory */
struct {
void *state; /* For keeping state */
union variant func; /* User callback function */
void *data; /* User defined parameters */
} emb; /* Embedded, user callback */
};
struct stream;
/*
* IO class descriptor (file, directory, socket, SSL, CGI, etc)
* These classes are defined in io_*.c files.
*/
struct io_class {
const char *name;
int (*read)(struct stream *, void *buf, size_t len);
int (*write)(struct stream *, const void *buf, size_t len);
void (*close)(struct stream *);
};
/*
* Data exchange stream. It is backed by some communication channel:
* opened file, socket, etc. The 'read' and 'write' methods are
* determined by a communication channel.
*/
struct stream {
struct conn *conn;
union channel chan; /* Descriptor */
struct io io; /* IO buffer */
const struct io_class *io_class; /* IO class */
int nread_last; /* Bytes last read */
int headers_len;
big_int_t content_len;
unsigned int flags;
#define FLAG_HEADERS_PARSED 1
#define FLAG_SSL_ACCEPTED 2
#define FLAG_R 4 /* Can read in general */
#define FLAG_W 8 /* Can write in general */
#define FLAG_CLOSED 16
#define FLAG_DONT_CLOSE 32
#define FLAG_ALWAYS_READY 64 /* File, dir, user_func */
};
struct conn {
struct llhead link; /* Connections chain */
struct shttpd_ctx *ctx; /* Context this conn belongs to */
struct usa sa; /* Remote socket address */
time_t birth_time; /* Creation time */
time_t expire_time; /* Expiration time */
int loc_port; /* Local port */
int status; /* Reply status code */
int method; /* Request method */
char *uri; /* Decoded URI */
unsigned long major_version; /* Major HTTP version number */
unsigned long minor_version; /* Minor HTTP version number */
char *request; /* Request line */
char *headers; /* Request headers */
char *query; /* QUERY_STRING part of the URI */
char *path_info; /* PATH_INFO thing */
struct vec mime_type; /* Mime type */
struct headers ch; /* Parsed client headers */
struct stream loc; /* Local stream */
struct stream rem; /* Remote stream */
#if !defined(NO_SSI)
void *ssi; /* SSI descriptor */
#endif /* NO_SSI */
};
enum {
OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST,
OPT_CGI_EXTENSIONS, OPT_CGI_INTERPRETER, OPT_CGI_ENVIRONMENT,
OPT_SSI_EXTENSIONS, OPT_AUTH_REALM, OPT_AUTH_GPASSWD,
OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, OPT_MIME_TYPES,
OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_INETD, OPT_UID,
OPT_CFG_URI, OPT_PROTECT,
NUM_OPTIONS
};
/*
* SHTTPD context
*/
struct shttpd_ctx {
time_t start_time; /* Start time */
int nactive; /* # of connections now */
unsigned long nrequests; /* Requests made */
uint64_t in, out; /* IN/OUT traffic counters */
SSL_CTX *ssl_ctx; /* SSL context */
struct llhead connections; /* List of connections */
struct llhead registered_uris;/* User urls */
struct llhead error_handlers; /* Embedded error handlers */
struct llhead acl; /* Access control list */
struct llhead ssi_funcs; /* SSI callback functions */
struct llhead listeners; /* Listening sockets */
FILE *access_log; /* Access log stream */
FILE *error_log; /* Error log stream */
char *options[NUM_OPTIONS]; /* Configurable options */
#if defined(_WIN32)
CRITICAL_SECTION mutex; /* For MT case */
HANDLE ev[2]; /* For thread synchronization */
#elif defined(__rtems__)
rtems_id mutex;
#endif /* _WIN32 */
};
#define IS_TRUE(ctx, opt) ((ctx)->options[opt] && (ctx)->options[opt][0] =='1')
/*
* In SHTTPD, list of values are represented as comma or space separated
* string. For example, list of CGI extensions can be represented as
* ".cgi,.php,.pl", or ".cgi .php .pl". The macro that follows allows to
* loop through the individual values in that list.
*
* A "const char *" pointer and size_t variable must be passed to the macro.
* Spaces or commas can be used as delimiters (macro DELIM_CHARS).
*
* In every iteration of the loop, "s" points to the current value, and
* "len" specifies its length. The code inside loop must not change
* "s" and "len" parameters.
*/
#define FOR_EACH_WORD_IN_LIST(s,len) \
for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0; \
s += len, s+= strspn(s, DELIM_CHARS))
/*
* IPv4 ACL entry. Specifies subnet with deny/allow flag
*/
struct acl {
struct llhead link;
uint32_t ip; /* IP, in network byte order */
uint32_t mask; /* Also in network byte order */
int flag; /* Either '+' or '-' */
};
/*
* shttpd.c
*/
extern time_t current_time; /* Current UTC time */
extern int tz_offset; /* Offset from GMT time zone */
extern const struct vec known_http_methods[];
extern void stop_stream(struct stream *stream);
extern int url_decode(const char *, int, char *dst, int);
extern void send_server_error(struct conn *, int code, const char *reason);
extern int get_headers_len(const char *buf, size_t buflen);
extern void parse_headers(const char *s, int len, struct headers *parsed);
extern void open_listening_ports(struct shttpd_ctx *ctx);
extern void get_mime_type(struct shttpd_ctx *, const char *, int, struct vec *);
extern void free_list(struct llhead *head, void (*)(struct llhead *));
extern void registered_uri_destructor(struct llhead *);
extern void listener_destructor(struct llhead *);
/*
* config.c
*/
extern void usage(const char *prog);
/*
* log.c
*/
extern void elog(int flags, struct conn *c, const char *fmt, ...);
extern void log_access(FILE *fp, const struct conn *c);
/*
* string.c
*/
extern void my_strlcpy(register char *, register const char *, size_t);
extern int my_strncasecmp(register const char *,
register const char *, size_t);
extern char *my_strndup(const char *ptr, size_t len);
extern char *my_strdup(const char *str);
extern int my_snprintf(char *buf, size_t buflen, const char *fmt, ...);
extern int match_extension(const char *path, const char *ext_list);
/*
* compat_*.c
*/
extern void set_close_on_exec(int fd);
extern int set_non_blocking_mode(int fd);
extern int my_stat(const char *, struct stat *stp);
extern int my_open(const char *, int flags, int mode);
extern int my_remove(const char *);
extern int my_rename(const char *, const char *);
extern int my_mkdir(const char *, int);
extern char * my_getcwd(char *, int);
extern int spawn_process(struct conn *c, const char *prog,
char *envblk, char *envp[], int sock, const char *dir);
/*
* io_*.c
*/
extern const struct io_class io_file;
extern const struct io_class io_socket;
extern const struct io_class io_ssl;
extern const struct io_class io_cgi;
extern const struct io_class io_dir;
extern const struct io_class io_embedded;
extern const struct io_class io_ssi;
extern int put_dir(const char *path);
extern void get_dir(struct conn *c);
extern void get_file(struct conn *c, struct stat *stp);
extern void ssl_handshake(struct stream *stream);
extern void setup_embedded_stream(struct conn *, union variant, void *);
extern struct registered_uri *is_registered_uri(struct shttpd_ctx *,
const char *uri);
extern void do_ssi(struct conn *);
extern void ssi_func_destructor(struct llhead *lp);
/*
* auth.c
*/
extern int check_authorization(struct conn *c, const char *path);
extern int is_authorized_for_put(struct conn *c);
extern void send_authorization_request(struct conn *c);
extern int edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass);
/*
* cgi.c
*/
extern int run_cgi(struct conn *c, const char *prog);
extern void do_cgi(struct conn *c);
#define CGI_REPLY "HTTP/1.1 OK\r\n"
#define CGI_REPLY_LEN (sizeof(CGI_REPLY) - 1)
#endif /* DEFS_HEADER_DEFINED */

97
third-party/shttpd/io.h vendored Normal file
View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef IO_HEADER_INCLUDED
#define IO_HEADER_INCLUDED
#include <assert.h>
#include <stddef.h>
/*
* I/O buffer descriptor
*/
struct io {
char *buf; /* IO Buffer */
size_t size; /* IO buffer size */
size_t head; /* Bytes read */
size_t tail; /* Bytes written */
size_t total; /* Total bytes read */
};
static __inline void
io_clear(struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
io->total = io->tail = io->head = 0;
}
static __inline char *
io_space(struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->head <= io->size);
return (io->buf + io->head);
}
static __inline char *
io_data(struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->tail <= io->size);
return (io->buf + io->tail);
}
static __inline size_t
io_space_len(const struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->head <= io->size);
return (io->size - io->head);
}
static __inline size_t
io_data_len(const struct io *io)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->head <= io->size);
assert(io->tail <= io->head);
return (io->head - io->tail);
}
static __inline void
io_inc_tail(struct io *io, size_t n)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->tail <= io->head);
assert(io->head <= io->size);
io->tail += n;
assert(io->tail <= io->head);
if (io->tail == io->head)
io->head = io->tail = 0;
}
static __inline void
io_inc_head(struct io *io, size_t n)
{
assert(io->buf != NULL);
assert(io->size > 0);
assert(io->tail <= io->head);
io->head += n;
io->total += n;
assert(io->head <= io->size);
}
#endif /* IO_HEADER_INCLUDED */

122
third-party/shttpd/io_cgi.c vendored Normal file
View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int
write_cgi(struct stream *stream, const void *buf, size_t len)
{
assert(stream->chan.sock != -1);
assert(stream->flags & FLAG_W);
return (send(stream->chan.sock, buf, len, 0));
}
static int
read_cgi(struct stream *stream, void *buf, size_t len)
{
struct headers parsed;
char status[4];
int n;
assert(stream->chan.sock != -1);
assert(stream->flags & FLAG_R);
stream->flags &= ~FLAG_DONT_CLOSE;
n = recv(stream->chan.sock, buf, len, 0);
if (stream->flags & FLAG_HEADERS_PARSED)
return (n);
if (n <= 0 && ERRNO != EWOULDBLOCK) {
send_server_error(stream->conn, 500, "Error running CGI");
return (n);
}
/*
* CGI script may output Status: and Location: headers, which
* may alter the status code. Buffer in headers, parse
* them, send correct status code and then forward all data
* from CGI script back to the remote end.
* Reply line was alredy appended to the IO buffer in
* decide_what_to_do(), with blank status code.
*/
stream->flags |= FLAG_DONT_CLOSE;
io_inc_head(&stream->io, n);
stream->headers_len = get_headers_len(stream->io.buf, stream->io.head);
if (stream->headers_len < 0) {
stream->flags &= ~FLAG_DONT_CLOSE;
send_server_error(stream->conn, 500, "Bad headers sent");
elog(E_LOG, stream->conn, "CGI script sent invalid headers: "
"[%.*s]", stream->io.head - CGI_REPLY_LEN,
stream->io.buf + CGI_REPLY_LEN);
return (0);
}
/*
* If we did not received full headers yet, we must not send any
* data read from the CGI back to the client. Suspend sending by
* setting tail = head, which tells that there is no data in IO buffer
*/
if (stream->headers_len == 0) {
stream->io.tail = stream->io.head;
return (0);
}
/* Received all headers. Set status code for the connection. */
(void) memset(&parsed, 0, sizeof(parsed));
parse_headers(stream->io.buf, stream->headers_len, &parsed);
stream->content_len = parsed.cl.v_big_int;
stream->conn->status = (int) parsed.status.v_big_int;
/* If script outputs 'Location:' header, set status code to 302 */
if (parsed.location.v_vec.len > 0)
stream->conn->status = 302;
/*
* If script did not output neither 'Location:' nor 'Status' headers,
* set the default status code 200, which means 'success'.
*/
if (stream->conn->status == 0)
stream->conn->status = 200;
/* Append the status line to the beginning of the output */
(void) my_snprintf(status, sizeof(status), "%3d", stream->conn->status);
(void) memcpy(stream->io.buf + 9, status, 3);
DBG(("read_cgi: content len %lu status %s",
stream->content_len, status));
/* Next time, pass output directly back to the client */
assert((big_int_t) stream->headers_len <= stream->io.total);
stream->io.total -= stream->headers_len;
stream->io.tail = 0;
stream->flags |= FLAG_HEADERS_PARSED;
/* Return 0 because we've already shifted the head */
return (0);
}
static void
close_cgi(struct stream *stream)
{
assert(stream->chan.sock != -1);
(void) closesocket(stream->chan.sock);
}
const struct io_class io_cgi = {
"cgi",
read_cgi,
write_cgi,
close_cgi
};

144
third-party/shttpd/io_dir.c vendored Normal file
View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
/*
* For a given PUT path, create all intermediate subdirectories
* for given path. Return 0 if the path itself is a directory,
* or -1 on error, 1 if OK.
*/
int
put_dir(const char *path)
{
char buf[FILENAME_MAX];
const char *s, *p;
struct stat st;
size_t len;
for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
len = p - path;
assert(len < sizeof(buf));
(void) memcpy(buf, path, len);
buf[len] = '\0';
/* Try to create intermediate directory */
if (my_stat(buf, &st) == -1 && my_mkdir(buf, 0755) != 0)
return (-1);
/* Is path itself a directory ? */
if (p[1] == '\0')
return (0);
}
return (1);
}
static int
read_dir(struct stream *stream, void *buf, size_t len)
{
struct dirent *dp = NULL;
char file[FILENAME_MAX], line[FILENAME_MAX + 512],
size[64], mod[64];
struct stat st;
struct conn *c = stream->conn;
int n, nwritten = 0;
const char *slash = "";
assert(stream->chan.dir.dirp != NULL);
assert(stream->conn->uri[0] != '\0');
do {
if (len < sizeof(line))
break;
if ((dp = readdir(stream->chan.dir.dirp)) == NULL) {
stream->flags |= FLAG_CLOSED;
break;
}
DBG(("read_dir: %s", dp->d_name));
/* Do not show current dir and passwords file */
if (strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, HTPASSWD) == 0)
continue;
(void) my_snprintf(file, sizeof(file),
"%s%s%s", stream->chan.dir.path, slash, dp->d_name);
(void) my_stat(file, &st);
if (S_ISDIR(st.st_mode)) {
my_snprintf(size,sizeof(size),"%s","&lt;DIR&gt;");
} else {
if (st.st_size < 1024)
(void) my_snprintf(size, sizeof(size),
"%lu", (unsigned long) st.st_size);
else if (st.st_size < 1024 * 1024)
(void) my_snprintf(size, sizeof(size), "%luk",
(unsigned long) (st.st_size >> 10) + 1);
else
(void) my_snprintf(size, sizeof(size),
"%.1fM", (float) st.st_size / 1048576);
}
(void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
localtime(&st.st_mtime));
n = my_snprintf(line, sizeof(line),
"<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
"<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
c->uri, slash, dp->d_name, dp->d_name,
S_ISDIR(st.st_mode) ? "/" : "", mod, size);
(void) memcpy(buf, line, n);
buf = (char *) buf + n;
nwritten += n;
len -= n;
} while (dp != NULL);
return (nwritten);
}
static void
close_dir(struct stream *stream)
{
assert(stream->chan.dir.dirp != NULL);
assert(stream->chan.dir.path != NULL);
(void) closedir(stream->chan.dir.dirp);
free(stream->chan.dir.path);
}
void
get_dir(struct conn *c)
{
if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) {
(void) free(c->loc.chan.dir.path);
send_server_error(c, 500, "Cannot open directory");
} else {
c->loc.io.head = my_snprintf(c->loc.io.buf, c->loc.io.size,
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=utf-8\r\n\r\n"
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
"<tr><th>Name</th><th>Modified</th><th>Size</th></tr>"
"<tr><td colspan=\"3\"><hr></td></tr>",
c->uri, c->uri);
io_clear(&c->rem.io);
c->status = 200;
c->loc.io_class = &io_dir;
c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
}
}
const struct io_class io_dir = {
"dir",
read_dir,
NULL,
close_dir
};

275
third-party/shttpd/io_emb.c vendored Normal file
View File

@@ -0,0 +1,275 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
const char *
shttpd_version(void)
{
return (VERSION);
}
static void
call_user(struct conn *c, struct shttpd_arg *arg, shttpd_callback_t func)
{
arg->priv = c;
arg->state = c->loc.chan.emb.state;
arg->out.buf = io_space(&c->loc.io);
arg->out.len = io_space_len(&c->loc.io);
arg->out.num_bytes = 0;
arg->in.buf = io_data(&c->rem.io);;
arg->in.len = io_data_len(&c->rem.io);
arg->in.num_bytes = 0;
if (io_data_len(&c->rem.io) >= c->rem.io.size)
arg->flags |= SHTTPD_POST_BUFFER_FULL;
if (c->rem.content_len > 0 && c->rem.io.total < c->rem.content_len)
arg->flags |= SHTTPD_MORE_POST_DATA;
func(arg);
io_inc_head(&c->loc.io, arg->out.num_bytes);
io_inc_tail(&c->rem.io, arg->in.num_bytes);
c->loc.chan.emb.state = arg->state; /* Save state */
/*
* If callback finished output, that means it did all cleanup.
* If the connection is terminated unexpectedly, we canna call
* the callback via the stream close() method from disconnect.
* However, if cleanup is already done, we set close() method to
* NULL, to prevent the call from disconnect().
*/
if (arg->flags & SHTTPD_END_OF_OUTPUT)
c->loc.flags &= ~FLAG_DONT_CLOSE;
else
c->loc.flags |= FLAG_DONT_CLOSE;
}
static int
do_embedded(struct stream *stream, void *buf, size_t len)
{
struct shttpd_arg arg;
buf = NULL; len = 0; /* Squash warnings */
arg.user_data = stream->conn->loc.chan.emb.data;
arg.flags = 0;
call_user(stream->conn, &arg, (shttpd_callback_t)
stream->conn->loc.chan.emb.func.v_func);
return (0);
}
static void
close_embedded(struct stream *stream)
{
struct shttpd_arg arg;
struct conn *c = stream->conn;
arg.flags = SHTTPD_CONNECTION_ERROR;
arg.user_data = c->loc.chan.emb.data;
/*
* Do not call the user function if SHTTPD_END_OF_OUTPUT was set,
* i.e. the callback already terminated correctly
*/
if (stream->flags & FLAG_DONT_CLOSE)
call_user(stream->conn, &arg, (shttpd_callback_t)
c->loc.chan.emb.func.v_func);
}
size_t
shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
{
struct conn *c = arg->priv;
struct io *io = &c->loc.io;
char *buf = arg->out.buf + arg->out.num_bytes;
int buflen = arg->out.len - arg->out.num_bytes, len = 0;
va_list ap;
assert(buf <= io->buf + io->size);
if (buflen > 0) {
va_start(ap, fmt);
len = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
if (len < 0 || len > buflen)
len = buflen;
arg->out.num_bytes += len;
}
return (len);
}
const char *
shttpd_get_header(struct shttpd_arg *arg, const char *header_name)
{
struct conn *c = arg->priv;
char *p, *s, *e;
size_t len;
p = c->headers;
e = c->request + c->rem.headers_len;
len = strlen(header_name);
while (p < e) {
if ((s = strchr(p, '\n')) != NULL)
s[s[-1] == '\r' ? -1 : 0] = '\0';
if (my_strncasecmp(header_name, p, len) == 0)
return (p + len + 2);
p += strlen(p) + 1;
}
return (NULL);
}
const char *
shttpd_get_env(struct shttpd_arg *arg, const char *env_name)
{
struct conn *c = arg->priv;
struct vec *vec;
if (strcmp(env_name, "REQUEST_METHOD") == 0) {
return (known_http_methods[c->method].ptr);
} else if (strcmp(env_name, "REQUEST_URI") == 0) {
return (c->uri);
} else if (strcmp(env_name, "QUERY_STRING") == 0) {
return (c->query);
} else if (strcmp(env_name, "REMOTE_USER") == 0) {
vec = &c->ch.user.v_vec;
if (vec->len > 0) {
((char *) vec->ptr)[vec->len] = '\0';
return (vec->ptr);
}
} else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
}
return (NULL);
}
void
shttpd_get_http_version(struct shttpd_arg *arg, unsigned long *major, unsigned long *minor)
{
struct conn *c = arg->priv;
*major = c->major_version;
*minor = c->minor_version;
}
void
shttpd_register_uri(struct shttpd_ctx *ctx,
const char *uri, shttpd_callback_t callback, void *data)
{
struct registered_uri *e;
if ((e = malloc(sizeof(*e))) != NULL) {
e->uri = my_strdup(uri);
e->callback.v_func = (void (*)(void)) callback;
e->callback_data = data;
LL_TAIL(&ctx->registered_uris, &e->link);
}
}
int
shttpd_get_var(const char *var, const char *buf, int buf_len,
char *value, int value_len)
{
const char *p, *e, *s;
size_t var_len;
var_len = strlen(var);
e = buf + buf_len; /* End of QUERY_STRING buffer */
/* buf is "var1=val1&var2=val2...". Find variable first */
for (p = buf; p + var_len < e; p++)
if ((p == buf || p[-1] == '&') &&
p[var_len] == '=' &&
!my_strncasecmp(var, p, var_len)) {
/* Point 'p' to var value, 's' to the end of value */
p += var_len + 1;
if ((s = memchr(p, '&', e - p)) == NULL)
s = e;
/* URL-decode value. Return result length */
return (url_decode(p, s - p, value, value_len));
}
return (-1);
}
static int
match_regexp(const char *regexp, const char *text)
{
if (*regexp == '\0')
return (*text == '\0');
if (*regexp == '*')
do {
if (match_regexp(regexp + 1, text))
return (1);
} while (*text++ != '\0');
if (*text != '\0' && *regexp == *text)
return (match_regexp(regexp + 1, text + 1));
return (0);
}
struct registered_uri *
is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
{
struct llhead *lp;
struct registered_uri *reg_uri;
LL_FOREACH(&ctx->registered_uris, lp) {
reg_uri = LL_ENTRY(lp, struct registered_uri, link);
if (match_regexp(reg_uri->uri, uri))
return (reg_uri);
}
return (NULL);
}
void
setup_embedded_stream(struct conn *c, union variant func, void *data)
{
c->loc.chan.emb.state = NULL;
c->loc.chan.emb.func = func;
c->loc.chan.emb.data = data;
c->loc.io_class = &io_embedded;
c->loc.flags |= FLAG_R | FLAG_W |FLAG_ALWAYS_READY;
}
void
shttpd_handle_error(struct shttpd_ctx *ctx, int code,
shttpd_callback_t func, void *data)
{
struct error_handler *e;
if ((e = malloc(sizeof(*e))) != NULL) {
e->code = code;
e->callback.v_func = (void (*)(void)) func;
e->callback_data = data;
LL_TAIL(&ctx->error_handlers, &e->link);
}
}
const struct io_class io_embedded = {
"embedded",
do_embedded,
(int (*)(struct stream *, const void *, size_t)) do_embedded,
close_embedded
};

155
third-party/shttpd/io_file.c vendored Normal file
View File

@@ -0,0 +1,155 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int
write_file(struct stream *stream, const void *buf, size_t len)
{
struct stat st;
struct stream *rem = &stream->conn->rem;
int n, fd = stream->chan.fd;
assert(fd != -1);
n = write(fd, buf, len);
DBG(("put_file(%p, %d): %d bytes", (void *) stream, len, n));
if (n <= 0 || (rem->io.total >= (big_int_t) rem->headers_len)) {
(void) fstat(fd, &st);
stream->io.head = stream->headers_len =
my_snprintf(stream->io.buf,
stream->io.size, "HTTP/1.1 %d OK\r\n"
"Content-Length: %lu\r\nConnection: close\r\n\r\n",
stream->conn->status, st.st_size);
stop_stream(stream);
}
return (n);
}
static int
read_file(struct stream *stream, void *buf, size_t len)
{
#ifdef USE_SENDFILE
struct iovec vec;
struct sf_hdtr hd = {&vec, 1, NULL, 0}, *hdp = &hd;
int sock, fd, n;
size_t nbytes;
off_t sent;
sock = stream->conn->rem.chan.sock;
fd = stream->chan.fd;
/* If this is the first call for this file, send the headers */
vec.iov_base = stream->io.buf;
vec.iov_len = stream->headers_len;
if (stream->io.total > 0)
hdp = NULL;
nbytes = stream->content_len - stream->io.total;
n = sendfile(fd, sock, lseek(fd, 0, SEEK_CUR), nbytes, hdp, &sent, 0);
if (n == -1 && ERRNO != EAGAIN) {
stream->flags &= ~FLAG_DONT_CLOSE;
return (n);
}
stream->conn->ctx->out += sent;
/* If we have sent the HTTP headers in this turn, clear them off */
if (stream->io.total == 0) {
assert(sent >= stream->headers_len);
sent -= stream->headers_len;
io_clear(&stream->io);
}
(void) lseek(fd, sent, SEEK_CUR);
stream->io.total += sent;
stream->flags |= FLAG_DONT_CLOSE;
return (0);
#endif /* USE_SENDFILE */
assert(stream->chan.fd != -1);
return (read(stream->chan.fd, buf, len));
}
static void
close_file(struct stream *stream)
{
assert(stream->chan.fd != -1);
(void) close(stream->chan.fd);
}
void
get_file(struct conn *c, struct stat *stp)
{
char date[64], lm[64], etag[64], range[64] = "";
size_t n, status = 200;
unsigned long r1, r2;
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";
big_int_t cl; /* Content-Length */
if (c->mime_type.len == 0)
get_mime_type(c->ctx, c->uri, strlen(c->uri), &c->mime_type);
cl = (big_int_t) stp->st_size;
/* If Range: header specified, act accordingly */
if (c->ch.range.v_vec.len > 0 &&
(n = sscanf(c->ch.range.v_vec.ptr,"bytes=%lu-%lu",&r1, &r2)) > 0) {
status = 206;
(void) lseek(c->loc.chan.fd, r1, SEEK_SET);
cl = n == 2 ? r2 - r1 + 1: cl - r1;
(void) my_snprintf(range, sizeof(range),
"Content-Range: bytes %lu-%lu/%lu\r\n",
r1, r1 + cl - 1, (unsigned long) stp->st_size);
msg = "Partial Content";
}
/* Prepare Etag, Date, Last-Modified headers */
(void) strftime(date, sizeof(date), fmt, localtime(&current_time));
(void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
(void) my_snprintf(etag, sizeof(etag), "%lx.%lx",
(unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
/*
* We do not do io_inc_head here, because it will increase 'total'
* member in io. We want 'total' to be equal to the content size,
* and exclude the headers length from it.
*/
c->loc.io.head = c->loc.headers_len = my_snprintf(c->loc.io.buf,
c->loc.io.size,
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Last-Modified: %s\r\n"
"Etag: \"%s\"\r\n"
"Content-Type: %.*s\r\n"
"Content-Length: %lu\r\n"
"Accept-Ranges: bytes\r\n"
"%s\r\n",
status, msg, date, lm, etag,
c->mime_type.len, c->mime_type.ptr, cl, range);
c->status = status;
c->loc.content_len = cl;
c->loc.io_class = &io_file;
c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
if (c->method == METHOD_HEAD)
stop_stream(&c->loc);
}
const struct io_class io_file = {
"file",
read_file,
write_file,
close_file
};

39
third-party/shttpd/io_socket.c vendored Normal file
View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int
read_socket(struct stream *stream, void *buf, size_t len)
{
assert(stream->chan.sock != -1);
return (recv(stream->chan.sock, buf, len, 0));
}
static int
write_socket(struct stream *stream, const void *buf, size_t len)
{
assert(stream->chan.sock != -1);
return (send(stream->chan.sock, buf, len, 0));
}
static void
close_socket(struct stream *stream)
{
assert(stream->chan.sock != -1);
(void) closesocket(stream->chan.sock);
}
const struct io_class io_socket = {
"socket",
read_socket,
write_socket,
close_socket
};

482
third-party/shttpd/io_ssi.c vendored Normal file
View File

@@ -0,0 +1,482 @@
/*
* Copyright (c) 2006,2007 Steven Johnson <sjohnson@sakuraindustries.com>
* Copyright (c) 2007 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
#define CMDBUFSIZ 512 /* SSI command buffer size */
#define NEST_MAX 6 /* Maximum nesting level */
struct ssi_func {
struct llhead link;
void *user_data;
char *name;
shttpd_callback_t func;
};
struct ssi_inc {
int state; /* Buffering state */
int cond; /* Conditional state */
FILE *fp; /* Icluded file stream */
char buf[CMDBUFSIZ]; /* SSI command buffer */
size_t nbuf; /* Bytes in a command buffer */
FILE *pipe; /* #exec stream */
struct ssi_func func; /* #call function */
};
struct ssi {
struct conn *conn; /* Connection we belong to */
int nest; /* Current nesting level */
struct ssi_inc incs[NEST_MAX]; /* Nested includes */
};
enum { SSI_PASS, SSI_BUF, SSI_EXEC, SSI_CALL };
enum { SSI_GO, SSI_STOP }; /* Conditional states */
static const struct vec st = {"<!--#", 5};
void
shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
shttpd_callback_t func, void *user_data)
{
struct ssi_func *e;
if ((e = malloc(sizeof(*e))) != NULL) {
e->name = my_strdup(name);
e->func = func;
e->user_data = user_data;
LL_TAIL(&ctx->ssi_funcs, &e->link);
}
}
void
ssi_func_destructor(struct llhead *lp)
{
struct ssi_func *e = LL_ENTRY(lp, struct ssi_func, link);
free(e->name);
free(e);
}
static const struct ssi_func *
find_ssi_func(struct ssi *ssi, const char *name)
{
struct ssi_func *e;
struct llhead *lp;
LL_FOREACH(&ssi->conn->ctx->ssi_funcs, lp) {
e = LL_ENTRY(lp, struct ssi_func, link);
if (!strcmp(name, e->name))
return (e);
}
return (NULL);
}
static void
call(struct ssi *ssi, const char *name,
struct shttpd_arg *arg, char *buf, int len)
{
const struct ssi_func *ssi_func;
(void) memset(arg, 0, sizeof(*arg));
/*
* SSI function may be called with parameters. These parameters
* are passed as arg->in.buf, arg->in.len vector.
*/
arg->in.buf = strchr(name, ' ');
if (arg->in.buf != NULL) {
*arg->in.buf++ = '\0';
arg->in.len = strlen(arg->in.buf);
}
if ((ssi_func = find_ssi_func(ssi, name)) != NULL) {
arg->priv = ssi->conn;
arg->user_data = ssi_func->user_data;
arg->out.buf = buf;
arg->out.len = len;
ssi_func->func(arg);
}
}
static int
evaluate(struct ssi *ssi, const char *name)
{
struct shttpd_arg arg;
call(ssi, name, &arg, NULL, 0);
return (arg.flags & SHTTPD_SSI_EVAL_TRUE);
}
static void
pass(struct ssi_inc *inc, void *buf, int *n)
{
if (inc->cond == SSI_GO) {
(void) memcpy(buf, inc->buf, inc->nbuf);
(*n) += inc->nbuf;
}
inc->nbuf = 0;
inc->state = SSI_PASS;
}
static int
get_path(struct conn *conn, const char *src,
int src_len, char *dst, int dst_len)
{
static struct vec accepted[] = {
{"\"", 1}, /* Relative to webserver CWD */
{"file=\"", 6}, /* Relative to current URI */
{"virtual=\"", 9}, /* Relative to document root */
{NULL, 0},
};
struct vec *vec;
const char *p, *root = conn->ctx->options[OPT_ROOT];
int len;
for (vec = accepted; vec->len > 0; vec++)
if (src_len > vec->len && !memcmp(src, vec->ptr, vec->len)) {
src += vec->len;
src_len -= vec->len;
if ((p = memchr(src, '"', src_len)) == NULL)
break;
if (vec->len == 6) {
len = my_snprintf(dst, dst_len, "%s%c%s",
root, DIRSEP, conn->uri);
while (len > 0 && dst[len] != '/')
len--;
dst += len;
dst_len -= len;
} else if (vec->len == 9) {
len = my_snprintf(dst, dst_len, "%s%c",
root, DIRSEP);
dst += len;
dst_len -= len;
}
url_decode(src, p - src, dst, dst_len);
return (1);
}
return (0);
}
static void
do_include(struct ssi *ssi)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
char buf[FILENAME_MAX];
FILE *fp;
assert(inc->nbuf >= 13);
if (inc->cond == SSI_STOP) {
/* Do nothing - conditional FALSE */
} else if (ssi->nest >= (int) NELEMS(ssi->incs) - 1) {
elog(E_LOG, ssi->conn,
"ssi: #include: maximum nested level reached");
} else if (!get_path(ssi->conn,
inc->buf + 13, inc->nbuf - 13, buf, sizeof(buf))) {
elog(E_LOG, ssi->conn, "ssi: bad #include: [%.*s]",
inc->nbuf, inc->buf);
} else if ((fp = fopen(buf, "r")) == NULL) {
elog(E_LOG, ssi->conn,
"ssi: fopen(%s): %s", buf, strerror(errno));
} else {
ssi->nest++;
ssi->incs[ssi->nest].fp = fp;
ssi->incs[ssi->nest].nbuf = 0;
ssi->incs[ssi->nest].cond = SSI_GO;
}
}
static char *
trim_spaces(struct ssi_inc *inc)
{
char *p = inc->buf + inc->nbuf - 2;
/* Trim spaces from the right */
*p-- = '\0';
while (isspace(* (unsigned char *) p))
*p-- = '\0';
/* Shift pointer to the start of attributes */
for (p = inc->buf; !isspace(* (unsigned char *) p); p++);
while (*p && isspace(* (unsigned char *) p)) p++;
return (p);
}
static void
do_if(struct ssi *ssi)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
char *name = trim_spaces(inc);
inc->cond = evaluate(ssi, name) ? SSI_GO : SSI_STOP;
}
static void
do_elif(struct ssi *ssi)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
char *name = trim_spaces(inc);
if (inc->cond == SSI_STOP && evaluate(ssi, name))
inc->cond = SSI_GO;
else
inc->cond = SSI_STOP;
}
static void
do_endif(struct ssi *ssi)
{
ssi->incs[ssi->nest].cond = SSI_GO;
}
static void
do_else(struct ssi *ssi)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
inc->cond = inc->cond == SSI_GO ? SSI_STOP : SSI_GO;
}
static void
do_call2(struct ssi *ssi, char *buf, int len, int *n)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
struct shttpd_arg arg;
call(ssi, inc->buf, &arg, buf, len);
(*n) += arg.out.num_bytes;
if (arg.flags & SHTTPD_END_OF_OUTPUT)
inc->state = SSI_PASS;
}
static void
do_call(struct ssi *ssi, char *buf, int len, int *n)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
char *name = trim_spaces(inc);
if (inc->cond == SSI_GO) {
(void) memmove(inc->buf, name, strlen(name) + 1);
inc->state = SSI_CALL;
do_call2(ssi, buf, len, n);
}
}
static void
do_exec2(struct ssi *ssi, char *buf, int len, int *n)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
int i, ch;
for (i = 0; i < len; i++) {
if ((ch = fgetc(inc->pipe)) == EOF) {
inc->state = SSI_PASS;
(void) pclose(inc->pipe);
inc->pipe = NULL;
break;
}
*buf++ = ch;
(*n)++;
}
}
static void
do_exec(struct ssi *ssi, char *buf, int len, int *n)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
char cmd[sizeof(inc->buf)], *e, *p;
p = trim_spaces(inc);
if (inc->cond == SSI_STOP) {
/* Do nothing - conditional FALSE */
} else if (*p != '"' || (e = strchr(p + 1, '"')) == NULL) {
elog(E_LOG, ssi->conn, "ssi: bad exec(%s)", p);
} else if (!url_decode(p + 1, e - p - 1, cmd, sizeof(cmd))) {
elog(E_LOG, ssi->conn, "ssi: cannot url_decode: exec(%s)", p);
} else if ((inc->pipe = popen(cmd, "r")) == NULL) {
elog(E_LOG, ssi->conn, "ssi: popen(%s)", cmd);
} else {
inc->state = SSI_EXEC;
do_exec2(ssi, buf, len, n);
}
}
static const struct ssi_cmd {
struct vec vec;
void (*func)();
} known_ssi_commands [] = {
{{"include ", 8}, do_include },
{{"if ", 3}, do_if },
{{"elif ", 5}, do_elif },
{{"else", 4}, do_else },
{{"endif", 5}, do_endif },
{{"call ", 5}, do_call },
{{"exec ", 5}, do_exec },
{{NULL, 0}, NULL }
};
static void
do_command(struct ssi *ssi, char *buf, size_t len, int *n)
{
struct ssi_inc *inc = ssi->incs + ssi->nest;
const struct ssi_cmd *cmd;
assert(len > 0);
assert(inc->nbuf <= len);
inc->state = SSI_PASS;
for (cmd = known_ssi_commands; cmd->func != NULL; cmd++)
if (inc->nbuf > (size_t) st.len + cmd->vec.len &&
!memcmp(inc->buf + st.len, cmd->vec.ptr, cmd->vec.len)) {
cmd->func(ssi, buf, len, n);
break;
}
if (cmd->func == NULL)
pass(inc, buf, n);
inc->nbuf = 0;
}
static int
read_ssi(struct stream *stream, void *vbuf, size_t len)
{
struct ssi *ssi = stream->conn->ssi;
struct ssi_inc *inc = ssi->incs + ssi->nest;
char *buf = vbuf;
int ch = EOF, n = 0;
again:
if (inc->state == SSI_CALL)
do_call2(ssi, buf, len, &n);
else if (inc->state == SSI_EXEC)
do_exec2(ssi, buf, len, &n);
while (n + inc->nbuf < len && (ch = fgetc(inc->fp)) != EOF)
switch (inc->state) {
case SSI_PASS:
if (ch == '<') {
inc->nbuf = 0;
inc->buf[inc->nbuf++] = ch;
inc->state = SSI_BUF;
} else if (inc->cond == SSI_GO) {
buf[n++] = ch;
}
break;
/*
* We are buffering whole SSI command, until closing "-->".
* That means that when do_command() is called, we can rely
* on that full command with arguments is buffered in and
* there is no need for streaming.
* Restrictions:
* 1. The command must fit in CMDBUFSIZ
* 2. HTML comments inside the command ? Not sure about this.
*/
case SSI_BUF:
if (inc->nbuf >= sizeof(inc->buf) - 1) {
pass(inc, buf + n, &n);
} else if (ch == '>' &&
!memcmp(inc->buf + inc->nbuf - 2, "--", 2)) {
do_command(ssi, buf + n, len - n, &n);
inc = ssi->incs + ssi->nest;
} else {
inc->buf[inc->nbuf++] = ch;
/* If not SSI tag, pass it */
if (inc->nbuf <= (size_t) st.len &&
memcmp(inc->buf, st.ptr, inc->nbuf) != 0)
pass(inc, buf + n, &n);
}
break;
case SSI_EXEC:
case SSI_CALL:
break;
default:
/* Never happens */
abort();
break;
}
if (ssi->nest > 0 && n + inc->nbuf < len && ch == EOF) {
(void) fclose(inc->fp);
inc->fp = NULL;
ssi->nest--;
inc--;
goto again;
}
return (n);
}
static void
close_ssi(struct stream *stream)
{
struct ssi *ssi = stream->conn->ssi;
size_t i;
for (i = 0; i < NELEMS(ssi->incs); i++) {
if (ssi->incs[i].fp != NULL)
(void) fclose(ssi->incs[i].fp);
if (ssi->incs[i].pipe != NULL)
(void) pclose(ssi->incs[i].pipe);
}
free(ssi);
}
void
do_ssi(struct conn *c)
{
char date[64];
struct ssi *ssi;
(void) strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT",
localtime(&current_time));
c->loc.io.head = c->loc.headers_len = my_snprintf(c->loc.io.buf,
c->loc.io.size,
"HTTP/1.1 200 OK\r\n"
"Date: %s\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n\r\n",
date);
c->status = 200;
c->loc.io_class = &io_ssi;
c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
if (c->method == METHOD_HEAD) {
stop_stream(&c->loc);
} else if ((ssi = calloc(1, sizeof(struct ssi))) == NULL) {
send_server_error(c, 500, "Cannot allocate SSI descriptor");
} else {
ssi->incs[0].fp = fdopen(c->loc.chan.fd, "r");
ssi->conn = c;
c->ssi = ssi;
}
}
const struct io_class io_ssi = {
"ssi",
read_ssi,
NULL,
close_ssi
};

85
third-party/shttpd/io_ssl.c vendored Normal file
View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
#if !defined(NO_SSL)
struct ssl_func ssl_sw[] = {
{"SSL_free", {0}},
{"SSL_accept", {0}},
{"SSL_connect", {0}},
{"SSL_read", {0}},
{"SSL_write", {0}},
{"SSL_get_error", {0}},
{"SSL_set_fd", {0}},
{"SSL_new", {0}},
{"SSL_CTX_new", {0}},
{"SSLv23_server_method", {0}},
{"SSL_library_init", {0}},
{"SSL_CTX_use_PrivateKey_file", {0}},
{"SSL_CTX_use_certificate_file",{0}},
{NULL, {0}}
};
void
ssl_handshake(struct stream *stream)
{
int n;
if ((n = SSL_accept(stream->chan.ssl.ssl)) == 0) {
n = SSL_get_error(stream->chan.ssl.ssl, n);
if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
stream->flags |= FLAG_CLOSED;
elog(E_LOG, stream->conn, "SSL_accept error %d", n);
} else {
DBG(("handshake: SSL accepted"));
stream->flags |= FLAG_SSL_ACCEPTED;
}
}
static int
read_ssl(struct stream *stream, void *buf, size_t len)
{
int nread = 0;
assert(stream->chan.ssl.ssl != NULL);
if (!(stream->flags & FLAG_SSL_ACCEPTED))
ssl_handshake(stream);
if (stream->flags & FLAG_SSL_ACCEPTED)
nread = SSL_read(stream->chan.ssl.ssl, buf, len);
return (nread);
}
static int
write_ssl(struct stream *stream, const void *buf, size_t len)
{
assert(stream->chan.ssl.ssl != NULL);
return (SSL_write(stream->chan.ssl.ssl, buf, len));
}
static void
close_ssl(struct stream *stream)
{
assert(stream->chan.ssl.sock != -1);
assert(stream->chan.ssl.ssl != NULL);
(void) closesocket(stream->chan.ssl.sock);
SSL_free(stream->chan.ssl.ssl);
}
const struct io_class io_ssl = {
"ssl",
read_ssl,
write_ssl,
close_ssl
};
#endif /* !NO_SSL */

59
third-party/shttpd/llist.h vendored Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef LLIST_HEADER_INCLUDED
#define LLIST_HEADER_INCLUDED
/*
* Linked list macros.
*/
struct llhead {
struct llhead *prev;
struct llhead *next;
};
#define LL_INIT(N) ((N)->next = (N)->prev = (N))
#define LL_HEAD(H) struct llhead H = { &H, &H }
#define LL_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N)))
#define LL_ADD(H, N) \
do { \
((H)->next)->prev = (N); \
(N)->next = ((H)->next); \
(N)->prev = (H); \
(H)->next = (N); \
} while (0)
#define LL_TAIL(H, N) \
do { \
((H)->prev)->next = (N); \
(N)->prev = ((H)->prev); \
(N)->next = (H); \
(H)->prev = (N); \
} while (0)
#define LL_DEL(N) \
do { \
((N)->next)->prev = ((N)->prev); \
((N)->prev)->next = ((N)->next); \
LL_INIT(N); \
} while (0)
#define LL_EMPTY(N) ((N)->next == (N))
#define LL_FOREACH(H,N) for (N = (H)->next; N != (H); N = (N)->next)
#define LL_FOREACH_SAFE(H,N,T) \
for (N = (H)->next, T = (N)->next; N != (H); \
N = (T), T = (N)->next)
#endif /* LLIST_HEADER_INCLUDED */

93
third-party/shttpd/log.c vendored Normal file
View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
/*
* Log function
*/
void
elog(int flags, struct conn *c, const char *fmt, ...)
{
char date[64], buf[URI_MAX];
int len;
FILE *fp = c == NULL ? NULL : c->ctx->error_log;
va_list ap;
/* Print to stderr */
if (c == NULL || !IS_TRUE(c->ctx, OPT_INETD)) {
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
(void) fputc('\n', stderr);
va_end(ap);
}
strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y",
localtime(&current_time));
len = my_snprintf(buf, sizeof(buf),
"[%s] [error] [client %s] \"%s\" ",
date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
c && c->request ? c->request : "-");
va_start(ap, fmt);
(void) vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
va_end(ap);
buf[sizeof(buf) - 1] = '\0';
if (fp != NULL && (flags & (E_FATAL | E_LOG))) {
(void) fprintf(fp, "%s\n", buf);
(void) fflush(fp);
}
if (flags & E_FATAL)
exit(EXIT_FAILURE);
}
void
log_access(FILE *fp, const struct conn *c)
{
static const struct vec dash = {"-", 1};
const struct vec *user = &c->ch.user.v_vec;
const struct vec *referer = &c->ch.referer.v_vec;
const struct vec *user_agent = &c->ch.useragent.v_vec;
char date[64], buf[URI_MAX], *q1 = "\"", *q2 = "\"";
if (user->len == 0)
user = &dash;
if (referer->len == 0) {
referer = &dash;
q1 = "";
}
if (user_agent->len == 0) {
user_agent = &dash;
q2 = "";
}
(void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
localtime(&c->birth_time));
(void) my_snprintf(buf, sizeof(buf),
"%s - %.*s [%s %+05d] \"%s\" %d %lu %s%.*s%s %s%.*s%s",
inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr,
date, tz_offset, c->request ? c->request : "-",
c->status, (unsigned long) c->loc.io.total,
q1, referer->len, referer->ptr, q1,
q2, user_agent->len, user_agent->ptr, q2);
if (fp != NULL) {
(void) fprintf(fp, "%s\n", buf);
(void) fflush(fp);
}
}

249
third-party/shttpd/md5.c vendored Normal file
View File

@@ -0,0 +1,249 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
#include "defs.h"
#ifndef HAVE_MD5
#if __BYTE_ORDER == 1234
#define byteReverse(buf, len) /* Nothing */
#else
/*
* Note: this code is harmless on little-endian machines.
*/
static void byteReverse(unsigned char *buf, unsigned longs)
{
uint32_t t;
do {
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
*(uint32_t *) buf = t;
buf += 4;
} while (--longs);
}
#endif /* __BYTE_ORDER */
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void MD5Init(MD5_CTX *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
{
register uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void
MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
{
uint32_t t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
MD5Final(unsigned char digest[16], MD5_CTX *ctx)
{
unsigned count;
unsigned char *p;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
((uint32_t *) ctx->in)[14] = ctx->bits[0];
((uint32_t *) ctx->in)[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
}
#endif /* !HAVE_MD5 */

24
third-party/shttpd/md5.h vendored Normal file
View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef MD5_HEADER_INCLUDED
#define MD5_HEADER_INCLUDED
typedef struct MD5Context {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
} MD5_CTX;
extern void MD5Init(MD5_CTX *ctx);
extern void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len);
extern void MD5Final(unsigned char digest[16], MD5_CTX *ctx);
#endif /*MD5_HEADER_INCLUDED */

172
third-party/shttpd/shttpd.1 vendored Normal file
View File

@@ -0,0 +1,172 @@
.\" Process this file with
.\" groff -man -Tascii shttpd.1
.\" $Id: shttpd.1,v 1.9 2008/02/13 21:57:27 drozd Exp $
.Dd Feb 12, 2008
.Dt SHTTPD 1
.Sh NAME
.Nm shttpd
.Nd lightweight web server
.Sh SYNOPSIS
.Nm
.Op Ar options
.Op Ar config_file
.Nm
.Fl A Ar htpasswd_file domain_name user_name password
.Sh DESCRIPTION
.Nm
is small, fast and easy to use web server with CGI, SSL, Digest Authorization
support. It can be run as stand-alone server, be managed by
.Xr inetd 8
, or be embedded into existing C/C++ application.
.Pp
.Nm
does not detach from terminal, and makes current working directory
be the web root, unless
.Fl root
option is specified.
.Pp
Unlike other web servers,
.Nm
does not expect CGI scirpts to be put in a special directory. They may be
anywhere. CGI files are recognized by the file extension.
.Pp
SSI files are also recognized by extension. Currently, the only SSI directives
supported are `<!--#include "url-encoded-path" -->'
and `<!--#exec "program" -->'. The `url-encoded-path' can be relative to
.Nm
working directory, or absolute system path. In the embedded mode, more
directives are available: #call, #if/#elif/#endif/#else/#endif.
Unsupported SSI directives are silently ignored.
.Pp
It is possible to specify multiple ports to listen on. For example, to
make
.Nm
listen on HTTP port 80 and HTTPS port 443, one should start it as
.Sq shttpd -ssl_cert cert.pem -ports 80,443s
.Pp
.Nm
can use the configuration file. By default, it is "shttpd.conf", and if it
is present in the same directory where
.Nm
lives, the command line options are read from it. Alternatively, the
configuration file may be specified as a last argument. The format of the
configuration file is exactly the same as for the command line options, the
only difference is that the command line options must be specified on
separate lines, and dashes for options must be omitted.
Lines beginning with '#' are regarded as comments and ignored.
.Pp
.Sh OPTIONS
.Bl -tag -width indent
.It Fl A Ar htpasswd_file domain_name user_name password
Add/edit user's password in the passwords file. Deleting users can be done
with any text editor. Functionality similar to Apache's
.Ic htdigest
utility.
.It Fl access_log Ar file
Access log file. Default: not set, no logging is done.
.It Fl acl Ar (+|-)x.x.x.x[/x],...
Specify access control list (ACL). ACL is a comma separated list
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus means allow,
minus means deny. If subnet mask is
omitted, like "-1.2.3.4", then it means single IP address. Mask may vary
from 0 to 32 inclusive. Default: not set, allow all.
.It Fl aliases Ar list
This options gives an ability to serve the directories outside web root
by sort of symbolic linking to certain URI. The
.Ar list
must be comma-separated list of URI=PATH pairs, like this:
"/etc/=/my_etc,/tmp=/my_tmp". Default: not set.
.It Fl auth_PUT Ar file
PUT and DELETE passwords file. This must be specified if PUT or
DELETE methods are used. Default: not set.
.It Fl auth_gpass Ar file
Location of global passwords file. When set, per-directory .htpasswd files are
ignored, and all accessed must be authorised against global passwords file.
Default: not set.
.It Fl auth_realm Ar domain_name
Authorization realm. Default: "mydomain.com".
.It Fl cfg_uri Ar uri
If set,
.Nm
creates special administrative URI where options may be changed at runtime.
This URI probably wants to be password-protected, look at
.Fl protect
option, and in the EXAMPLES section on how to do it. Default: not set.
.It Fl cgi_env Ar list
Pass environment variables to the CGI script in addition to standard ones.
The list must be comma-separated list of X=Y pairs, like this:
"VARIABLE1=VALUE1,VARIABLE2=VALUE2". Default: not set.
.It Fl cgi_ext Ar list
Comma-separated list of CGI extensions. All files having these extensions
are treated as CGI scripts. Default: "cgi,pl,php"
.It Fl cgi_interp Ar file
Force
.Ar file
to be a CGI interpreter for all CGI scripts. By default this option is not
set, and
.Nm
decides which interpreter to use by looking at the first line of CGI script.
.It Fl dir_list Ar 0|1
Enable/disable directory listing. Default: "1" (enabled).
.It Fl error_log Ar file
Error log file. Default: not set, no errors are logged.
.It Fl inetd Ar 0|1
Enable/disable inetd mode. Default: "0" (disabled).
.It Fl mime_types Ar list
Additional to builtin mime types, in form "EXTENSION1=TYPE1,EXTENSION2=TYPE2".
.It Fl ports Ar port_list
Comma-separated list of ports to listen on. If the port is SSL, a letter 's'
must be appeneded, for example, "80,443s" will open port 80 and port 443,
and connections on port 443 will be SSL-ed. Default: 80
.It Fl protect Ar list
Comma separated list of URI=PATH pairs, specifying that given URIs
must the protected with respected password files. Default: not set.
.It Fl root Ar directory
Location of the WWW root directory. Default: working directory from which
.Nm
has been started.
.It Fl ssi_ext Ar list
Comma separated list of SSI extensions. Default: "shtml,shtm".
.It Fl ssl_cert Ar pem_file
Location of SSL certificate file. Default: not set.
.It Fl uid Ar login
Switch to given user after startup. Default: not set.
.El
.Pp
.Sh EMBEDDING
.Nm
can be built as a library to embed web server functionality
into C/C++ application. The API functions are declared in a header
file
.Pa shttpd.h .
Please refer to the source package for a header file and the examples.
.Pp
.Sh EXAMPLES
.Bl -tag -width indent
.It Nm Fl root Ar /var/www Fl ports Ar 8080,8043s Fl ssl_cert Ar /etc/cert.pem Fl aliases Ar /aa=/tmp,/bb=/etc
Start listening on port 8080 for HTTP, and 8043 for HTTPS connections.
Use /etc/cert.pem as SSL certificate file. Web root is /var/www. In addition,
map directory /tmp to URI /aa, directory /etc to URI /bb.
.It Nm Fl acl Ar -0.0.0.0/0,+10.0.0.0/8,+1.2.3.4
Deny connections from everywhere, allow only IP address 1.2.3.4 and
all IP addresses from 10.0.0.0/8 subnet to connect.
.It Nm Fl ports Ar 8080 Fl cfg_uri Ar /ctl Fl protect Ar /ctl=/tmp/passwords.txt
Start listening on port 8080, create an administrative URI "/ctl" where
options may be changed at runtime, and protect that URI with authorization.
.It http stream tcp nowait nobody /bin/shttpd shttpd -inetd 1 -root /var/www
This line in
.Pa /etc/inetd.conf
makes
.Nm
run by
.Xr inetd 8
daemon.
.El
.Pp
.Sh SEE ALSO
.Xr inetd 8 .
.Sh COPYRIGHT
.Nm
is licensed under the terms of beerware license.
.Sh AUTHOR
.An Sergey Lyubka Aq valenok@gmail.com .

1220
third-party/shttpd/shttpd.c vendored Normal file

File diff suppressed because it is too large Load Diff

125
third-party/shttpd/shttpd.h vendored Normal file
View File

@@ -0,0 +1,125 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* $Id: shttpd.h,v 1.9 2008/02/13 20:44:32 drozd Exp $
*/
#ifndef SHTTPD_HEADER_INCLUDED
#define SHTTPD_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
struct ubuf {
char *buf; /* Buffer pointer */
int len; /* Size of a buffer */
int num_bytes; /* Bytes processed by callback */
};
/*
* This structure is passed to the user callback function
*/
struct shttpd_arg {
void *priv; /* Private! Do not touch! */
void *state; /* User state */
void *user_data; /* User-defined data */
struct ubuf in; /* Input is here, POST data */
struct ubuf out; /* Output goes here */
unsigned int flags;
#define SHTTPD_END_OF_OUTPUT 1
#define SHTTPD_CONNECTION_ERROR 2
#define SHTTPD_MORE_POST_DATA 4
#define SHTTPD_POST_BUFFER_FULL 8
#define SHTTPD_SSI_EVAL_TRUE 16
};
/*
* User callback function. Called when certain registered URLs have been
* requested. These are the requirements to the callback function:
*
* 1. it must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
* and record how many bytes are copied, into 'out.num_bytes'
* 2. it must not block the execution
* 3. it must set SHTTPD_END_OF_OUTPUT flag when finished
* 4. for POST requests, it must process the incoming data (in.buf) of length
* 'in.len', and set 'in.num_bytes', which is how many bytes of POST
* data is read and can be discarded by SHTTPD.
* 5. If callback allocates arg->state, to keep state, it must deallocate it
* at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
*/
typedef void (*shttpd_callback_t)(struct shttpd_arg *);
/*
* shttpd_init Initialize shttpd context.
* shttpd_set_option Set new value for option.
* shttpd_fini Dealocate the context
* shttpd_register_uri Setup the callback function for specified URL.
* shtppd_listen Setup a listening socket in the SHTTPD context
* shttpd_poll Do connections processing
* shttpd_version return string with SHTTPD version
* shttpd_get_var Fetch POST/GET variable value by name. Return value len
* shttpd_get_header return value of the specified HTTP header
* shttpd_get_env return string values for the following
* pseudo-variables: "REQUEST_METHOD", "REQUEST_URI",
* "REMOTE_USER" and "REMOTE_ADDR".
*/
struct shttpd_ctx;
struct shttpd_ctx *shttpd_init(void);
void shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
void shttpd_fini(struct shttpd_ctx *);
int shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl);
void shttpd_register_uri(struct shttpd_ctx *ctx, const char *uri,
shttpd_callback_t callback, void *const user_data);
void shttpd_poll(struct shttpd_ctx *, int milliseconds);
const char *shttpd_version(void);
int shttpd_get_var(const char *var, const char *buf, int buf_len,
char *value, int value_len);
const char *shttpd_get_header(struct shttpd_arg *, const char *header_name);
const char *shttpd_get_env(struct shttpd_arg *, const char *name);
void shttpd_get_http_version(struct shttpd_arg *,
unsigned long *major, unsigned long *minor);
size_t shttpd_printf(struct shttpd_arg *, const char *fmt, ...);
void shttpd_handle_error(struct shttpd_ctx *ctx, int status,
shttpd_callback_t func, void *const data);
void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
shttpd_callback_t func, void *const user_data);
/*
* The following three functions are for applications that need to
* load-balance the connections on their own. Many threads may be spawned
* with one SHTTPD context per thread. Boss thread may only wait for
* new connections by means of shttpd_accept(). Then it may scan thread
* pool for the idle thread by means of shttpd_active(), and add new
* connection to the context by means of shttpd_add().
*/
void shttpd_add_socket(struct shttpd_ctx *, int sock, int is_ssl);
int shttpd_accept(int lsn_sock, int milliseconds);
int shttpd_active(struct shttpd_ctx *);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SHTTPD_HEADER_INCLUDED */

52
third-party/shttpd/ssl.h vendored Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
/*
* Snatched from OpenSSL includes. I put the prototypes here to be independent
* from the OpenSSL source installation. Having this, shttpd + SSL can be
* built on any system with binary SSL libraries installed.
*/
typedef struct ssl_st SSL;
typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_ctx_st SSL_CTX;
#define SSL_ERROR_WANT_READ 2
#define SSL_ERROR_WANT_WRITE 3
#define SSL_FILETYPE_PEM 1
/*
* Dynamically loaded SSL functionality
*/
struct ssl_func {
const char *name; /* SSL function name */
union variant ptr; /* Function pointer */
};
extern struct ssl_func ssl_sw[];
#define FUNC(x) ssl_sw[x].ptr.v_func
#define SSL_free(x) (* (void (*)(SSL *)) FUNC(0))(x)
#define SSL_accept(x) (* (int (*)(SSL *)) FUNC(1))(x)
#define SSL_connect(x) (* (int (*)(SSL *)) FUNC(2))(x)
#define SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) FUNC(3))((x),(y),(z))
#define SSL_write(x,y,z) \
(* (int (*)(SSL *, const void *,int)) FUNC(4))((x), (y), (z))
#define SSL_get_error(x,y)(* (int (*)(SSL *, int)) FUNC(5))((x), (y))
#define SSL_set_fd(x,y) (* (int (*)(SSL *, int)) FUNC(6))((x), (y))
#define SSL_new(x) (* (SSL * (*)(SSL_CTX *)) FUNC(7))(x)
#define SSL_CTX_new(x) (* (SSL_CTX * (*)(SSL_METHOD *)) FUNC(8))(x)
#define SSLv23_server_method() (* (SSL_METHOD * (*)(void)) FUNC(9))()
#define SSL_library_init() (* (int (*)(void)) FUNC(10))()
#define SSL_CTX_use_PrivateKey_file(x,y,z) (* (int (*)(SSL_CTX *, \
const char *, int)) FUNC(11))((x), (y), (z))
#define SSL_CTX_use_certificate_file(x,y,z) (* (int (*)(SSL_CTX *, \
const char *, int)) FUNC(12))((x), (y), (z))

154
third-party/shttpd/standalone.c vendored Normal file
View File

@@ -0,0 +1,154 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int exit_flag;
static void
signal_handler(int sig_num)
{
switch (sig_num) {
#ifndef _WIN32
case SIGCHLD:
while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
break;
#endif /* !_WIN32 */
default:
exit_flag = sig_num;
break;
}
}
void
process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
{
const char *config_file = CONFIG_FILE;
char line[BUFSIZ], opt[BUFSIZ],
val[BUFSIZ], path[FILENAME_MAX], *p;
FILE *fp;
size_t i, line_no = 0;
/* First find out, which config file to open */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
if (argv[i + 1] == NULL)
usage(argv[0]);
if (argv[i] != NULL && argv[i + 1] != NULL) {
/* More than one non-option arguments are given w*/
usage(argv[0]);
} else if (argv[i] != NULL) {
/* Just one non-option argument is given, this is config file */
config_file = argv[i];
} else {
/* No config file specified. Look for one where shttpd lives */
if ((p = strrchr(argv[0], DIRSEP)) != 0) {
my_snprintf(path, sizeof(path), "%.*s%s",
p - argv[0] + 1, argv[0], config_file);
config_file = path;
}
}
fp = fopen(config_file, "r");
/* If config file was set in command line and open failed, exit */
if (fp == NULL && argv[i] != NULL)
elog(E_FATAL, NULL, "cannot open config file %s: %s",
config_file, strerror(errno));
if (fp != NULL) {
elog(E_LOG, NULL, "Loading config file %s", config_file);
/* Loop over the lines in config file */
while (fgets(line, sizeof(line), fp) != NULL) {
line_no++;
/* Ignore empty lines and comments */
if (line[0] == '#' || line[0] == '\n')
continue;
if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
elog(E_FATAL, NULL, "line %d in %s is invalid",
line_no, config_file);
shttpd_set_option(ctx, opt, val);
}
(void) fclose(fp);
}
/* Now pass through the command line options */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
shttpd_set_option(ctx, &argv[i][1], argv[i + 1]);
}
int
main(int argc, char *argv[])
{
struct shttpd_ctx *ctx;
#if !defined(NO_AUTH)
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
if (argc != 6)
usage(argv[0]);
exit(edit_passwords(argv[2],argv[3],argv[4],argv[5]));
}
#endif /* NO_AUTH */
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
usage(argv[0]);
ctx = shttpd_init();
process_command_line_arguments(ctx, argv);
#ifndef _WIN32
/* Switch to alternate UID, it is safe now, after shttpd_listen() */
if (ctx->options[OPT_UID] != NULL) {
struct passwd *pw;
if ((pw = getpwnam(ctx->options[OPT_UID])) == NULL)
elog(E_FATAL, 0, "main: unknown user [%s]",
ctx->options[OPT_UID]);
else if (setgid(pw->pw_gid) == -1)
elog(E_FATAL, NULL, "main: setgid(%s): %s",
ctx->options[OPT_UID], strerror(errno));
else if (setuid(pw->pw_uid) == -1)
elog(E_FATAL, NULL, "main: setuid(%s): %s",
ctx->options[OPT_UID], strerror(errno));
}
(void) signal(SIGCHLD, signal_handler);
(void) signal(SIGPIPE, SIG_IGN);
#endif /* _WIN32 */
(void) signal(SIGTERM, signal_handler);
(void) signal(SIGINT, signal_handler);
if (IS_TRUE(ctx, OPT_INETD)) {
shttpd_set_option(ctx, "ports", NULL);
(void) freopen("/dev/null", "a", stderr);
shttpd_add_socket(ctx, fileno(stdin), 0);
}
elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
while (exit_flag == 0)
shttpd_poll(ctx, 5000);
elog(E_LOG, NULL, "%d requests %.2lf Mb in %.2lf Mb out. "
"Exit on signal %d", ctx->nrequests, (double) (ctx->in / 1048576),
(double) ctx->out / 1048576, exit_flag);
shttpd_fini(ctx);
return (EXIT_SUCCESS);
}

41
third-party/shttpd/std_includes.h vendored Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#ifndef STD_HEADERS_INCLUDED
#define STD_HEADERS_INCLUDED
#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#endif /* _WIN32_WCE */
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <wchar.h>
#if defined(_WIN32) /* Windows specific */
#include "compat_win32.h"
#elif defined(__rtems__) /* RTEMS specific */
#include "compat_rtems.h"
#else /* UNIX specific */
#include "compat_unix.h"
#endif /* _WIN32 */
#endif /* STD_HEADERS_INCLUDED */

95
third-party/shttpd/string.c vendored Normal file
View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
void
my_strlcpy(register char *dst, register const char *src, size_t n)
{
for (; *src != '\0' && n > 1; n--)
*dst++ = *src++;
*dst = '\0';
}
int
my_strncasecmp(const char *str1, const char *str2, size_t len)
{
register const unsigned char *s1 = (unsigned char *) str1,
*s2 = (unsigned char *) str2, *e;
int ret;
for (e = s1 + len - 1; s1 < e && *s1 != '\0' && *s2 != '\0' &&
tolower(*s1) == tolower(*s2); s1++, s2++) ;
ret = tolower(*s1) - tolower(*s2);
return (ret);
}
char *
my_strndup(const char *ptr, size_t len)
{
char *p;
if ((p = malloc(len + 1)) != NULL)
my_strlcpy(p, ptr, len + 1);
return (p);
}
char *
my_strdup(const char *str)
{
return (my_strndup(str, strlen(str)));
}
/*
* Sane snprintf(). Acts like snprintf(), but never return -1 or the
* value bigger than supplied buffer.
* Thanks Adam Zeldis to pointing snprintf()-caused vulnerability
* in his audit report.
*/
int
my_snprintf(char *buf, size_t buflen, const char *fmt, ...)
{
va_list ap;
int n;
if (buflen == 0)
return (0);
va_start(ap, fmt);
n = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
if (n < 0 || (size_t) n >= buflen)
n = buflen - 1;
buf[n] = '\0';
return (n);
}
/*
* Verify that given file has certain extension
*/
int
match_extension(const char *path, const char *ext_list)
{
size_t len, path_len;
path_len = strlen(path);
FOR_EACH_WORD_IN_LIST(ext_list, len)
if (len < path_len && path[path_len - len - 1] == '.' &&
!my_strncasecmp(path + path_len - len, ext_list, len))
return (TRUE);
return (FALSE);
}