This commit is contained in:
Ralf Corsepius
2010-06-09 08:06:08 +00:00
parent c7bdd7749c
commit dc35201765
36 changed files with 0 additions and 8068 deletions

View File

@@ -1,12 +0,0 @@
dnl $Id$
AC_DEFUN([RTEMS_ENABLE_SHTTPD],
[
AC_ARG_ENABLE([shttpd],
AS_HELP_STRING([--enable-shttpd],[enable (small httpd) shttpd (DEPRECATED)]),
[case "${enableval}" in
yes) enable_shttpd=yes ;;
no) enable_shttpd=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for enable-shttpd option) ;;
esac],[enable_shttpd=no])
])

View File

@@ -1,2 +0,0 @@
Makefile.in
Makefile

View File

@@ -1,70 +0,0 @@
# DEPRECATED, don't use.
include $(top_srcdir)/automake/compile.am
if LIBSHTTPD
include_shttpddir = $(includedir)/shttpd
project_lib_LIBRARIES = libshttpd.a
libshttpd_a_CPPFLAGS = $(AM_CPPFLAGS) -DHAVE_MD5
# HACK ALERT:
# prefix all non-public symbols with _shttp_
# FIXME: There must be something better than this
libshttpd_a_CPPFLAGS += -Dcheck_authorization=_shttpd_check_authorization
libshttpd_a_CPPFLAGS += -Ddecode_url_encoded_string=_shttpd_decode_url_encoded_string
libshttpd_a_CPPFLAGS += -Dedit_passwords=_shttpd_edit_passwords
libshttpd_a_CPPFLAGS += -Delog=_shttpd_elog
libshttpd_a_CPPFLAGS += -Dget_dir=_shttpd_get_dir
libshttpd_a_CPPFLAGS += -Dget_file=_shttpd_get_file
libshttpd_a_CPPFLAGS += -Dget_headers_len=_shttpd_get_headers_len
libshttpd_a_CPPFLAGS += -Dget_mime_type=_shttpd_get_mime_type
libshttpd_a_CPPFLAGS += -Dinit_from_argc_argv=_shttpd_init_from_argc_argv
libshttpd_a_CPPFLAGS += -Dis_authorized_for_put=_shttpd_is_authorized_for_put
libshttpd_a_CPPFLAGS += -Dis_registered_uri=_shttpd_is_registered_uri
libshttpd_a_CPPFLAGS += -Dlog_access=_shttpd_log_access
libshttpd_a_CPPFLAGS += -Dparse_headers=_shttpd_parse_headers
libshttpd_a_CPPFLAGS += -Dput_dir=_shttpd_put_dir
libshttpd_a_CPPFLAGS += -Dsend_authorization_request=_shttpd_send_authorization_request
libshttpd_a_CPPFLAGS += -Dsend_server_error=_shttpd_send_server_error
libshttpd_a_CPPFLAGS += -Dset_close_on_exec=_shttpd_set_close_on_exec
libshttpd_a_CPPFLAGS += -Dset_mime_types=_shttpd_set_mime_types
libshttpd_a_CPPFLAGS += -Dset_non_blocking_mode=_shttpd_set_non_blocking_mode
libshttpd_a_CPPFLAGS += -Dsetup_embedded_stream=_shttpd_setup_embedded_stream
libshttpd_a_CPPFLAGS += -Dstop_stream=_shttpd_stop_stream
libshttpd_a_CPPFLAGS += -Dusage=_shttpd_usage
libshttpd_a_CPPFLAGS += -Dknown_http_methods=_shttpd_known_http_methods
libshttpd_a_CPPFLAGS += -Doptions=_shttpd_options
libshttpd_a_CPPFLAGS += -Dio_file=_shttpd_io_file
libshttpd_a_CPPFLAGS += -Dio_socket=_shttpd_io_socket
libshttpd_a_CPPFLAGS += -Dio_embedded=_shttpd_io_embedded
libshttpd_a_CPPFLAGS += -Dio_dir=_shttpd_io_dir
libshttpd_a_CPPFLAGS += -Dio_cgi=_shttpd_io_cgi
libshttpd_a_CPPFLAGS += -Dcurrent_time=_shttpd_current_time
libshttpd_a_CPPFLAGS += -Dtz_offset=_shttpd_tz_offset
SRCS= string.c shttpd.c log.c auth.c md5.c \
cgi.c mime_type.c config.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
libshttpd_a_SOURCES = $(SRCS) $(HDRS)
libshttpd_a_SOURCES += compat_rtems.c
include_shttpd_HEADERS = shttpd.h
# 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)
# -DNO_GUI - Win32 only. Build console version, no GUI
# -DCONFIG=\"file\" - use `file' as the default config file
endif
include $(srcdir)/preinstall.am
include $(top_srcdir)/automake/local.am

View File

@@ -1,50 +0,0 @@
SRCS= string.c shttpd.c log.c auth.c md5.c \
cgi.c mime_type.c config.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
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)
# -DNO_GUI - Win32 only. Build console version, no GUI
# -DCONFIG=\"file\" - use `file' as the default config file
CL_FLAGS= /O1 /MD /TC /nologo /DNDEBUG
all:
@echo "make (unix|msvc|mingw|rtems)"
unix:
$(CC) -c $(CFLAGS) -DEMBEDDED $(SRCS) compat_unix.c
$(AR) -r lib$(PROG).a *.o && ranlib lib$(PROG).a
$(CC) $(CFLAGS) $(SRCS) compat_unix.c standalone.c -o $(PROG) $(LIBS)
rtems:
$(CC) -c $(CFLAGS) -DEMBEDDED $(SRCS) compat_rtems.c
$(AR) -r lib$(PROG).a *.o && ranlib lib$(PROG).a
msvc:
cl $(SRCS) /c $(CL_FLAGS) /DEMBEDDED
lib *.obj /out:shttpd.lib
rc resources.rc
cl $(SRCS) compat_win32.c standalone.c $(CL_FLAGS) \
/link resources.res /out:$(PROG).exe /machine:ix86
mingw:
$(CC) -c $(CFLAGS) -DEMBEDDED $(SRCS) compat_win32.c
$(AR) -r lib$(PROG).a *.o && ranlib lib$(PROG).a
windres resources.rc resources.o
$(CC) $(CFLAGS) $(SRCS) compat_win32.c standalone.c resources.o -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

View File

@@ -1,397 +0,0 @@
/*
* 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"
#include <unistd.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#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->global_passwd_file) {
/* Use global passwords file */
my_snprintf(name, sizeof(name), "%s", ctx->global_passwd_file);
} 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->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 authorized = 1;
#ifdef EMBEDDED
struct llhead *lp;
struct uri_auth *auth;
/* Check, is this URL protected by shttpd_protect_url() */
LL_FOREACH(&c->ctx->uri_auths, lp) {
auth = LL_ENTRY(lp, struct uri_auth, link);
if (!strncmp(c->uri, auth->uri, auth->uri_len)) {
fp = fopen(auth->file_name, "r");
break;
}
}
#endif /* EMBEDDED */
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->put_auth_file, "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->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 */

View File

@@ -1,292 +0,0 @@
/*
* 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 */
};
/*
* Verify that given file has CGI extension
*/
int
is_cgi(struct shttpd_ctx *ctx, const char *path)
{
size_t len, path_len;
const char *s = ctx->cgi_extensions;
path_len = strlen(path);
FOR_EACH_WORD_IN_LIST(s, len)
if (len < path_len &&
!my_strncasecmp(path + path_len - len, s, len))
return (1);
return (0);
}
/*
* 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;
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->ctx->port);
addenv(blk, "SERVER_NAME=%s", c->ctx->auth_realm);
addenv(blk, "SERVER_ROOT=%s", c->ctx->document_root);
addenv(blk, "DOCUMENT_ROOT=%s", c->ctx->document_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(c->ctx->document_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->cgi_vars;
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);
/* 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 */

View File

@@ -1,200 +0,0 @@
/**********************************************************************
*
* rtems shttpd management
*
* FILE NAME : rtems_shttpd.c
*
* AUTHOR : Steven Johnson
*
* DESCRIPTION : Defines the interface functions to the shttp daemon
*
* REVISION : $Id$
*
* 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;
unsigned int port;
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, httpd_args->port);
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,
size_t stack_size,
rtems_mode initial_modes,
rtems_attribute attribute_set,
rtems_shttpd_init init_callback,
rtems_shttpd_addpages addpages_callback,
char *webroot,
unsigned int port
)
{
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;
args->port = port;
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);
}
#ifndef __rtems__
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));
}
#endif
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

View File

@@ -1,69 +0,0 @@
/**
* @file rtems/rtems-shttpd.h
*/
#ifndef _rtems_rtems_webserver_h
#define _rtems_rtems_webserver_h
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "shttpd.h"
#include <rtems.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <dirent.h>
#include <arpa/inet.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 IS_DIRSEP_CHAR(c) ((c) == '/')
#define O_BINARY 0
#define closesocket(a) close(a)
#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,
size_t stack_size,
rtems_mode initial_modes,
rtems_attribute attribute_set,
rtems_shttpd_init init_callback,
rtems_shttpd_addpages addpages_callback,
char *webroot,
unsigned int port
);
void rtems_terminate_webserver(void);
int rtems_webserver_ok(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,123 +0,0 @@
/*
* 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;
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);
/* Execute CGI program */
if (c->ctx->cgi_interpreter == NULL) {
(void) execle(prog, prog, NULL, envp);
elog(E_FATAL, c, "redirect: exec(%s)", prog);
} else {
(void) execle(c->ctx->cgi_interpreter,
c->ctx->cgi_interpreter, prog, NULL, envp);
elog(E_FATAL, c, "redirect: exec(%s %s)",
c->ctx->cgi_interpreter, prog);
}
/* UNREACHED */
ret = -1;
exit(EXIT_FAILURE);
} else {
/* Parent */
ret = 0;
(void) closesocket(sock);
}
return (ret);
}
#endif /* !NO_CGI */

View File

@@ -1,33 +0,0 @@
/*
* 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)

View File

@@ -1,923 +0,0 @@
/*
* 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 const char *config_file = CONFIG;
#if !defined(NO_GUI)
static HICON hIcon; /* SHTTPD icon handle */
HWND hLog; /* Log window */
/*
* Dialog box control IDs
*/
#define ID_GROUP 100
#define ID_SAVE 101
#define ID_STATUS 102
#define ID_STATIC 103
#define ID_SETTINGS 104
#define ID_QUIT 105
#define ID_TRAYICON 106
#define ID_TIMER 107
#define ID_ICON 108
#define ID_ADVANCED 109
#define ID_SHOWLOG 110
#define ID_LOG 111
#define ID_USER 200
#define ID_DELTA 1000
static void
run_server(void *param)
{
struct shttpd_ctx *ctx = param;
if (shttpd_listen(ctx, ctx->port) == -1)
elog(E_FATAL, NULL, "Cannot open socket on port %d", ctx->port);
while (WaitForSingleObject(ctx->ev[0], 0) != WAIT_OBJECT_0)
shttpd_poll(ctx, 1000);
SetEvent(ctx->ev[1]);
shttpd_fini(ctx);
}
/*
* Save the configuration back into config file
*/
static void
save_config(HWND hDlg, FILE *fp)
{
const struct opt *opt;
char text[FILENAME_MAX];
int id;
if (fp == NULL)
elog(E_FATAL, NULL, "save_config: cannot open %s", config_file);
for (opt = options; opt->name != NULL; opt++) {
id = ID_USER + (opt - options); /* Control ID */
/* Do not save if the text is the same as default */
if (opt->flags & OPT_BOOL)
(void) fprintf(fp, "%s\t%d\n",
opt->name, IsDlgButtonChecked(hDlg, id));
else if (GetDlgItemText(hDlg, id, text, sizeof(text)) != 0 &&
(opt->def == NULL || strcmp(text, opt->def) != 0))
(void) fprintf(fp, "%s\t%s\n", opt->name, text);
}
(void) fclose(fp);
}
static void
set_control_values(HWND hDlg, const struct shttpd_ctx *ctx)
{
const struct opt *opt;
const union variant *v;
char buf[FILENAME_MAX];
int id;
for (opt = options; opt->name != NULL; opt++) {
id = ID_USER + (opt - options);
v = (union variant *) ((char *) ctx + opt->ofs);
if (opt->flags & OPT_BOOL) {
CheckDlgButton(hDlg, id,
v->v_int ? BST_CHECKED : BST_UNCHECKED);
} else if (opt->flags & OPT_INT) {
my_snprintf(buf, sizeof(buf), "%d", v->v_int);
SetDlgItemText(hDlg, id, buf);
} else {
SetDlgItemText(hDlg, id, v->v_str);
}
}
}
static BOOL CALLBACK
DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
static struct shttpd_ctx *ctx, **pctx;
HANDLE ev;
const struct opt *opt;
DWORD tid;
int id, up;
char text[256];
switch (msg) {
case WM_CLOSE:
KillTimer(hDlg, ID_TIMER);
DestroyWindow(hDlg);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_SAVE:
EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
save_config(hDlg, fopen(config_file, "w+"));
ev = ctx->ev[1];
SetEvent(ctx->ev[0]);
WaitForSingleObject(ev, INFINITE);
*pctx = ctx = init_from_argc_argv(config_file, 0, NULL);
shttpd_listen(ctx, ctx->port);
_beginthread(run_server, 0, ctx);
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
break;
}
id = ID_USER + ID_DELTA;
for (opt = options; opt->name != NULL; opt++, id++)
if (LOWORD(wParam) == id) {
OPENFILENAME of;
BROWSEINFO bi;
char path[FILENAME_MAX] = "";
memset(&of, 0, sizeof(of));
of.lStructSize = sizeof(of);
of.hwndOwner = (HWND) hDlg;
of.lpstrFile = path;
of.nMaxFile = sizeof(path);
of.lpstrInitialDir = ctx->document_root;
of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
memset(&bi, 0, sizeof(bi));
bi.hwndOwner = (HWND) hDlg;
bi.lpszTitle = "Choose WWW root directory:";
bi.ulFlags = BIF_RETURNONLYFSDIRS;
if (opt->flags & OPT_DIR)
SHGetPathFromIDList(
SHBrowseForFolder(&bi), path);
else
GetOpenFileName(&of);
if (path[0] != '\0')
SetWindowText(GetDlgItem(hDlg,
id - ID_DELTA), path);
}
break;
case WM_INITDIALOG:
pctx = (struct shttpd_ctx **) lParam;
ctx = *pctx;
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
SetWindowText(hDlg, "SHTTPD settings");
SetFocus(GetDlgItem(hDlg, ID_SAVE));
set_control_values(hDlg, ctx);
break;
default:
break;
}
return FALSE;
}
static void *
align(void *ptr, DWORD alig)
{
ULONG ul = (ULONG) ptr;
ul += alig;
ul &= ~alig;
return ((void *) ul);
}
static void
add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type, DWORD id,
DWORD style, WORD x, WORD y, WORD cx, WORD cy, const char *caption)
{
DLGITEMTEMPLATE *tp;
LPWORD p;
dia->cdit++;
*mem = align(*mem, 3);
tp = (DLGITEMTEMPLATE *) *mem;
tp->id = (WORD)id;
tp->style = style;
tp->dwExtendedStyle = 0;
tp->x = x;
tp->y = y;
tp->cx = cx;
tp->cy = cy;
p = align(*mem + sizeof(*tp), 1);
*p++ = 0xffff;
*p++ = type;
while (*caption != '\0')
*p++ = (WCHAR) *caption++;
*p++ = 0;
p = align(p, 1);
*p++ = 0;
*mem = (unsigned char *) p;
}
static void
show_settings_dialog(struct shttpd_ctx **ctxp)
{
#define HEIGHT 15
#define WIDTH 400
#define LABEL_WIDTH 70
unsigned char mem[4096], *p;
DWORD style;
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
WORD cl, x, y, width, nelems = 0;
const struct opt *opt;
static int guard;
static struct {
DLGTEMPLATE template; /* 18 bytes */
WORD menu, class;
wchar_t caption[1];
WORD fontsiz;
wchar_t fontface[7];
} dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
0, 200, 200, WIDTH, 0}, 0, 0, L"", 8, L"Tahoma"};
if (guard == 0)
guard++;
else
return;
(void) memset(mem, 0, sizeof(mem));
(void) memcpy(mem, &dialog_header, sizeof(dialog_header));
p = mem + sizeof(dialog_header);
for (opt = options; opt->name != NULL; opt++) {
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
x = 10 + (WIDTH / 2) * (nelems % 2);
y = (nelems/2 + 1) * HEIGHT + 5;
width = WIDTH / 2 - 20 - LABEL_WIDTH;
if (opt->flags & OPT_INT) {
style |= ES_NUMBER;
cl = 0x81;
style |= WS_BORDER | ES_AUTOHSCROLL;
} else if (opt->flags & OPT_BOOL) {
cl = 0x80;
style |= BS_AUTOCHECKBOX;
} else if (opt->flags & (OPT_DIR | OPT_FILE)) {
style |= WS_BORDER | ES_AUTOHSCROLL;
width -= 20;
cl = 0x81;
add_control(&p, dia, 0x80,
ID_USER + ID_DELTA + (opt - options),
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
(WORD) (x + width + LABEL_WIDTH + 5),
y, 15, 12, "...");
} else {
cl = 0x81;
style |= WS_BORDER | ES_AUTOHSCROLL;
}
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
x, y, LABEL_WIDTH, HEIGHT, opt->desc);
add_control(&p, dia, cl, ID_USER + (opt - options), style,
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
nelems++;
}
y = (WORD) (((nelems + 1)/2 + 1) * HEIGHT + 5);
add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
BS_GROUPBOX, 5, 5, WIDTH - 10, y, "Settings");
y += 10;
add_control(&p, dia, 0x80, ID_SAVE,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
WIDTH - 70, y, 65, 12, "Save Settings");
#if 0
add_control(&p, dia, 0x80, ID_ADVANCED,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
WIDTH - 190, y, 110, 12, "Show Advanced Settings >>");
#endif
add_control(&p, dia, 0x82, ID_STATIC,
WS_CHILD | WS_VISIBLE | WS_DISABLED,
5, y, 180, 12,"SHTTPD v." VERSION
" (http://shttpd.sourceforge.net)");
dia->cy = ((nelems + 1)/2 + 1) * HEIGHT + 30;
DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) ctxp);
guard--;
}
static BOOL CALLBACK
LogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
static struct shttpd_ctx *ctx;
static HWND hStatus;
HWND hEdit;
RECT rect, rect2, rect3, rect4;
int len, up, widths[] = {120, 220, 330, 460, -1};
char text[256], buf[1024 * 64];
switch (msg) {
case WM_CLOSE:
KillTimer(hDlg, ID_TIMER);
DestroyWindow(hDlg);
break;
case WM_APP:
hEdit = GetDlgItem(hDlg, ID_LOG);
len = GetWindowText(hEdit, buf, sizeof(buf));
if (len > sizeof(buf) * 4 / 5)
len = sizeof(buf) * 4 / 5;
my_snprintf(buf + len, sizeof(buf) - len,
"%s\r\n", (char *) lParam);
SetWindowText(hEdit, buf);
SendMessage(hEdit, WM_VSCROLL, SB_BOTTOM, 0);
break;
case WM_TIMER:
/* Print statistics on a status bar */
up = current_time - ctx->start_time;
(void) my_snprintf(text, sizeof(text),
" Up: %3d h %2d min %2d sec",
up / 3600, up / 60 % 60, up % 60);
SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM) text);
(void) my_snprintf(text, sizeof(text),
" Requests: %u", ctx->nrequests);
SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM) text);
(void) my_snprintf(text, sizeof(text),
" Sent: %4.2f Mb", (double) ctx->out / 1048576);
SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM) text);
(void) my_snprintf(text, sizeof(text),
" Received: %4.2f Mb", (double) ctx->in / 1048576);
SendMessage(hStatus, SB_SETTEXT, 3, (LPARAM) text);
break;
case WM_INITDIALOG:
ctx = (struct shttpd_ctx *) lParam;
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
hStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE,
"", hDlg, ID_STATUS);
SendMessage(hStatus, SB_SETPARTS, 5, (LPARAM) widths);
SendMessage(hStatus, SB_SETTEXT, 4, (LPARAM) " Running");
SetWindowText(hDlg, "SHTTPD web server log");
SetTimer(hDlg, ID_TIMER, 1000, NULL);
GetWindowRect(GetDesktopWindow(), &rect3);
GetWindowRect(hDlg, &rect4);
GetClientRect(hDlg, &rect);
GetClientRect(hStatus, &rect2);
SetWindowPos(GetDlgItem(hDlg, ID_LOG), 0,
0, 0, rect.right, rect.bottom - rect2.bottom, 0);
SetWindowPos(hDlg, HWND_TOPMOST,
rect3.right - (rect4.right - rect4.left),
rect3.bottom - (rect4.bottom - rect4.top) - 30,
0, 0, SWP_NOSIZE);
SetFocus(hStatus);
SendMessage(hDlg, WM_TIMER, 0, 0);
hLog = hDlg;
break;
default:
break;
}
return (FALSE);
}
static void
show_log_window(struct shttpd_ctx *ctx)
{
unsigned char mem[4096], *p;
DWORD style;
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
WORD cl, x, y, width, nelems = 0;
static struct {
DLGTEMPLATE template; /* 18 bytes */
WORD menu, class;
wchar_t caption[1];
WORD fontsiz;
wchar_t fontface[7];
} dialog_header = {{WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_SYSMENU |
DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
0, 200, 200, 400, 100}, 0, 0, L"", 8, L"Tahoma"};
if (hLog != NULL)
return;
(void) memset(mem, 0, sizeof(mem));
(void) memcpy(mem, &dialog_header, sizeof(dialog_header));
p = mem + sizeof(dialog_header);
add_control(&p, dia, 0x81, ID_LOG, WS_CHILD | WS_VISIBLE |
WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL |
ES_READONLY, 5, 5, WIDTH - 10, 60, "");
DialogBoxIndirectParam(NULL, dia, NULL, LogProc, (LPARAM) ctx);
hLog = NULL;
}
static LRESULT CALLBACK
WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static NOTIFYICONDATA ni;
static struct shttpd_ctx *ctx;
DWORD tid; /* Thread ID */
HMENU hMenu;
POINT pt;
switch (msg) {
case WM_CREATE:
ctx = ((CREATESTRUCT *) lParam)->lpCreateParams;
memset(&ni, 0, sizeof(ni));
ni.cbSize = sizeof(ni);
ni.uID = ID_TRAYICON;
ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
ni.hIcon = hIcon;
ni.hWnd = hWnd;
my_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
ni.uCallbackMessage = WM_USER;
Shell_NotifyIcon(NIM_ADD, &ni);
ctx->ev[0] = CreateEvent(0, TRUE, FALSE, 0);
ctx->ev[1] = CreateEvent(0, TRUE, FALSE, 0);
_beginthread(run_server, 0, ctx);
break;
case WM_CLOSE:
Shell_NotifyIcon(NIM_DELETE, &ni);
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_SETTINGS:
show_settings_dialog(&ctx);
break;
case ID_QUIT:
SendMessage(hWnd, WM_CLOSE, wParam, lParam);
PostQuitMessage(0);
break;
case ID_SHOWLOG:
show_log_window(ctx);
break;
}
break;
case WM_USER:
switch (lParam) {
case WM_RBUTTONUP:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
hMenu = CreatePopupMenu();
AppendMenu(hMenu, 0, ID_SETTINGS, "Settings");
AppendMenu(hMenu, 0, ID_SHOWLOG, "Show Log");
AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
GetCursorPos(&pt);
TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
DestroyMenu(hMenu);
break;
}
break;
}
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
int WINAPI
WinMain(HINSTANCE h, HINSTANCE prev, char *cmdline, int show)
{
struct shttpd_ctx *ctx;
WNDCLASS cls;
HWND hWnd;
MSG msg;
ctx = init_from_argc_argv(config_file, 0, NULL);
(void) memset(&cls, 0, sizeof(cls));
hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON));
if (hIcon == NULL)
hIcon = LoadIcon(NULL, IDI_APPLICATION);
cls.lpfnWndProc = (WNDPROC) WindowProc;
cls.hIcon = hIcon;
cls.lpszClassName = "shttpd v." VERSION;
if (!RegisterClass(&cls))
elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
else if ((hWnd = CreateWindow(cls.lpszClassName, "",WS_OVERLAPPEDWINDOW,
0, 0, 0, 0, NULL, NULL, NULL, ctx)) == NULL)
elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (0);
}
#endif /* NO_GUI */
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);
}
}
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));
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->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->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 */

View File

@@ -1,93 +0,0 @@
/*
* 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
#ifdef _MSC_VER /* pragmas not valid on MinGW */
#pragma comment(lib,"ws2_32")
#pragma comment(lib,"user32")
#pragma comment(lib,"comctl32")
#pragma comment(lib,"comdlg32")
#pragma comment(lib,"shell32")
#ifdef NO_GUI
#pragma comment(linker,"/subsystem:console")
#else
#pragma comment(linker,"/subsystem:windows")
#endif /* NO_GUI */
#endif /* _MSC_VER */
#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 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);

File diff suppressed because it is too large Load Diff

View File

@@ -1,145 +0,0 @@
#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 */

View File

@@ -1,334 +0,0 @@
/*
* 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"
#include <unistd.h>
/*
* Configuration parameters setters
*/
static void
set_int(struct shttpd_ctx *ctx, void *ptr, const char *string)
{
ctx = NULL; /* Unused */
* (int *) ptr = atoi(string);
}
static void
set_str(struct shttpd_ctx *ctx, void *ptr, const char *string)
{
ctx = NULL; /* Unused */
* (char **) ptr = my_strdup(string);
}
static void
set_log_file(struct shttpd_ctx *ctx, void *ptr, const char *string)
{
FILE **fp = ptr;
ctx = NULL;
if ((*fp = fopen(string, "a")) == NULL)
elog(E_FATAL, NULL, "cannot open log file %s: %s",
string, strerror(errno));
}
#ifndef NO_SSL
/*
* Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
*/
static void
set_ssl(struct shttpd_ctx *ctx, void *arg, const char *pem)
{
SSL_CTX *CTX;
void *lib;
struct ssl_func *fp;
arg = NULL; /* Unused */
/* 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
set_mime(struct shttpd_ctx *ctx, void *arg, const char *string)
{
arg = NULL;
set_mime_types(ctx, string);
}
#define OFS(x) offsetof(struct shttpd_ctx, x)
#define BOOL_OPT "0|1"
const struct opt options[] = {
{'d', "document_root", "Web root directory", set_str,
OFS(document_root), "directory", NULL, OPT_DIR},
{'i', "index_files", "Index files", set_str, OFS(index_files),
"file_list", INDEX_FILES, OPT_ADVANCED},
{'p', "listen_port", "Listening port", set_int,
OFS(port), "port", HTTP_PORT, OPT_INT | OPT_ADVANCED},
{'D', "list_directories", "Directory listing", set_int,
OFS(dirlist), BOOL_OPT, "1", OPT_BOOL | OPT_ADVANCED},
#ifndef NO_CGI
{'c', "cgi_extensions", "CGI extensions", set_str,
OFS(cgi_extensions), "ext_list", CGI_EXT, OPT_ADVANCED},
{'C', "cgi_interpreter", "CGI interpreter", set_str,
OFS(cgi_interpreter), "file", NULL, OPT_FILE | OPT_ADVANCED},
{'V', "cgi_envvar", "CGI envir variables", set_str,
OFS(cgi_vars), "X=Y,....", NULL, OPT_ADVANCED},
#endif /* NO_CGI */
{'N', "auth_realm", "Authentication realm", set_str,
OFS(auth_realm), "auth_realm", REALM, OPT_ADVANCED},
{'l', "access_log", "Access log file", set_log_file,
OFS(access_log), "file", NULL, OPT_FILE | OPT_ADVANCED},
{'e', "error_log", "Error log file", set_log_file,
OFS(error_log), "file", NULL, OPT_FILE | OPT_ADVANCED},
{'m', "mime_types", "Mime types file", set_mime,
OFS(mime_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
{'P', "global_htpasswd", "Global passwords file", set_str,
OFS(global_passwd_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
#ifndef NO_SSL
{'s', "ssl_certificate", "SSL certificate file", set_ssl,
OFS(ssl_ctx), "pem_file", NULL, OPT_FILE | OPT_ADVANCED},
#endif /* NO_SSL */
{'U', "put_auth", "PUT,DELETE auth file",set_str,
OFS(put_auth_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
{'a', "aliases", "Aliases", set_str,
OFS(aliases), "X=Y,...", NULL, OPT_ADVANCED},
{'b', "io_buf_size", "IO buffer size", set_int, OFS(io_buf_size),
"bytes", DFLT_IO_SIZ, OPT_INT | OPT_ADVANCED},
#ifdef _WIN32
{'S', "auto_start", "Autostart with Windows", set_int,
OFS(auto_start), BOOL_OPT, "1", OPT_BOOL},
#else
{'I', "inetd_mode", "Inetd mode", set_int,
OFS(inetd_mode), BOOL_OPT, NULL, OPT_BOOL },
{'u', "runtime_uid", "Run as user", set_str,
OFS(uid), "user_name", NULL, 0 },
#endif /* _WIN32 */
{0, NULL, NULL, NULL, 0, NULL, NULL, 0 }
};
static const struct opt *
find_option(int sw, const char *name)
{
const struct opt *opt;
for (opt = options; opt->sw != 0; opt++)
if (sw == opt->sw || (name && strcmp(opt->name, name) == 0))
return (opt);
return (NULL);
}
static void
set_option(const struct opt *opt, const char *val, char **tmpvars)
{
tmpvars += opt - options;
if (*tmpvars != NULL)
free(*tmpvars);
*tmpvars = my_strdup(val);
}
/*
* Initialize shttpd context
*/
static void
initialize_context(struct shttpd_ctx *ctx, const char *config_file,
int argc, char *argv[], char **tmpvars)
{
char line[FILENAME_MAX], root[FILENAME_MAX],
var[sizeof(line)], val[sizeof(line)];
const char *arg;
size_t i;
const struct opt *opt;
FILE *fp;
struct tm *tm;
current_time = time(NULL);
tm = localtime(&current_time);
tz_offset = 0;
#if 0
tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
#endif
(void) memset(ctx, 0, sizeof(*ctx));
ctx->start_time = current_time;
InitializeCriticalSection(&ctx->mutex);
LL_INIT(&ctx->connections);
LL_INIT(&ctx->mime_types);
LL_INIT(&ctx->registered_uris);
LL_INIT(&ctx->uri_auths);
LL_INIT(&ctx->error_handlers);
/* First pass: set the defaults */
for (opt = options; opt->sw != 0; opt++)
if (opt->def != NULL)
tmpvars[opt - options] = my_strdup(opt->def);
/* Second pass: load config file */
if (config_file != NULL && (fp = fopen(config_file, "r")) != NULL) {
DBG(("init_ctx: config file %s", config_file));
/* Loop through the lines in config file */
while (fgets(line, sizeof(line), fp) != NULL) {
/* Skip comments and empty lines */
if (line[0] == '#' || line[0] == '\n')
continue;
/* Trim trailing newline character */
line[strlen(line) - 1] = '\0';
if (sscanf(line, "%s %[^#\n]", var, val) != 2)
elog(E_FATAL,0,"init_ctx: bad line: [%s]",line);
if ((opt = find_option(0, var)) == NULL)
elog(E_FATAL, NULL,
"set_option: unknown variable [%s]", var);
set_option(opt, val, tmpvars);
}
(void) fclose(fp);
}
/* Third pass: process command line args */
for (i = 1; i < (size_t) argc && argv[i][0] == '-'; i++)
if ((opt = find_option(argv[i][1], NULL)) != NULL) {
arg = argv[i][2] ? &argv[i][2] : argv[++i];
if (arg == NULL)
usage(argv[0]);
set_option(opt, arg, tmpvars);
} else {
usage(argv[0]);
}
/* Call setters functions now */
for (i = 0; i < NELEMS(options); i++)
if (tmpvars[i] != NULL) {
options[i].setter(ctx,
((char *) ctx) + options[i].ofs, tmpvars[i]);
free(tmpvars[i]);
}
#ifndef NO_SSL
/* If SSL is wanted and port is not overridden, set it to 443 */
if (ctx->port == atoi(HTTP_PORT) && ctx->ssl_ctx != NULL)
ctx->port = 443;
#endif /* NO_SSL */
/* If document_root is not set, set it to current directory */
if (ctx->document_root == NULL) {
(void) my_getcwd(root, sizeof(root));
ctx->document_root = my_strdup(root);
}
#ifdef _WIN32
{WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
#endif /* _WIN32 */
DBG(("init_ctx: initialized context %p", (void *) ctx));
}
/*
* Show usage string and exit.
*/
void
usage(const char *prog)
{
const struct opt *opt;
(void) fprintf(stderr,
"SHTTPD version %s (c) Sergey Lyubka\n"
"usage: %s [OPTIONS] [config_file]\n"
"Note: config line keyword for every option is in the "
"round brackets\n", VERSION, prog);
#if !defined(NO_AUTH)
(void) fprintf(stderr, "-A <htpasswd_file> <realm> <user> <passwd>\n");
#endif /* NO_AUTH */
for (opt = options; opt->name != NULL; opt++)
(void) fprintf(stderr, "-%c <%s>\t\t%s (%s)\n",
opt->sw, opt->arg, opt->desc, opt->name);
exit(EXIT_FAILURE);
}
struct shttpd_ctx *
init_from_argc_argv(const char *config_file, int argc, char *argv[])
{
struct shttpd_ctx *ctx;
char *tmpvars[NELEMS(options)];
size_t i;
/* Initialize all temporary holders to NULL */
for (i = 0; i < NELEMS(tmpvars); i++)
tmpvars[i] = NULL;
if ((ctx = malloc(sizeof(*ctx))) != NULL)
initialize_context(ctx, config_file, argc, argv, tmpvars);
return (ctx);
}
struct shttpd_ctx *
shttpd_init(const char *config_file, ...)
{
struct shttpd_ctx *ctx;
va_list ap;
const char *opt_name, *opt_value;
char *tmpvars[NELEMS(options)];
const struct opt *opt;
size_t i;
/* Initialize all temporary holders to NULL */
for (i = 0; i < NELEMS(tmpvars); i++)
tmpvars[i] = NULL;
if ((ctx = malloc(sizeof(*ctx))) != NULL) {
va_start(ap, config_file);
while ((opt_name = va_arg(ap, const char *)) != NULL) {
opt_value = va_arg(ap, const char *);
if ((opt = find_option(0, opt_name)) == NULL)
elog(E_FATAL, NULL, "shttpd_init: "
"unknown variable [%s]", opt_name);
set_option(opt, opt_value, tmpvars);
}
va_end(ap);
initialize_context(ctx, config_file, 0, NULL, tmpvars);
}
return (ctx);
}

View File

@@ -1,492 +0,0 @@
/*
* 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"
#define VERSION "1.37" /* Version */
#ifndef CONFIG
#define CONFIG "shttpd.conf" /* Configuration file */
#endif /* CONFIG */
#define HTPASSWD ".htpasswd" /* Passwords file name */
#define DFLT_IO_SIZ "16384" /* Default max request size */
#define HTTP_PORT "80" /* Default listening port */
#define INDEX_FILES "index.html index.htm index.php index.cgi"
#define CGI_EXT ".cgi .pl .php" /* Default CGI 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 */
#ifdef __rtems__
#if defined(__SIZEOF_SIZE_T__) && (__SIZEOF_SIZE_T__ <= 2)
/* HACK: Reduce the array size on targets with 16bit size_t */
# if defined(__AVR__)
/* FIXME: 1500 is sufficient to avoid compilation breakdown. */
# define URI_MAX (32767-1500)
# elif defined(__m32c__)
/* FIXME: 1500 is sufficient to avoid compilation breakdown. */
# define URI_MAX (32767-1500)
# else
/* Theoretically, this should work on all targets with 16bit size_t
* In practice, it trips over other compiler limitations. */
# define URI_MAX 32767
# endif
#else // __SIZEOF_SIZE_T__ > 2
/* HACK: 32768 is 1 too much to fit into 16bit array indices. */
#define URI_MAX 32767 /* Maximum URI size */
#endif
#else // __rtems__
#define URI_MAX 32768 /* Maximum URI size */
#endif
#define MIN_REQ_LEN 16 /* "GET / HTTP/1.1\n\n" */
#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;
};
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 structure is used to hold mime types and associated file extensions.
*/
struct mime_type {
const char *ext;
int ext_len;
const char *mime;
};
struct mime_type_link {
struct llhead link;
char *ext;
int ext_len;
char *mime;
};
/*
* 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 bind a passwords file to any URI. This makes that URI password
* protected: anybody who accesses that URI will be asked to authorize.
*/
struct uri_auth {
struct llhead link;
const char *uri;
const char *file_name;
size_t uri_len;
};
/*
* 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 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 */
const char *mime_type; /* Mime type */
struct headers ch; /* Parsed client headers */
struct stream loc; /* Local stream */
struct stream rem; /* Remote stream */
};
/*
* 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 */
#if !defined(NO_SSL)
SSL_CTX *ssl_ctx; /* SSL context */
#endif /* NO_SSL */
struct llhead connections; /* List of connections */
struct llhead mime_types; /* Known mime types */
struct llhead registered_uris;/* User urls */
struct llhead uri_auths; /* User auth files */
struct llhead error_handlers; /* Embedded error handlers */
FILE *access_log; /* Access log stream */
FILE *error_log; /* Error log stream */
char *put_auth_file; /* PUT auth file */
char *document_root; /* Document root */
char *index_files; /* Index files */
char *aliases; /* Aliases */
char *mime_file; /* Mime types file */
#if !defined(NO_CGI)
char *cgi_vars; /* CGI environment variables */
char *cgi_extensions; /* CGI extensions */
char *cgi_interpreter; /* CGI script interpreter */
#endif /* NO_CGI */
char *auth_realm; /* Auth realm */
char *global_passwd_file; /* Global passwords file */
char *uid; /* Run as user */
int port; /* Listening port */
int dirlist; /* Directory listing */
int gui; /* Show GUI flag */
int auto_start; /* Start on OS boot */
int io_buf_size; /* IO buffer size */
int inetd_mode; /* Inetd flag */
#if defined(_WIN32)
CRITICAL_SECTION mutex; /* For MT case */
HANDLE ev[2]; /* For thread synchronization */
#elif defined(__rtems__)
rtems_id mutex;
#endif /* _WIN32 */
};
struct listener {
struct llhead link;
struct shttpd_ctx *ctx; /* Context that socket belongs */
int sock; /* Listening socket */
};
/* Option setter function */
typedef void (*optset_t)(struct shttpd_ctx *, void *ptr, const char *string);
struct opt {
int sw; /* Command line switch */
const char *name; /* Option name in config file */
const char *desc; /* Description */
optset_t setter; /* Option setter function */
size_t ofs; /* Value offset in context */
const char *arg; /* Argument format */
const char *def; /* Default option value */
unsigned int flags; /* Flags */
#define OPT_BOOL 1
#define OPT_INT 2
#define OPT_FILE 4
#define OPT_DIR 8
#define OPT_ADVANCED 16
};
extern const struct opt options[];
/*
* 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)
*/
#define FOR_EACH_WORD_IN_LIST(s,len) \
for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0; s += len + 1)
/*
* 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 void decode_url_encoded_string(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);
/*
* mime_type.c
*/
extern const char *get_mime_type(struct shttpd_ctx *, const char *uri, int len);
extern void set_mime_types(struct shttpd_ctx *ctx, const char *path);
/*
* config.c
*/
extern void usage(const char *prog);
extern struct shttpd_ctx *init_from_argc_argv(const char *, int, char *[]);
/*
* 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
*/
#ifndef HAVE_STRLCPY
extern void my_strlcpy(register char *, register const char *, size_t);
#else
#include <string.h>
#define my_strlcpy(d,s,l) strlcpy(d,s,l)
#endif
#ifndef HAVE_STRNCASECMP
extern int my_strncasecmp(register const char *,
register const char *, size_t);
#else
#ifdef __rtems__
/* strncasecmp should be in strings.h, but newlib has it in <string.h> */
#include <string.h>
#else
#include <strings.h>
#endif
#define my_strncasecmp(s1,s2,l) strncasecmp(s1,s2,l)
#endif
#ifndef HAVE_STRNDUP
extern char *my_strndup(const char *ptr, size_t len);
#else
#include <string.h>
#define my_strndup(x,l) strndup((x),(l))
#endif
#ifndef HAVE_STRDUP
extern char *my_strdup(const char *str);
#else
#include <string.h>
#define my_strdup(x) strdup(x)
#endif
extern int my_snprintf(char *buf, size_t buflen, const char *fmt, ...);
/*
* compat_*.c
*/
extern void set_close_on_exec(int fd);
extern int set_non_blocking_mode(int fd);
#if __rtems__
#define my_stat stat
#define my_open open
#define my_remove remove
#define my_rename rename
#define my_mkdir mkdir
#define my_getcwd getcwd
#else
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);
#endif
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 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);
/*
* 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 is_cgi(struct shttpd_ctx *ctx, const char *path);
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 */

View File

@@ -1,97 +0,0 @@
/*
* 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 */

View File

@@ -1,124 +0,0 @@
/*
* 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"
#include <unistd.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
};

View File

@@ -1,143 +0,0 @@
/*
* 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 < ((size_t)1024 * (size_t)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"
"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
};

View File

@@ -1,305 +0,0 @@
/*
* 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"
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#if defined(EMBEDDED)
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;
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 = io_space(io) + arg->out.num_bytes;
int buflen, len = 0;
va_list ap;
assert(buf <= io->buf + io->size);
if ((buflen = (io->buf + io->size) - buf) > 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);
}
}
#if 0
struct shttpd_ctx *
shttpd_init2(const char *config_file, char *names[], char *values[], size_t n)
{
size_t i;
for (i = 0; i < n; i++)
set_option(names[i], values[i]);
return (init_ctx(config_file, 0, NULL));
}
#endif
void
shttpd_protect_uri(struct shttpd_ctx *ctx, const char *uri, const char *file)
{
struct uri_auth *auth;
if ((auth = malloc(sizeof(*auth))) != NULL) {
auth->uri = my_strdup(uri);
auth->file_name = my_strdup(file);
auth->uri_len = strlen(uri);
LL_ADD(&ctx->uri_auths, &auth->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 - var_len;
/* buf is "var1=val1&var2=val2...". Find variable first */
for (p = buf; p < e; p++)
if (!my_strncasecmp(var, p, var_len) && p[var_len] == '=') {
/* Found. Shift to variable value */
e = buf + buf_len;
p += var_len + 1;
/* Find where the value ends */
if ((s = memchr(p, '&', e - p)) == NULL)
s = e;
/* Copy value into the buffer, decoding it */
decode_url_encoded_string(p, s - p, value, value_len);
return (1);
}
return (0);
}
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
};
#endif /* EMBEDDED */

View File

@@ -1,156 +0,0 @@
/*
* 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"
#include <unistd.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 == NULL)
c->mime_type = get_mime_type(c->ctx, c->uri, strlen(c->uri));
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"
"Connection: close\r\n"
"%s\r\n",
status, msg, date, lm, etag, c->mime_type, 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
};

View File

@@ -1,41 +0,0 @@
/*
* 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"
#include <unistd.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
};

View File

@@ -1,85 +0,0 @@
/*
* 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 */

View File

@@ -1,59 +0,0 @@
/*
* 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 */

View File

@@ -1,141 +0,0 @@
/*
* 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 || c->ctx->inetd_mode == 0) {
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 defined(_WIN32) && !defined(NO_GUI)
{
extern HWND hLog;
if (hLog != NULL)
SendMessage(hLog, WM_APP, 0, (LPARAM) buf);
}
#endif /* _WIN32 */
if (flags & E_FATAL)
exit(EXIT_FAILURE);
}
/*
* HACK: m68k-gcc <= 4.2.1 ICEs on the snprintf below for some
* coldfire variants for yet unknown reasons.
* C.f.: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=32307
* T.S, 2008/3/25 -- 4.2.3 still has this problem
*/
#if defined(__GNUC__) && \
( (__GNUC__ < 4 ) || \
( (__GNUC__ == 4 ) && (__GNUC_MINOR__ < 2 ) ) || \
( (__GNUC__ == 4 ) && (__GNUC_MINOR__ == 2 ) && (__GNUC_PATCHLEVEL__ <= 3 ) ) )
#if defined(__mcoldfire__)
#define SPLIT_SNPRINTF 1
#endif /* __mcoldfire__ */
#endif /* __GNUC__ */
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(&current_time));
#if SPLIT_SNPRINTF
{
int num;
num = my_snprintf(buf, sizeof(buf),
"%s - %.*s [%s %+05d] \"%s\" %d %lu",
inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr,
date, tz_offset, c->request ? c->request : "-",
c->status, c->loc.io.total );
my_snprintf(&buf[num], sizeof(buf)-num, " %s%.*s%s" " %s%.*s%s",
q1, referer->len, referer->ptr, q1,
q2, user_agent->len, user_agent->ptr, q2);
}
#else
(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);
#endif
if (fp != NULL) {
(void) fprintf(fp, "%s\n", buf);
(void) fflush(fp);
}
#if defined(_WIN32) && !defined(NO_GUI)
{
extern HWND hLog;
if (hLog != NULL)
SendMessage(hLog, WM_APP, 0, (LPARAM) buf);
}
#endif /* _WIN32 */
}

View File

@@ -1,249 +0,0 @@
/*
* 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 */

View File

@@ -1,24 +0,0 @@
/*
* 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 */

View File

@@ -1,108 +0,0 @@
/*
* 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"
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
static const struct mime_type default_mime_types[] = {
{"html", 4, "text/html" },
{"htm", 3, "text/html" },
{"txt", 3, "text/plain" },
{"css", 3, "text/css" },
{"ico", 3, "image/x-icon" },
{"gif", 3, "image/gif" },
{"jpg", 3, "image/jpeg" },
{"jpeg", 4, "image/jpeg" },
{"png", 3, "image/png" },
{"svg", 3, "image/svg+xml" },
{"torrent", 7, "application/x-bittorrent" },
{"wav", 3, "audio/x-wav" },
{"mp3", 3, "audio/x-mp3" },
{"mid", 3, "audio/mid" },
{"m3u", 3, "audio/x-mpegurl" },
{"ram", 3, "audio/x-pn-realaudio" },
{"ra", 2, "audio/x-pn-realaudio" },
{"doc", 3, "application/msword", },
{"exe", 3, "application/octet-stream" },
{"zip", 3, "application/x-zip-compressed" },
{"xls", 3, "application/excel" },
{"tgz", 3, "application/x-tar-gz" },
{"tar.gz", 6, "application/x-tar-gz" },
{"tar", 3, "application/x-tar" },
{"gz", 2, "application/x-gunzip" },
{"arj", 3, "application/x-arj-compressed" },
{"rar", 3, "application/x-arj-compressed" },
{"rtf", 3, "application/rtf" },
{"pdf", 3, "application/pdf" },
{"mpg", 3, "video/mpeg" },
{"mpeg", 4, "video/mpeg" },
{"asf", 3, "video/x-ms-asf" },
{"avi", 3, "video/x-msvideo" },
{"bmp", 3, "image/bmp" },
{NULL, 0, NULL }
};
const char *
get_mime_type(struct shttpd_ctx *ctx, const char *uri, int len)
{
struct llhead *lp;
const struct mime_type *mt;
struct mime_type_link *mtl;
const char *s;
/* Firt, loop through the custom mime types if any */
LL_FOREACH(&ctx->mime_types, lp) {
mtl = LL_ENTRY(lp, struct mime_type_link, link);
s = uri + len - mtl->ext_len;
if (s > uri && s[-1] == '.' &&
!my_strncasecmp(mtl->ext, s, mtl->ext_len))
return (mtl->mime);
}
/* If no luck, try built-in mime types */
for (mt = default_mime_types; mt->ext != NULL; mt++) {
s = uri + len - mt->ext_len;
if (s > uri && s[-1] == '.' &&
!my_strncasecmp(mt->ext, s, mt->ext_len))
return (mt->mime);
}
/* Oops. This extension is unknown to us. Fallback to text/plain */
return ("text/plain");
}
void
set_mime_types(struct shttpd_ctx *ctx, const char *path)
{
FILE *fp;
char line[512], ext[sizeof(line)], mime[sizeof(line)], *s;
if ((fp = fopen(path, "r")) == NULL)
elog(E_FATAL, NULL, "set_mime_types: fopen(%s): %s",
path, strerror(errno));
while (fgets(line, sizeof(line), fp) != NULL) {
/* Skip empty lines */
if (line[0] == '#' || line[0] == '\n')
continue;
if (sscanf(line, "%s", mime)) {
s = line + strlen(mime);
while (*s && *s != '\n' && sscanf(s, "%s", ext)) {
shttpd_add_mime_type(ctx, ext, mime);
s += strlen(mime);
}
}
}
(void) fclose(fp);
}

View File

@@ -1,39 +0,0 @@
## Automatically generated by ampolish3 - Do not edit
if AMPOLISH3
$(srcdir)/preinstall.am: Makefile.am
$(AMPOLISH3) $(srcdir)/Makefile.am > $(srcdir)/preinstall.am
endif
PREINSTALL_DIRS =
DISTCLEANFILES = $(PREINSTALL_DIRS)
all-local: $(TMPINSTALL_FILES)
TMPINSTALL_FILES =
CLEANFILES = $(TMPINSTALL_FILES)
all-am: $(PREINSTALL_FILES)
PREINSTALL_FILES =
CLEANFILES += $(PREINSTALL_FILES)
$(PROJECT_LIB)/$(dirstamp):
@$(MKDIR_P) $(PROJECT_LIB)
@: > $(PROJECT_LIB)/$(dirstamp)
PREINSTALL_DIRS += $(PROJECT_LIB)/$(dirstamp)
if LIBSHTTPD
$(PROJECT_INCLUDE)/shttpd/$(dirstamp):
@$(MKDIR_P) $(PROJECT_INCLUDE)/shttpd
@: > $(PROJECT_INCLUDE)/shttpd/$(dirstamp)
PREINSTALL_DIRS += $(PROJECT_INCLUDE)/shttpd/$(dirstamp)
$(PROJECT_LIB)/libshttpd.a: libshttpd.a $(PROJECT_LIB)/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_LIB)/libshttpd.a
TMPINSTALL_FILES += $(PROJECT_LIB)/libshttpd.a
$(PROJECT_INCLUDE)/shttpd/shttpd.h: shttpd.h $(PROJECT_INCLUDE)/shttpd/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/shttpd/shttpd.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/shttpd/shttpd.h
endif

View File

@@ -1,135 +0,0 @@
.\" Process this file with
.\" groff -man -Tascii shttpd.1
.\" $Id$
.Dd Nov 10, 2006
.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 realm username 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
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 an extension, which is
.Dq .cgi
by default.
.Pp
By default
.Nm
does not use SSL and starts listening on port 80. Specifying the
.Fl s Ar pem_file
option automatically switches
.Nm
to SSL mode on port 443.
.Pp
.Nm
can take configuration parameters from two sources: from the command
line and from the configuration file. Command line parameters have
higher priority. Every command line parameter has associated configuration
file keyword, except
.Fl A
parameter.
In the configuration file, blank lines and lines started with
.Dq #
character are ignored. All other lines must start with the keyword
followed by a whitespace followed by keyword's value.
If both command line parameter and configuration
file option are not specified, the default value is taken. The configuration
file may not be present at all.
.Pp
If
.Nm
should be managed by
.Xr inetd 8 ,
add this line to
.Pa inetd.conf :
.Pp
.Dl http stream tcp nowait nobody /path/to/shttpd shttpd -I1 -d /my/www
.Pp
.Sh OPTIONS
Below is the list of command line parameters. In the brackets there are
corresponding configuration file keywords.
.Bl -tag -width indent
.It Fl A Ar htpasswd server_name user_name user_password
Edit the passwords file. Functionality similar to Apache's
.Ic htdigest
utility.
.It Fl C Ar file ( Cm cgi_interpreter Ar file )
Force
.Ar file
to be a CGI interpreter for all CGI scripts. Default: none.
.It Fl D Ar 0|1 ( Cm list_directories Ar 0|1 )
Disable directory listing. Default: enabled.
.It Fl I Ar 0|1 ( Cm inetd_mode Ar 0|1 )
Enable inetd mode. Default: disabled.
.It Fl N Ar realm ( Cm server_name Ar realm )
Authorization realm. Default:
.Dq mydomain.com .
.It Fl P Ar file ( Cm global_htpasswd Ar file )
Location of global passwords file. Per-directory .htpasswd files are
ignored, if this option is set. Default: not set.
.It Fl U Ar file ( Cm put_auth Ar file )
PUT and DELETE passwords file. This must be specified if PUT or
DELETE methods are used. Default: none.
.It Fl V Ar string ( Cm cgi_envvar Ar string )
Pass additional environment variables to the CGI script. These must be
comma-separated list of var=val pairs, like this: "VAR1=VAL1,VAR2=VAL2".
Default: not set.
.It Fl a Ar string ( Cm aliases Ar string )
Attach directories (even those outside document_root) to URIs. The
.Ar string
must be comma-separated list of var=val pairs, like this:
"/etc/=/my_etc,/tmp=/my_tmp". Default: not set.
.It Fl c Ar string ( Cm cgi_extension Ar string )
CGI filename pattern. For every requested file,
.Nm
uses the pattern to figure out whether to return the file content,
ot run the file as CGI application. Default:
.Dq .cgi .
.It Fl d Ar directory ( Cm document_root Ar directory )
Location of the WWW root directory. Default: working directory from which
.Nm
has been started.
.It Fl e Ar file ( Cm error_log Ar file )
Error log file. Default: not set, no errors are logged.
.It Fl l Ar file ( Cm access_log Ar file )
Access log file. Default: not set, no logging is done.
.It Fl m Ar file ( Cm mime_types Ar file )
Location of mime types file. Default: not set, builtins are used.
.It Fl p Ar port ( Cm listen_port Ar port )
Listening port. Default: 80 for non-SSL and 443 for SSL mode.
.It Fl s Ar pem_file ( Cm ssl_certificate Ar pem_file )
Location of SSL certificate file. Default: not set.
.It Fl u Ar login ( Cm runtime_uid Ar login )
Switch to given user ID 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.
.Sh FILES
.Pa /usr/local/etc/shttpd.conf
.br
.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 .

File diff suppressed because it is too large Load Diff

View File

@@ -1,129 +0,0 @@
/*
* 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$
*/
#ifndef SHTTPD_HEADER_INCLUDED
#define SHTTPD_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
* 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 {
char *buf; /* Buffer pointer */
int len; /* Size of a buffer */
int num_bytes; /* Bytes processed by callback */
} in, out; /* POST data buffer (in) and output buffer (out) */
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
};
/*
* 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', not more than 'out_buf_max_len' bytes,
* and record how many bytes are copied, into 'out_buf_written_len'
* 2. it must not block the execution
* 3. it must set 'last' in shttpd_arg to 1 if there is no more data
* 4. for POST requests, it must process the incoming data (in_buf), and
* set 'in_buf_read_len', which is how many bytes of POST data is read
* and can be discarded by SHTTPD.
*/
typedef void (*shttpd_callback_t)(struct shttpd_arg *);
/*
* shttpd_init Initialize shttpd context. Parameters: configuration
* file name (may be NULL), then NULL-terminated
* sequence of pairs "option_name", "option_value".
* shttpd_init2 Same as shttpd_init, but the list of option/value
* pairs is passed in arrays
* shttpd_fini Dealocate the context
* shttpd_register_uri Setup the callback function for specified URL.
* shttpd_protect_uri Associate authorization file with an URL.
* shttpd_add_mime_type Add mime type
* 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 Return POST/GET variable value for given variable name.
* 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(const char *config_file, ...);
struct shttpd_ctx *shttpd_init2(const char *config_file,
char *names[], char *values[], size_t num_options);
void shttpd_fini(struct shttpd_ctx *);
void shttpd_add_mime_type(struct shttpd_ctx *,
const char *ext, const char *mime);
int shttpd_listen(struct shttpd_ctx *ctx, int port);
void shttpd_register_uri(struct shttpd_ctx *ctx,
const char *uri, shttpd_callback_t callback, void *user_data);
void shttpd_protect_uri(struct shttpd_ctx *ctx,
const char *uri, const char *file);
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 *);
const char *shttpd_get_env(struct shttpd_arg *, const char *);
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 *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 shttpd_accept(int lsn_sock, int milliseconds);
int shttpd_active(struct shttpd_ctx *);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SHTTPD_HEADER_INCLUDED */

View File

@@ -1,52 +0,0 @@
/*
* 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))

View File

@@ -1,92 +0,0 @@
/*
* 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 const char *config_file = CONFIG;
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;
}
}
int
main(int argc, char *argv[])
{
struct shttpd_ctx *ctx;
current_time = time(NULL);
if (argc > 1 && argv[argc - 2][0] != '-' && argv[argc - 1][0] != '-')
config_file = argv[argc - 1];
#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 */
ctx = init_from_argc_argv(config_file, argc, argv);
if (ctx->inetd_mode)
(void) freopen("/dev/null", "a", stderr);
if (ctx->inetd_mode)
shttpd_add_socket(ctx, fileno(stdin));
else if (shttpd_listen(ctx, ctx->port) == -1)
elog(E_FATAL, NULL, "Cannot open socket on port %d", ctx->port);
#ifndef _WIN32
/* Switch to alternate UID, it is safe now, after shttpd_listen() */
if (ctx->uid != NULL) {
struct passwd *pw;
if ((pw = getpwnam(ctx->uid)) == NULL)
elog(E_FATAL, 0, "main: unknown user [%s]", ctx->uid);
else if (setgid(pw->pw_gid) == -1)
elog(E_FATAL, NULL, "main: setgid(%s): %s",
ctx->uid, strerror(errno));
else if (setuid(pw->pw_uid) == -1)
elog(E_FATAL, NULL, "main: setuid(%s): %s",
ctx->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);
elog(E_LOG, NULL, "shttpd %s started on port %d, serving %s",
VERSION, ctx->port, ctx->document_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);
}

View File

@@ -1,41 +0,0 @@
/*
* 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 */

View File

@@ -1,85 +0,0 @@
/*
* 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"
#ifndef HAVE_STRLCPY
void
my_strlcpy(register char *dst, register const char *src, size_t n)
{
for (; *src != '\0' && n > 1; n--)
*dst++ = *src++;
*dst = '\0';
}
#endif
#ifndef HAVE_STRNCASECMP
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);
}
#endif
#ifndef HAVE_STRNDUP
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);
}
#endif
#ifndef HAVE_STRDUP
char *
my_strdup(const char *str)
{
return (my_strndup(str, strlen(str)));
}
#endif
/*
* 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);
}