forked from Imagelibrary/rtems
Remove.
This commit is contained in:
@@ -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])
|
||||
])
|
||||
@@ -1,2 +0,0 @@
|
||||
Makefile.in
|
||||
Makefile
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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)
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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(¤t_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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
};
|
||||
@@ -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","<DIR>");
|
||||
} 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> %s</td><td> %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
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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(¤t_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
|
||||
};
|
||||
@@ -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
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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(¤t_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 = ‐
|
||||
|
||||
if (referer->len == 0) {
|
||||
referer = ‐
|
||||
q1 = "";
|
||||
}
|
||||
|
||||
if (user_agent->len == 0) {
|
||||
user_agent = ‐
|
||||
q2 = "";
|
||||
}
|
||||
|
||||
(void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
|
||||
localtime(¤t_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 */
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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))
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user