version 0.1.6
authorSomeDude <SomeDude@NuBL7aaJ6Cn4fB7GXFb9Zfi8w1FhPyW3oKgU9TweZMw>
Sat, 2 Feb 2008 09:26:00 +0000 (10:26 +0100)
committerDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Sat, 2 Feb 2008 09:26:00 +0000 (10:26 +0100)
51 files changed:
CMakeLists.txt
include/global.h
include/http/httpdefs.h [new file with mode: 0644]
include/http/httpthread.h [new file with mode: 0644]
include/http/ipagehandler.h [new file with mode: 0644]
include/http/pages/homepage.h [new file with mode: 0644]
include/pthreadwrapper/runnable.h
libs/shttpd/auth.c [new file with mode: 0644]
libs/shttpd/cgi.c [new file with mode: 0644]
libs/shttpd/compat_rtems.c [new file with mode: 0644]
libs/shttpd/compat_rtems.h [new file with mode: 0644]
libs/shttpd/compat_unix.c [new file with mode: 0644]
libs/shttpd/compat_unix.h [new file with mode: 0644]
libs/shttpd/compat_win32.c [new file with mode: 0644]
libs/shttpd/compat_win32.h [new file with mode: 0644]
libs/shttpd/compat_wince.c [new file with mode: 0644]
libs/shttpd/compat_wince.h [new file with mode: 0644]
libs/shttpd/config.c [new file with mode: 0644]
libs/shttpd/defs.h [new file with mode: 0644]
libs/shttpd/include/shttpd.h [new file with mode: 0644]
libs/shttpd/io.h [new file with mode: 0644]
libs/shttpd/io_cgi.c [new file with mode: 0644]
libs/shttpd/io_dir.c [new file with mode: 0644]
libs/shttpd/io_emb.c [new file with mode: 0644]
libs/shttpd/io_file.c [new file with mode: 0644]
libs/shttpd/io_socket.c [new file with mode: 0644]
libs/shttpd/io_ssi.c [new file with mode: 0644]
libs/shttpd/io_ssl.c [new file with mode: 0644]
libs/shttpd/llist.h [new file with mode: 0644]
libs/shttpd/log.c [new file with mode: 0644]
libs/shttpd/md5.c [new file with mode: 0644]
libs/shttpd/md5.h [new file with mode: 0644]
libs/shttpd/mime_type.c [new file with mode: 0644]
libs/shttpd/shttpd.c [new file with mode: 0644]
libs/shttpd/shttpd.h [new file with mode: 0644]
libs/shttpd/ssl.h [new file with mode: 0644]
libs/shttpd/standalone.c [new file with mode: 0644]
libs/shttpd/std_includes.h [new file with mode: 0644]
libs/shttpd/string.c [new file with mode: 0644]
src/freenet/freenetmasterthread.cpp
src/freenet/identityintroductionrequester.cpp
src/freenet/identityrequester.cpp
src/freenet/introductionpuzzlerequester.cpp
src/freenet/trustlistrequester.cpp
src/global.cpp
src/http/httpthread.cpp [new file with mode: 0644]
src/http/ipagehandler.cpp [new file with mode: 0644]
src/http/pages/homepage.cpp [new file with mode: 0644]
src/nntp/mime/Mime.cpp
src/nntp/nntpconnection.cpp
src/nntp/nntplistener.cpp

index 18a089c..52233e8 100644 (file)
@@ -46,6 +46,9 @@ src/freenet/captcha/simplecaptcha.cpp
 src/freenet/captcha/easybmp/EasyBMP.cpp\r
 src/freenet/captcha/easybmp/EasyBMP_Font.cpp\r
 src/freenet/captcha/easybmp/EasyBMP_Geometry.cpp\r
+src/http/httpthread.cpp\r
+src/http/ipagehandler.cpp\r
+src/http/pages/homepage.cpp\r
 src/nntp/nntpconnection.cpp\r
 src/nntp/nntplistener.cpp\r
 src/nntp/uwildmat.cpp\r
@@ -74,8 +77,8 @@ ENDIF(WIN32)
 \r
 FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3 sqlite3_s)\r
 FIND_LIBRARY(TINYXML_LIBRARY NAMES tinyxml tinyxml_s)\r
-#FIND_LIBRARY(ZTHREADS_LIBRARY NAMES zthread ZThread zthread_s)\r
 FIND_LIBRARY(PTHREADS_LIBRARY NAMES pthread pthreads pthreadvc2)\r
+FIND_LIBRARY(SHTTPD_LIBRARY NAMES shttpd)\r
 \r
 IF(SQLITE3_LIBRARY)\r
        TARGET_LINK_LIBRARIES(fms ${SQLITE3_LIBRARY})\r
@@ -98,14 +101,23 @@ ELSE(TINYXML_LIBRARY)
        INCLUDE_DIRECTORIES(libs/tinyxml)\r
 ENDIF(TINYXML_LIBRARY)\r
 \r
-#IF(ZTHREADS_LIBRARY)\r
-#      TARGET_LINK_LIBRARIES(fms ${ZTHREADS_LIBRARY})\r
-#ELSE(ZTHREADS_LIBRARY)\r
-#      MESSAGE(FATAL_ERROR "Could not find ZThreads library.  You must set the location manually.")\r
-#ENDIF(ZTHREADS_LIBRARY)\r
-\r
 IF(PTHREADS_LIBRARY)\r
        TARGET_LINK_LIBRARIES(fms ${PTHREADS_LIBRARY})\r
 ELSE(PTHREADS_LIBRARY)\r
        MESSAGE(FATAL ERROR "Could not find pthreads library.  You must set the location manually.")\r
 ENDIF(PTHREADS_LIBRARY)\r
+\r
+IF(SHTTPD_LIBRARY)\r
+       TARGET_LINK_LIBRARIES(fms ${SHTTPD_LIBRARY})\r
+ELSE(SHTTPD_LIBRARY)\r
+       MESSAGE(STATUS "Could not find shttpd library.  Will compile from included source.")\r
+       ADD_DEFINITIONS(-DEMBEDDED -DNO_GUI)\r
+       INCLUDE_DIRECTORIES(libs/shttpd/include)\r
+       IF(WIN32)\r
+               SET(SHTTPD_PLATFORM_SRC libs/shttpd/compat_win32.c)\r
+       ELSE(WIN32)\r
+               SET(SHTTPD_PLATFORM_SRC libs/shttpd/compat_unix.c)\r
+       ENDIF(WIN32)\r
+       ADD_LIBRARY(shttpd ${SHTTPD_PLATFORM_SRC} libs/shttpd/auth.c libs/shttpd/cgi.c libs/shttpd/config.c libs/shttpd/io_cgi.c libs/shttpd/io_dir.c libs/shttpd/io_emb.c libs/shttpd/io_file.c libs/shttpd/io_socket.c libs/shttpd/io_ssi.c libs/shttpd/io_ssl.c libs/shttpd/log.c libs/shttpd/md5.c libs/shttpd/mime_type.c libs/shttpd/shttpd.c libs/shttpd/string.c)\r
+       TARGET_LINK_LIBRARIES(fms shttpd)\r
+ENDIF(SHTTPD_LIBRARY)\r
index 6f5445f..cee8ab5 100644 (file)
@@ -5,7 +5,7 @@
 //#include <zthread/Thread.h>\r
 #include "pthreadwrapper/thread.h"\r
 \r
-#define FMS_VERSION    "0.1.5"\r
+#define FMS_VERSION    "0.1.6"\r
 \r
 // opens database and creates tables and initial inserts if necessary\r
 void SetupDB();\r
diff --git a/include/http/httpdefs.h b/include/http/httpdefs.h
new file mode 100644 (file)
index 0000000..8dcd77f
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _httpdefs_\r
+#define _httpdefs_\r
+\r
+struct httpstate\r
+{\r
+       char *m_indata;\r
+       long m_indatalen;\r
+       long m_indatapos;\r
+       char *m_outdata;\r
+       long m_outdatalen;\r
+       long m_outdatapos;\r
+};\r
+\r
+#endif // _httpdefs_\r
diff --git a/include/http/httpthread.h b/include/http/httpthread.h
new file mode 100644 (file)
index 0000000..ecb61e4
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _httpthread_\r
+#define _httpthread_\r
+\r
+#include "../pthreadwrapper/runnable.h"\r
+#include "../ilogger.h"\r
+#include "../idatabase.h"\r
+#include "ipagehandler.h"\r
+#include "httpdefs.h"\r
+\r
+#include <shttpd.h>\r
+\r
+class HTTPThread:public PThread::Runnable,public ILogger, public IDatabase\r
+{\r
+public:\r
+       HTTPThread();\r
+       ~HTTPThread();\r
+       \r
+       void Run();\r
+\r
+private:\r
+       static void PageCallback(shttpd_arg *arg);\r
+\r
+       struct shttpd_ctx *m_ctx;\r
+\r
+       std::vector<IPageHandler *> m_pagehandlers;\r
+\r
+};\r
+\r
+#endif // _httpthread_\r
diff --git a/include/http/ipagehandler.h b/include/http/ipagehandler.h
new file mode 100644 (file)
index 0000000..7624c4a
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _ipagehandler_\r
+#define _ipagehandler_\r
+\r
+#include <shttpd.h>\r
+#include <string>\r
+#include <map>\r
+\r
+class IPageHandler\r
+{\r
+public:\r
+\r
+       /**\r
+               \brief Handles request for a page\r
+               \r
+               \return true if request was handled, false if it was ignored\r
+       */\r
+       const bool Handle(shttpd_arg *arg);\r
+\r
+private:\r
+       void HandlePost(shttpd_arg *arg);\r
+       void HadleGet(shttpd_arg *arg);\r
+       virtual const bool WillHandleURI(const std::string &uri)=0;\r
+       virtual const std::string GeneratePage(const std::string &method, const std::map<std::string,std::string> &queryvars)=0;\r
+       \r
+};\r
+\r
+#endif // _ipagehandler_\r
diff --git a/include/http/pages/homepage.h b/include/http/pages/homepage.h
new file mode 100644 (file)
index 0000000..977a015
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _homepage_\r
+#define _homepage_\r
+\r
+#include "../ipagehandler.h"\r
+\r
+class HomePage:public IPageHandler\r
+{\r
+public:\r
+\r
+private:\r
+       const bool WillHandleURI(const std::string &uri);\r
+       const std::string GeneratePage(const std::string &method, const std::map<std::string,std::string> &queryvars);\r
+};\r
+\r
+#endif // _homepage_\r
index cc3900e..fa10847 100644 (file)
@@ -12,7 +12,7 @@ class Runnable
 {\r
 public:\r
        Runnable():m_thread(0)  {}\r
-       ~Runnable()                             {}\r
+       virtual ~Runnable()             {}\r
 \r
        virtual void Run()=0;\r
 \r
diff --git a/libs/shttpd/auth.c b/libs/shttpd/auth.c
new file mode 100644 (file)
index 0000000..86f8eac
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_AUTH)
+/*
+ * Stringify binary data. Output buffer must be twice as big as input,
+ * because each byte takes 2 bytes in string representation
+ */
+static void
+bin2str(char *to, const unsigned char *p, size_t len)
+{
+       const char      *hex = "0123456789abcdef";
+
+       for (;len--; p++) {
+               *to++ = hex[p[0] >> 4];
+               *to++ = hex[p[0] & 0x0f];
+       }
+}
+
+/*
+ * Return stringified MD5 hash for list of vectors.
+ * buf must point to at least 32-bytes long buffer
+ */
+static void
+md5(char *buf, ...)
+{
+       unsigned char   hash[16];
+       const struct vec *v;
+       va_list         ap;
+       MD5_CTX ctx;
+       int             i;
+
+       MD5Init(&ctx);
+
+       va_start(ap, buf);
+       for (i = 0; (v = va_arg(ap, const struct vec *)) != NULL; i++) {
+               assert(v->len >= 0);
+               if (v->len == 0)
+                       continue;
+               if (i > 0)
+                       MD5Update(&ctx, (unsigned char *) ":", 1);
+               MD5Update(&ctx,(unsigned char *)v->ptr,(unsigned int)v->len);
+       }
+       va_end(ap);
+
+       MD5Final(hash, &ctx);
+       bin2str(buf, hash, sizeof(hash));
+}
+
+/*
+ * Compare to vectors. Return 1 if they are equal
+ */
+static int
+vcmp(const struct vec *v1, const struct vec *v2)
+{
+       return (v1->len == v2->len && !memcmp(v1->ptr, v2->ptr, v1->len));
+}
+
+struct digest {
+       struct vec      user;
+       struct vec      uri;
+       struct vec      nonce;
+       struct vec      cnonce;
+       struct vec      resp;
+       struct vec      qop;
+       struct vec      nc;
+};
+
+static const struct auth_keyword {
+       size_t          offset;
+       struct vec      vec;
+} known_auth_keywords[] = {
+       {offsetof(struct digest, user),         {"username=",   9}},
+       {offsetof(struct digest, cnonce),       {"cnonce=",     7}},
+       {offsetof(struct digest, resp),         {"response=",   9}},
+       {offsetof(struct digest, uri),          {"uri=",        4}},
+       {offsetof(struct digest, qop),          {"qop=",        4}},
+       {offsetof(struct digest, nc),           {"nc=",         3}},
+       {offsetof(struct digest, nonce),        {"nonce=",      6}},
+       {0,                                     {NULL,          0}}
+};
+
+static void
+parse_authorization_header(const struct vec *h, struct digest *dig)
+{
+       const unsigned char     *p, *e, *s;
+       struct vec              *v, vec;
+       const struct auth_keyword *kw;
+
+       (void) memset(dig, 0, sizeof(*dig));
+       p = (unsigned char *) h->ptr + 7;
+       e = (unsigned char *) h->ptr + h->len;
+
+       while (p < e) {
+
+               /* Skip spaces */
+               while (p < e && (*p == ' ' || *p == ','))
+                       p++;
+
+               /* Skip to "=" */
+               for (s = p; s < e && *s != '='; )
+                       s++;
+               s++;
+
+               /* Is it known keyword ? */
+               for (kw = known_auth_keywords; kw->vec.len > 0; kw++)
+                       if (kw->vec.len <= s - p &&
+                           !memcmp(p, kw->vec.ptr, kw->vec.len))
+                               break;
+
+               if (kw->vec.len == 0)
+                       v = &vec;               /* Dummy placeholder    */
+               else
+                       v = (struct vec *) ((char *) dig + kw->offset);
+
+               if (*s == '"') {
+                       p = ++s;
+                       while (p < e && *p != '"')
+                               p++;
+               } else {
+                       p = s;
+                       while (p < e && *p != ' ' && *p != ',')
+                               p++;
+               }
+
+               v->ptr = (char *) s;
+               v->len = p - s;
+
+               if (*p == '"')
+                       p++;
+
+               DBG(("auth field [%.*s]", v->len, v->ptr));
+       }
+}
+
+/*
+ * Check the user's password, return 1 if OK
+ */
+static int
+check_password(int method, const struct vec *ha1, const struct digest *digest)
+{
+       char            a2[32], resp[32];
+       struct vec      vec_a2;
+
+       /* XXX  Due to a bug in MSIE, we do not compare the URI  */
+       /* Also, we do not check for authentication timeout */
+       if (/*strcmp(dig->uri, c->ouri) != 0 || */
+           digest->resp.len != 32 /*||
+           now - strtoul(dig->nonce, NULL, 10) > 3600 */)
+               return (0);
+
+       md5(a2, &known_http_methods[method], &digest->uri, NULL);
+       vec_a2.ptr = a2;
+       vec_a2.len = sizeof(a2);
+       md5(resp, ha1, &digest->nonce, &digest->nc,
+           &digest->cnonce, &digest->qop, &vec_a2, NULL);
+
+       return (!memcmp(resp, digest->resp.ptr, 32));
+}
+
+static FILE *
+open_auth_file(struct shttpd_ctx *ctx, const char *path)
+{
+       char            name[FILENAME_MAX];
+       const char      *p, *e;
+       FILE            *fp = NULL;
+       int             fd;
+
+       if (ctx->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 */
diff --git a/libs/shttpd/cgi.c b/libs/shttpd/cgi.c
new file mode 100644 (file)
index 0000000..122dee3
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_CGI)
+struct env_block {
+       char    buf[ENV_MAX];           /* Environment buffer           */
+       int     len;                    /* Space taken                  */
+       char    *vars[CGI_ENV_VARS];    /* Point into the buffer        */
+       int     nvars;                  /* Number of variables          */
+};
+
+/*
+ * UNIX socketpair() implementation. Why? Because Windows does not have it.
+ * Return 0 on success, -1 on error.
+ */
+static int
+my_socketpair(struct conn *c, int sp[2])
+{
+       struct sockaddr_in      sa;
+       int                     sock, ret = -1;
+       socklen_t               len = sizeof(sa);
+
+       (void) memset(&sa, 0, sizeof(sa));
+       sa.sin_family           = AF_INET;
+       sa.sin_port             = htons(0);
+       sa.sin_addr.s_addr      = htonl(INADDR_LOOPBACK);
+
+       if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+               elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
+       } else if (bind(sock, (struct sockaddr *) &sa, len) != 0) {
+               elog(E_LOG, c, "mysocketpair: bind(): %d", ERRNO);
+               (void) closesocket(sock);
+       } else if (listen(sock, 1) != 0) {
+               elog(E_LOG, c, "mysocketpair: listen(): %d", ERRNO);
+               (void) closesocket(sock);
+       } else if (getsockname(sock, (struct sockaddr *) &sa, &len) != 0) {
+               elog(E_LOG, c, "mysocketpair: getsockname(): %d", ERRNO);
+               (void) closesocket(sock);
+       } else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
+               elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
+               (void) closesocket(sock);
+       } else if (connect(sp[0], (struct sockaddr *) &sa, len) != 0) {
+               elog(E_LOG, c, "mysocketpair: connect(): %d", ERRNO);
+               (void) closesocket(sock);
+               (void) closesocket(sp[0]);
+       } else if ((sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) == -1) {
+               elog(E_LOG, c, "mysocketpair: accept(): %d", ERRNO);
+               (void) closesocket(sock);
+               (void) closesocket(sp[0]);
+       } else {
+               /* Success */
+               ret = 0;
+               (void) closesocket(sock);
+       }
+
+#ifndef _WIN32
+       (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
+       (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
+#endif /* _WIN32*/
+
+       return (ret);
+}
+
+static void
+addenv(struct env_block *block, const char *fmt, ...)
+{
+       int     n, space;
+       va_list ap;
+
+       space = sizeof(block->buf) - block->len - 2;
+       assert(space >= 0);
+
+       va_start(ap, fmt);
+       n = vsnprintf(block->buf + block->len, space, fmt, ap);
+       va_end(ap);
+
+       if (n > 0 && n < space && block->nvars < CGI_ENV_VARS - 2) {
+               block->vars[block->nvars++] = block->buf + block->len;
+               block->len += n + 1;    /* Include \0 terminator */
+       }
+}
+
+static void
+add_http_headers_to_env(struct env_block *b, const char *s, int len)
+{
+       const char      *p, *v, *e = s + len;
+       int             space, n, i, ch;
+
+       /* Loop through all headers in the request */
+       while (s < e) {
+
+               /* Find where this header ends. Remember where value starts */
+               for (p = s, v = NULL; p < e && *p != '\n'; p++)
+                       if (v == NULL && *p == ':') 
+                               v = p;
+
+               /* 2 null terminators and "HTTP_" */
+               space = (sizeof(b->buf) - b->len) - (2 + 5);
+               assert(space >= 0);
+       
+               /* Copy header if enough space in the environment block */
+               if (v > s && p > v + 2 && space > p - s) {
+
+                       /* Store var */
+                       if (b->nvars < (int) NELEMS(b->vars) - 1)
+                               b->vars[b->nvars++] = b->buf + b->len;
+
+                       (void) memcpy(b->buf + b->len, "HTTP_", 5);
+                       b->len += 5;
+
+                       /* Copy header name. Substitute '-' to '_' */
+                       n = v - s;
+                       for (i = 0; i < n; i++) {
+                               ch = s[i] == '-' ? '_' : s[i];
+                               b->buf[b->len++] = toupper(ch);
+                       }
+
+                       b->buf[b->len++] = '=';
+
+                       /* Copy header value */
+                       v += 2;
+                       n = p[-1] == '\r' ? (p - v) - 1 : p - v;
+                       for (i = 0; i < n; i++)
+                               b->buf[b->len++] = v[i];
+
+                       /* Null-terminate */
+                       b->buf[b->len++] = '\0';
+               }
+
+               s = p + 1;      /* Shift to the next header */
+       }
+}
+
+static void
+prepare_environment(const struct conn *c, const char *prog,
+               struct env_block *blk)
+{
+       const struct headers    *h = &c->ch;
+       const char              *s;
+       size_t                  len;
+
+       blk->len = blk->nvars = 0;
+
+       /* Prepare the environment block */
+       addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
+       addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
+       addenv(blk, "%s", "REDIRECT_STATUS=200");       /* PHP */
+       addenv(blk, "SERVER_PORT=%d", c->loc_port);
+       addenv(blk, "SERVER_NAME=%s", c->ctx->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 */
diff --git a/libs/shttpd/compat_rtems.c b/libs/shttpd/compat_rtems.c
new file mode 100644 (file)
index 0000000..2a01a07
--- /dev/null
@@ -0,0 +1,198 @@
+/**********************************************************************
+ *
+ *  rtems shttpd management
+ *
+ *  FILE NAME   : rtems_shttpd.c
+ *
+ *  AUTHOR      : Steven Johnson
+ *
+ *  DESCRIPTION : Defines the interface functions to the shttp daemon
+ *
+ *  REVISION    : $Id: compat_rtems.c,v 1.2 2006/11/12 03:29:17 infidel Exp $
+ *
+ *  COMMENTS    :
+ *
+ **********************************************************************/
+
+ /**********************************************************************
+ * INCLUDED MODULES
+ **********************************************************************/
+#include <rtems.h>
+#include "defs.h"
+
+#define MAX_WEB_BASE_PATH_LENGTH 256
+#define MIN_SHTTPD_STACK         (8*1024)
+
+typedef struct RTEMS_HTTPD_ARGS {
+    rtems_shttpd_init     init_callback;
+    rtems_shttpd_addpages addpages_callback;
+    char                  webroot[MAX_WEB_BASE_PATH_LENGTH];
+} RTEMS_HTTPD_ARGS;
+
+static int rtems_webserver_running = FALSE; //not running.
+
+static rtems_task rtems_httpd_daemon(rtems_task_argument args )
+{
+  RTEMS_HTTPD_ARGS *httpd_args = (RTEMS_HTTPD_ARGS*)args;
+
+  struct shttpd_ctx       *ctx;
+
+  if (httpd_args != NULL)
+    if (httpd_args->init_callback != NULL)
+      httpd_args->init_callback();
+
+/**************************************
+ *  Initialize the web server
+ */
+  /*
+    * Initialize SHTTPD context.
+    * Set WWW root to current WEB_ROOT_PATH.
+    */
+  ctx = shttpd_init(NULL, "document_root", httpd_args->webroot, NULL);
+
+  if (httpd_args != NULL)
+    if (httpd_args->addpages_callback != NULL)
+      httpd_args->addpages_callback(ctx);
+
+  /* Finished with args, so free them */
+  if (httpd_args != NULL)
+    free(httpd_args);
+
+  /* Open listening socket */
+  shttpd_listen(ctx, 9000);
+
+  rtems_webserver_running = TRUE;
+
+  /* Serve connections infinitely until someone kills us */
+  while (rtems_webserver_running)
+    shttpd_poll(ctx, 1000);
+
+  /* Unreached, because we will be killed by a signal */
+  shttpd_fini(ctx);
+
+  rtems_task_delete( RTEMS_SELF );
+}
+
+rtems_status_code rtems_initialize_webserver(rtems_task_priority   initial_priority,
+                                             rtems_unsigned32      stack_size,
+                                             rtems_mode            initial_modes,
+                                             rtems_attribute       attribute_set,
+                                             rtems_shttpd_init     init_callback,
+                                             rtems_shttpd_addpages addpages_callback,
+                                             char                 *webroot
+                                            )
+{
+  rtems_status_code   sc;
+  rtems_id            tid;
+  RTEMS_HTTPD_ARGS    *args;
+
+  if (stack_size < MIN_SHTTPD_STACK)
+    stack_size = MIN_SHTTPD_STACK;
+
+  args = malloc(sizeof(RTEMS_HTTPD_ARGS));
+
+  if (args != NULL)
+  {
+    args->init_callback = init_callback;
+    args->addpages_callback = addpages_callback;
+    strncpy(args->webroot,webroot,MAX_WEB_BASE_PATH_LENGTH);
+
+    sc = rtems_task_create(rtems_build_name('H', 'T', 'P', 'D'),
+                           initial_priority,
+                           stack_size,
+                           initial_modes,
+                           attribute_set,
+                           &tid);
+
+    if (sc == RTEMS_SUCCESSFUL)
+    {
+      sc = rtems_task_start(tid, rtems_httpd_daemon, (rtems_task_argument)args);
+    }
+  }
+  else
+  {
+    sc = RTEMS_NO_MEMORY;
+  }
+
+  return sc;
+}
+
+void rtems_terminate_webserver(void)
+{
+  rtems_webserver_running = FALSE; //not running, so terminate
+}
+
+int rtems_webserver_ok(void)
+{
+  return rtems_webserver_running;
+}
+
+void
+set_close_on_exec(int fd)
+{
+        // RTEMS Does not have a functional "execve"
+        // so technically this call does not do anything,
+        // but it doesnt hurt either.
+        (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+int
+my_stat(const char *path, struct stat *stp)
+{
+        return (stat(path, stp));
+}
+
+int
+my_open(const char *path, int flags, int mode)
+{
+        return (open(path, flags, mode));
+}
+
+int
+my_remove(const char *path)
+{
+       return (remove(path));
+}
+
+int
+my_rename(const char *path1, const char *path2)
+{
+       return (rename(path1, path2));
+}
+
+int
+my_mkdir(const char *path, int mode)
+{
+       return (mkdir(path, mode));
+}
+
+char *
+my_getcwd(char *buffer, int maxlen)
+{
+       return (getcwd(buffer, maxlen));
+}
+
+int
+set_non_blocking_mode(int fd)
+{
+        int     ret = -1;
+        int     flags;
+
+        if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
+                DBG(("nonblock: fcntl(F_GETFL): %d", ERRNO));
+        } else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
+                DBG(("nonblock: fcntl(F_SETFL): %d", ERRNO));
+        } else {
+                ret = 0;        /* Success */
+        }
+
+        return (ret);
+}
+
+#if !defined(NO_CGI)
+int
+spawn_process(struct conn *c, const char *prog, char *envblk, char **envp)
+{
+        return (-1); // RTEMS does not have subprocess support as standard.
+}
+#endif
diff --git a/libs/shttpd/compat_rtems.h b/libs/shttpd/compat_rtems.h
new file mode 100644 (file)
index 0000000..8d31e46
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * @file rtems/rtems-shttpd.h
+ */
+
+#ifndef _rtems_rtems_webserver_h
+#define _rtems_rtems_webserver_h
+
+#include "shttpd.h"
+
+#include <rtems.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <dirent.h>
+
+/* RTEMS is an Real Time Embedded operating system, for operation in hardware.
+  It does not have SSL or CGI support, as it does not have dynamic library
+  loading or sub-processes. */
+#define EMBEDDED
+#define NO_SSL
+#define NO_CGI
+
+#define DIRSEP                          '/'
+#define O_BINARY                        0
+#define ERRNO                           errno
+
+/* RTEMS version is Thread Safe */
+#define InitializeCriticalSection(x)  rtems_semaphore_create( \
+                                  rtems_build_name('H','T','P','X'), \
+                                  1, /* Not Held Yet.*/ \
+                                  RTEMS_FIFO | \
+                                  RTEMS_BINARY_SEMAPHORE, \
+                                  0, \
+                                  x);
+#define EnterCriticalSection(x) rtems_semaphore_obtain(*(x),RTEMS_WAIT,RTEMS_NO_TIMEOUT)
+#define LeaveCriticalSection(x) rtems_semaphore_release(*(x))
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*rtems_shttpd_addpages)(struct shttpd_ctx *ctx);
+typedef void (*rtems_shttpd_init)(void);
+
+rtems_status_code rtems_initialize_webserver(rtems_task_priority   initial_priority,
+                                             rtems_unsigned32      stack_size,
+                                             rtems_mode            initial_modes,
+                                             rtems_attribute       attribute_set,
+                                             rtems_shttpd_init     init_callback,
+                                             rtems_shttpd_addpages addpages_callback,
+                                             char                 *webroot
+                                            );
+void rtems_terminate_webserver(void);
+int  rtems_webserver_ok(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libs/shttpd/compat_unix.c b/libs/shttpd/compat_unix.c
new file mode 100644 (file)
index 0000000..649d0c8
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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 */
diff --git a/libs/shttpd/compat_unix.h b/libs/shttpd/compat_unix.h
new file mode 100644 (file)
index 0000000..67c935f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2004-2007 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/mman.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+
+#include <pwd.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#define        SSL_LIB                         "libssl.so"
+#define        DIRSEP                          '/'
+#define        IS_DIRSEP_CHAR(c)               ((c) == '/')
+#define        O_BINARY                        0
+#define        closesocket(a)                  close(a)
+#define        ERRNO                           errno
+#define        NO_GUI
+
+#define        InitializeCriticalSection(x)    /* FIXME UNIX version is not MT safe */
+#define        EnterCriticalSection(x)
+#define        LeaveCriticalSection(x)
diff --git a/libs/shttpd/compat_win32.c b/libs/shttpd/compat_win32.c
new file mode 100644 (file)
index 0000000..fb5b1a1
--- /dev/null
@@ -0,0 +1,922 @@
+/*
+ * 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;
+
+       open_listening_ports(ctx);
+
+       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);
+                       open_listening_ports(ctx);
+                       _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 */
diff --git a/libs/shttpd/compat_win32.h b/libs/shttpd/compat_win32.h
new file mode 100644 (file)
index 0000000..a5c1dce
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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        popen(x,y)              _popen(x, y)
+#define        pclose(x)               _pclose(x)
+#define        dlopen(x,y)             LoadLibraryW(x)
+#define        dlsym(x,y)              (void *) GetProcAddress(x,y)
+#define        _POSIX_
+
+#ifdef __LCC__
+#include <stdint.h>
+#endif /* __LCC__ */
+
+#ifdef _MSC_VER /* MinGW already has these */
+typedef unsigned int           uint32_t;
+typedef unsigned short         uint16_t;
+typedef __int64                        uint64_t;
+#define S_ISDIR(x)             ((x) & _S_IFDIR)
+#endif /* _MSC_VER */
+
+/*
+ * POSIX dirent interface
+ */
+struct dirent {
+       char    d_name[FILENAME_MAX];
+};
+
+typedef struct DIR {
+       HANDLE                  handle;
+       WIN32_FIND_DATAW        info;
+       struct dirent           result;
+       char                    *name;
+} DIR;
+
+extern DIR *opendir(const char *name);
+extern int closedir(DIR *dir);
+extern struct dirent *readdir(DIR *dir);
diff --git a/libs/shttpd/compat_wince.c b/libs/shttpd/compat_wince.c
new file mode 100644 (file)
index 0000000..36702f6
--- /dev/null
@@ -0,0 +1,1593 @@
+/*
+ vi:ts=8:sw=8:noet
+ * Copyright (c) 2006 Luke Dunstan <infidel@users.sourceforge.net>
+ * Partly based on code by David Kashtan, Validus Medical Systems
+ *
+ * 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.
+ */
+
+/*
+ * This file provides various functions that are available on desktop Windows
+ * but not on Windows CE
+ */
+
+#ifdef _MSC_VER
+/* Level 4 warnings caused by windows.h */
+#pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable : 4115) // named type definition in parentheses
+#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable : 4514) // unreferenced inline function has been removed
+#pragma warning(disable : 4244) // conversion from 'int ' to 'unsigned short ', possible loss of data
+#pragma warning(disable : 4100) // unreferenced formal parameter
+#endif
+
+#include <windows.h>
+#include <stdlib.h>
+
+#include "compat_wince.h"
+
+
+static WCHAR *to_wide_string(LPCSTR pStr)
+{
+       int nwide;
+       WCHAR *buf;
+
+       if(pStr == NULL)
+               return NULL;
+       nwide = MultiByteToWideChar(CP_ACP, 0, pStr, -1, NULL, 0);
+       if(nwide == 0)
+               return NULL;
+       buf = malloc(nwide * sizeof(WCHAR));
+       if(buf == NULL) {
+               SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+               return NULL;
+       }
+       if(MultiByteToWideChar(CP_ACP, 0, pStr, -1, buf, nwide) == 0) {
+               free(buf);
+               return NULL;
+       }
+       return buf;
+}
+
+FILE *fdopen(int handle, const char *mode)
+{
+       WCHAR *wmode = to_wide_string(mode);
+       FILE *result;
+
+       if(wmode != NULL)
+               result = _wfdopen((void *)handle, wmode);
+       else
+               result = NULL;
+       free(wmode);
+       return result;
+}
+
+/*
+ *     Time conversion constants
+ */
+#define FT_EPOCH (116444736000000000i64)
+#define        FT_TICKS (10000000i64)
+
+ /*
+ *     Convert a FILETIME to a time_t
+ */
+static time_t convert_FILETIME_to_time_t(FILETIME *File_Time)
+{
+       __int64 Temp;
+
+       /*
+        *      Convert the FILETIME structure to 100nSecs since 1601 (as a 64-bit value)
+        */
+       Temp = (((__int64)File_Time->dwHighDateTime) << 32) + (__int64)File_Time->dwLowDateTime;
+       /*
+        *      Convert to seconds from 1970
+        */
+       return((time_t)((Temp - FT_EPOCH) / FT_TICKS));
+}
+
+/*
+ *     Convert a FILETIME to a tm structure
+ */
+static struct tm *Convert_FILETIME_To_tm(FILETIME *File_Time)
+{
+       SYSTEMTIME System_Time;
+       static struct tm tm = {0};
+       static const short Day_Of_Year_By_Month[12] = {(short)(0),
+                                                      (short)(31),
+                                                      (short)(31 + 28),
+                                                      (short)(31 + 28 + 31),
+                                                      (short)(31 + 28 + 31 + 30),
+                                                      (short)(31 + 28 + 31 + 30 + 31),
+                                                      (short)(31 + 28 + 31 + 30 + 31 + 30),
+                                                      (short)(31 + 28 + 31 + 30 + 31 + 30 + 31),
+                                                      (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31),
+                                                      (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30),
+                                                      (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31),
+                                                      (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)};
+
+
+       /*
+        *      Turn the FILETIME into a SYSTEMTIME
+        */
+       FileTimeToSystemTime(File_Time, &System_Time);
+       /*
+        *      Use SYSTEMTIME to fill in the tm structure
+        */
+       tm.tm_sec = System_Time.wSecond;
+       tm.tm_min = System_Time.wMinute;
+       tm.tm_hour = System_Time.wHour;
+       tm.tm_mday = System_Time.wDay;
+       tm.tm_mon = System_Time.wMonth - 1;
+       tm.tm_year = System_Time.wYear - 1900;
+       tm.tm_wday = System_Time.wDayOfWeek;
+       tm.tm_yday = Day_Of_Year_By_Month[tm.tm_mon] + tm.tm_mday - 1;
+       if (tm.tm_mon >= 2) {
+               /*
+                *      Check for leap year (every 4 years but not every 100 years but every 400 years)
+                */
+               if ((System_Time.wYear % 4) == 0) {
+                       /*
+                        *      It Is a 4th year
+                        */
+                       if ((System_Time.wYear % 100) == 0) {
+                               /*
+                                *      It is a 100th year
+                                */
+                               if ((System_Time.wYear % 400) == 0) {
+                                       /*
+                                        *      It is a 400th year: It is a leap year
+                                        */
+                                       tm.tm_yday++;
+                               }
+                       } else {
+                               /*
+                                *      It is not a 100th year: It is a leap year
+                                */
+                               tm.tm_yday++;
+                       }
+               }
+       }
+       return(&tm);
+}
+
+/*
+ *     Convert a time_t to a FILETIME
+ */
+static void Convert_time_t_To_FILETIME(time_t Time, FILETIME *File_Time)
+{
+       __int64 Temp;
+
+       /*
+        *      Use 64-bit calculation to convert seconds since 1970 to
+        *      100nSecs since 1601
+        */
+       Temp = ((__int64)Time * FT_TICKS) + FT_EPOCH;
+       /*
+        *      Put it into the FILETIME structure
+        */
+       File_Time->dwLowDateTime = (DWORD)Temp;
+       File_Time->dwHighDateTime = (DWORD)(Temp >> 32);
+}
+
+/*
+ *     Convert a tm structure to a FILETIME
+ */
+static FILETIME *Convert_tm_To_FILETIME(struct tm *tm)
+{
+       SYSTEMTIME System_Time;
+       static FILETIME File_Time = {0};
+
+       /*
+        *      Use the tm structure to fill in a SYSTEM
+        */
+       System_Time.wYear = tm->tm_year + 1900;
+       System_Time.wMonth = tm->tm_mon + 1;
+       System_Time.wDayOfWeek = tm->tm_wday;
+       System_Time.wDay = tm->tm_mday;
+       System_Time.wHour = tm->tm_hour;
+       System_Time.wMinute = tm->tm_min;
+       System_Time.wSecond = tm->tm_sec;
+       System_Time.wMilliseconds = 0;
+       /*
+        *      Convert it to a FILETIME and return it
+        */
+       SystemTimeToFileTime(&System_Time, &File_Time);
+       return(&File_Time);
+}
+
+
+/************************************************************************/
+/*                                                                     */
+/*     Errno emulation:  There is no errno on Windows/CE and we need   */
+/*                       to make it per-thread.  So we have a function */
+/*                       that returns a pointer to the errno for the   */
+/*                       current thread.                               */
+/*                                                                     */
+/*                       If there is ONLY the main thread then we can  */
+/*                       quickly return some static storage.           */
+/*                                                                     */
+/*                       If we have multiple threads running, we use   */
+/*                       Thread-Local Storage to hold the pointer      */
+/*                                                                     */
+/************************************************************************/
+
+/*
+ *     Function pointer for returning errno pointer
+ */
+static int *Initialize_Errno(void);
+int *(*__WinCE_Errno_Pointer_Function)(void) = Initialize_Errno;
+
+/*
+ *     Static errno storage for the main thread
+ */
+static int Errno_Storage = 0;
+
+/*
+ *     Thread-Local storage slot for errno
+ */
+static int TLS_Errno_Slot = 0xffffffff;
+
+/*
+ *     Number of threads we have running and critical section protection
+ *     for manipulating it
+ */
+static int Number_Of_Threads = 0;
+static CRITICAL_SECTION Number_Of_Threads_Critical_Section;
+
+/*
+ *     For the main thread only -- return the errno pointer
+ */
+static int *Get_Main_Thread_Errno(void)
+{
+       return &Errno_Storage;
+}
+
+/*
+ *     When there is more than one thread -- return the errno pointer
+ */
+static int *Get_Thread_Errno(void)
+{
+       return (int *)TlsGetValue(TLS_Errno_Slot);
+}
+
+/*
+ *     Initialize a thread's errno
+ */
+static void Initialize_Thread_Errno(int *Errno_Pointer)
+{
+       /*
+        *      Make sure we have a slot
+        */
+       if (TLS_Errno_Slot == 0xffffffff) {
+               /*
+                *      No: Get one
+                */
+               TLS_Errno_Slot = (int)TlsAlloc();
+               if (TLS_Errno_Slot == 0xffffffff) ExitProcess(3);
+       }
+       /*
+        *      We can safely check for 0 threads, because
+        *      only the main thread will be initializing
+        *      at this point.  Make sure the critical
+        *      section that protects the number of threads
+        *      is initialized
+        */
+       if (Number_Of_Threads == 0)
+               InitializeCriticalSection(&Number_Of_Threads_Critical_Section);
+       /*
+        *      Store the errno pointer
+        */
+       if (TlsSetValue(TLS_Errno_Slot, (LPVOID)Errno_Pointer) == 0) ExitProcess(3);
+       /*
+        *      Bump the number of threads
+        */
+       EnterCriticalSection(&Number_Of_Threads_Critical_Section);
+       Number_Of_Threads++;
+       if (Number_Of_Threads > 1) {
+               /*
+                *      We have threads other than the main thread:
+                *        Use thread-local storage
+                */
+               __WinCE_Errno_Pointer_Function = Get_Thread_Errno;
+       }
+       LeaveCriticalSection(&Number_Of_Threads_Critical_Section);
+}
+
+/*
+ *     Initialize errno emulation on Windows/CE (Main thread)
+ */
+static int *Initialize_Errno(void)
+{
+       /*
+        *      Initialize the main thread's errno in thread-local storage
+        */
+       Initialize_Thread_Errno(&Errno_Storage);
+       /*
+        *      Set the errno function to be the one that returns the
+        *      main thread's errno
+        */
+       __WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno;
+       /*
+        *      Return the main thread's errno
+        */
+       return &Errno_Storage;
+}
+
+/*
+ *     Initialize errno emulation on Windows/CE (New thread)
+ */
+void __WinCE_Errno_New_Thread(int *Errno_Pointer)
+{
+       Initialize_Thread_Errno(Errno_Pointer);
+}
+
+/*
+ *     Note that a thread has exited
+ */
+void __WinCE_Errno_Thread_Exit(void)
+{
+       /*
+        *      Decrease the number of threads
+        */
+       EnterCriticalSection(&Number_Of_Threads_Critical_Section);
+       Number_Of_Threads--;
+       if (Number_Of_Threads <= 1) {
+               /*
+                *      We only have the main thread
+                */
+               __WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno;
+       }
+       LeaveCriticalSection(&Number_Of_Threads_Critical_Section);
+}
+
+
+char *
+strerror(int errnum)
+{
+       return "(strerror not implemented)";
+}
+
+#define FT_EPOCH (116444736000000000i64)
+#define        FT_TICKS (10000000i64)
+
+int
+_wstat(const WCHAR *path, struct _stat *buffer)
+{
+       WIN32_FIND_DATA data;
+       HANDLE handle;
+       WCHAR *p;
+
+       /* Fail if wildcard characters are specified */
+       if (wcscspn(path, L"?*") != wcslen(path))
+               return -1;
+
+       handle = FindFirstFile(path, &data);
+       if (handle == INVALID_HANDLE_VALUE) {
+               errno = GetLastError();
+               return -1;
+       }
+       FindClose(handle);
+
+       /* Found: Convert the file times */
+       buffer->st_mtime = convert_FILETIME_to_time_t(&data.ftLastWriteTime);
+       if (data.ftLastAccessTime.dwLowDateTime || data.ftLastAccessTime.dwHighDateTime)
+               buffer->st_atime = convert_FILETIME_to_time_t(&data.ftLastAccessTime);
+       else
+               buffer->st_atime = buffer->st_mtime;
+       if (data.ftCreationTime.dwLowDateTime || data.ftCreationTime.dwHighDateTime)
+               buffer->st_ctime = convert_FILETIME_to_time_t(&data.ftCreationTime);
+       else
+               buffer->st_ctime = buffer->st_mtime;
+
+       /* Convert the file modes */
+       buffer->st_mode = (unsigned short)((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG);
+       buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE);
+       if((p = wcsrchr(path, L'.')) != NULL) {
+               p++;
+               if (_wcsicmp(p, L".exe") == 0)
+                       buffer->st_mode |= S_IEXEC;
+       }
+       buffer->st_mode |= (buffer->st_mode & 0700) >> 3;
+       buffer->st_mode |= (buffer->st_mode & 0700) >> 6;
+       /* Set the other information */
+       buffer->st_nlink = 1;
+       buffer->st_size = (unsigned long int)data.nFileSizeLow;
+       buffer->st_uid = 0;
+       buffer->st_gid = 0;
+       buffer->st_ino = 0 /*data.dwOID ?*/;
+       buffer->st_dev = 0;
+
+       return 0;       /* success */
+}
+
+/*
+ *     Helper function for cemodule -- do an fstat() operation on a Win32 File Handle
+ */
+int
+_fstat(int handle, struct _stat *st)
+{
+       BY_HANDLE_FILE_INFORMATION Data;
+
+       /*
+        *      Get the file information
+        */
+       if (!GetFileInformationByHandle((HANDLE)handle, &Data)) {
+               /*
+                *      Return error
+                */
+               errno = GetLastError();
+               return(-1);
+       }
+       /*
+        *      Found: Convert the file times
+        */
+       st->st_mtime=(time_t)((*(__int64*)&Data.ftLastWriteTime-FT_EPOCH)/FT_TICKS);
+       if(Data.ftLastAccessTime.dwLowDateTime || Data.ftLastAccessTime.dwHighDateTime)
+               st->st_atime=(time_t)((*(__int64*)&Data.ftLastAccessTime-FT_EPOCH)/FT_TICKS);
+       else
+               st->st_atime=st->st_mtime ;
+       if(Data.ftCreationTime.dwLowDateTime || Data.ftCreationTime.dwHighDateTime )
+               st->st_ctime=(time_t)((*(__int64*)&Data.ftCreationTime-FT_EPOCH)/FT_TICKS);
+       else
+               st->st_ctime=st->st_mtime ;
+       /*
+        *      Convert the file modes
+        */
+       st->st_mode = (unsigned short)((Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG);
+       st->st_mode |= (Data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE);
+       st->st_mode |= (st->st_mode & 0700) >> 3;
+       st->st_mode |= (st->st_mode & 0700) >> 6;
+       /*
+        *      Set the other information
+        */
+       st->st_nlink=1;
+       st->st_size=(unsigned long int)Data.nFileSizeLow;
+       st->st_uid=0;
+       st->st_gid=0;
+       st->st_ino=0;
+       st->st_dev=0;
+       /*
+        *      Return success
+        */
+       return(0);
+}
+
+int _wopen(const wchar_t *filename, int oflag, ...)
+{
+       DWORD Access, ShareMode, CreationDisposition;
+       HANDLE Handle;
+       static int Modes[4] = {0, (GENERIC_READ | GENERIC_WRITE), GENERIC_READ, GENERIC_WRITE};
+
+       /*
+        *      Calculate the CreateFile arguments
+        */
+       Access = Modes[oflag & O_MODE_MASK];
+       ShareMode = (oflag & O_EXCL) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE);
+       if (oflag & O_TRUNC)
+               CreationDisposition = (oflag & O_CREAT) ? CREATE_ALWAYS : TRUNCATE_EXISTING;
+       else
+               CreationDisposition = (oflag & O_CREAT) ? CREATE_NEW : OPEN_EXISTING;
+
+       Handle = CreateFileW(filename, Access, ShareMode, NULL, CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
+
+       /*
+        *      Deal with errors
+        */
+       if (Handle == INVALID_HANDLE_VALUE) {
+               errno = GetLastError();
+               if ((errno == ERROR_ALREADY_EXISTS) || (errno == ERROR_FILE_EXISTS))
+                       errno = ERROR_ALREADY_EXISTS;
+               return -1;
+       }
+       /*
+        *      Return the handle
+        */
+       return (int)Handle;
+}
+
+int
+_close(int handle)
+{
+       if(CloseHandle((HANDLE)handle))
+               return 0;
+       errno = GetLastError();
+       return -1;
+}
+
+int
+_write(int handle, const void *buffer, unsigned int count)
+{
+       DWORD numwritten = 0;
+       if(!WriteFile((HANDLE)handle, buffer, count, &numwritten, NULL)) {
+               errno = GetLastError();
+               return -1;
+       }
+       return numwritten;
+}
+
+int
+_read(int handle, void *buffer, unsigned int count)
+{
+       DWORD numread = 0;
+       if(!ReadFile((HANDLE)handle, buffer, count, &numread, NULL)) {
+               errno = GetLastError();
+               return -1;
+       }
+       return numread;
+}
+
+long
+_lseek(int handle, long offset, int origin)
+{
+       DWORD dwMoveMethod;
+       DWORD result;
+
+       switch(origin) {
+               default:
+                       errno = EINVAL;
+                       return -1L;
+               case SEEK_SET:
+                       dwMoveMethod = FILE_BEGIN;
+                       break;
+               case SEEK_CUR:
+                       dwMoveMethod = FILE_CURRENT;
+                       break;
+               case SEEK_END:
+                       dwMoveMethod = FILE_END;
+                       break;
+       }
+       result = SetFilePointer((HANDLE)handle, offset, NULL, dwMoveMethod);
+       if(result == 0xFFFFFFFF) {
+               errno = GetLastError();
+               return -1;
+       }
+       return (long)result;
+}
+
+int
+_wmkdir(const wchar_t *dirname)
+{
+       if(!CreateDirectoryW(dirname, NULL)) {
+               errno = GetLastError();
+               return -1;
+       }
+       return 0;
+}
+
+int
+_wremove(const wchar_t *filename)
+{
+       if(!DeleteFileW(filename)) {
+               errno = GetLastError();
+               return -1;
+       }
+       return 0;
+}
+
+int
+_wrename(const wchar_t *oldname, const wchar_t *newname)
+{
+       if(!MoveFileW(oldname, newname)) {
+               errno = GetLastError();
+               return -1;
+       }
+       return 0;
+}
+
+wchar_t *
+_wgetcwd(wchar_t *buffer, int maxlen)
+{
+       wchar_t *result;
+       WCHAR wszPath[MAX_PATH + 1];
+       WCHAR *p;
+
+       if(!GetModuleFileNameW(NULL, wszPath, MAX_PATH + 1)) {
+               errno = GetLastError();
+               return NULL;
+       }
+       /* Remove the filename part of the path to leave the directory */
+       p = wcsrchr(wszPath, L'\\');
+       if(p)
+               *p = L'\0';
+
+       if(buffer == NULL)
+               result = _wcsdup(wszPath);
+       else if(wcslen(wszPath) + 1 > (size_t)maxlen) {
+               result = NULL;
+               errno = ERROR_INSUFFICIENT_BUFFER;
+       } else {
+               wcsncpy(buffer, wszPath, maxlen);
+               buffer[maxlen - 1] = L'\0';
+               result = buffer;
+       }
+       return result;
+}
+
+/*
+ *     The missing "C" runtime gmtime() function
+ */
+struct tm *gmtime(const time_t *TimeP)
+{
+       FILETIME File_Time;
+
+       /*
+        *      Deal with null time pointer
+        */
+       if (!TimeP) return(NULL);
+       /*
+        *      time_t -> FILETIME -> tm
+        */
+       Convert_time_t_To_FILETIME(*TimeP, &File_Time);
+       return(Convert_FILETIME_To_tm(&File_Time));
+}
+
+/*
+ *     The missing "C" runtime localtime() function
+ */
+struct tm *localtime(const time_t *TimeP)
+{
+       FILETIME File_Time, Local_File_Time;
+
+       /*
+        *      Deal with null time pointer
+        */
+       if (!TimeP) return(NULL);
+       /*
+        *      time_t -> FILETIME -> Local FILETIME -> tm
+        */
+       Convert_time_t_To_FILETIME(*TimeP, &File_Time);
+       FileTimeToLocalFileTime(&File_Time, &Local_File_Time);
+       return(Convert_FILETIME_To_tm(&Local_File_Time));
+}
+
+/*
+ *     The missing "C" runtime mktime() function
+ */
+time_t mktime(struct tm *tm)
+{
+       FILETIME *Local_File_Time;
+       FILETIME File_Time;
+
+       /*
+        *      tm -> Local FILETIME -> FILETIME -> time_t
+        */
+       Local_File_Time = Convert_tm_To_FILETIME(tm);
+       LocalFileTimeToFileTime(Local_File_Time, &File_Time);
+       return(convert_FILETIME_to_time_t(&File_Time));
+}
+
+/*
+ *     Missing "C" runtime time() function
+ */
+time_t time(time_t *TimeP)
+{
+       SYSTEMTIME System_Time;
+       FILETIME File_Time;
+       time_t Result;
+
+       /*
+        *      Get the current system time
+        */
+       GetSystemTime(&System_Time);
+       /*
+        *      SYSTEMTIME -> FILETIME -> time_t
+        */
+       SystemTimeToFileTime(&System_Time, &File_Time);
+       Result = convert_FILETIME_to_time_t(&File_Time);
+       /*
+        *      Return the time_t
+        */
+       if (TimeP) *TimeP = Result;
+       return(Result);
+}
+
+static char Standard_Name[32] = "GMT";
+static char Daylight_Name[32] = "GMT";
+char *tzname[2] = {Standard_Name, Daylight_Name};
+long timezone = 0;
+int daylight = 0;
+
+void tzset(void)
+{
+       TIME_ZONE_INFORMATION Info;
+       int Result;
+
+       /*
+        *      Get our current timezone information
+        */
+       Result = GetTimeZoneInformation(&Info);
+       switch(Result) {
+               /*
+                *      We are on standard time
+                */
+               case TIME_ZONE_ID_STANDARD:
+                       daylight = 0;
+                       break;
+               /*
+                *      We are on daylight savings time
+                */
+               case TIME_ZONE_ID_DAYLIGHT:
+                       daylight = 1;
+                       break;
+               /*
+                *      We don't know the timezone information (leave it GMT)
+                */
+               default: return;
+       }
+       /*
+        *      Extract the timezone information
+        */
+       timezone = Info.Bias * 60;
+       if (Info.StandardName[0])
+               WideCharToMultiByte(CP_ACP, 0, Info.StandardName, -1, Standard_Name, sizeof(Standard_Name) - 1, NULL, NULL);
+       if (Info.DaylightName[0])
+               WideCharToMultiByte(CP_ACP, 0, Info.DaylightName, -1, Daylight_Name, sizeof(Daylight_Name) - 1, NULL, NULL);
+}
+
+/*** strftime() from newlib libc/time/strftime.c ***/
+
+/*
+ * Sane snprintf(). Acts like snprintf(), but never return -1 or the
+ * value bigger than supplied buffer.
+ */
+static int
+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 || n > (int) buflen - 1) {
+               n = buflen - 1;
+       }
+       buf[n] = '\0';
+
+       return (n);
+}
+
+#define snprintf Snprintf
+
+/* from libc/include/_ansi.h */
+#define _CONST const
+#define        _DEFUN(name, arglist, args)     name(args)
+#define        _AND            ,
+/* from libc/time/local.h */
+#define TZ_LOCK
+#define TZ_UNLOCK
+#define _tzname tzname
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+#define YEAR_BASE      1900
+#define SECSPERMIN     60L
+#define MINSPERHOUR    60L
+#define HOURSPERDAY    24L
+#define SECSPERHOUR    (SECSPERMIN * MINSPERHOUR)
+
+/*
+ * strftime.c
+ * Original Author:    G. Haley
+ * Additions from:     Eric Blake
+ *
+ * Places characters into the array pointed to by s as controlled by the string
+ * pointed to by format. If the total number of resulting characters including
+ * the terminating null character is not more than maxsize, returns the number
+ * of characters placed into the array pointed to by s (not including the
+ * terminating null character); otherwise zero is returned and the contents of
+ * the array indeterminate.
+ */
+
+/*
+FUNCTION
+<<strftime>>---flexible calendar time formatter
+
+INDEX
+       strftime
+
+ANSI_SYNOPSIS
+       #include <time.h>
+       size_t strftime(char *<[s]>, size_t <[maxsize]>,
+                       const char *<[format]>, const struct tm *<[timp]>);
+
+TRAD_SYNOPSIS
+       #include <time.h>
+       size_t strftime(<[s]>, <[maxsize]>, <[format]>, <[timp]>)
+       char *<[s]>;
+       size_t <[maxsize]>;
+       char *<[format]>;
+       struct tm *<[timp]>;
+
+DESCRIPTION
+<<strftime>> converts a <<struct tm>> representation of the time (at
+<[timp]>) into a null-terminated string, starting at <[s]> and occupying
+no more than <[maxsize]> characters.
+
+You control the format of the output using the string at <[format]>.
+<<*<[format]>>> can contain two kinds of specifications: text to be
+copied literally into the formatted string, and time conversion
+specifications.  Time conversion specifications are two- and
+three-character sequences beginning with `<<%>>' (use `<<%%>>' to
+include a percent sign in the output).  Each defined conversion
+specification selects only the specified field(s) of calendar time
+data from <<*<[timp]>>>, and converts it to a string in one of the
+following ways:
+
+o+
+o %a
+A three-letter abbreviation for the day of the week. [tm_wday]
+
+o %A
+The full name for the day of the week, one of `<<Sunday>>',
+`<<Monday>>', `<<Tuesday>>', `<<Wednesday>>', `<<Thursday>>',
+`<<Friday>>', or `<<Saturday>>'. [tm_wday]
+
+o %b
+A three-letter abbreviation for the month name. [tm_mon]
+
+o %B
+The full name of the month, one of `<<January>>', `<<February>>',
+`<<March>>', `<<April>>', `<<May>>', `<<June>>', `<<July>>',
+`<<August>>', `<<September>>', `<<October>>', `<<November>>',
+`<<December>>'. [tm_mon]
+
+o %c
+A string representing the complete date and time, in the form
+`<<"%a %b %e %H:%M:%S %Y">>' (example "Mon Apr 01 13:13:13
+1992"). [tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday]
+
+o %C
+The century, that is, the year divided by 100 then truncated.  For
+4-digit years, the result is zero-padded and exactly two characters;
+but for other years, there may a negative sign or more digits.  In
+this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year]
+o %d
+The day of the month, formatted with two digits (from `<<01>>' to
+`<<31>>'). [tm_mday]
+
+o %D
+A string representing the date, in the form `<<"%m/%d/%y">>'.
+[tm_mday, tm_mon, tm_year]
+
+o %e
+The day of the month, formatted with leading space if single digit
+(from `<<1>>' to `<<31>>'). [tm_mday]
+
+o %E<<x>>
+In some locales, the E modifier selects alternative representations of
+certain modifiers <<x>>.  But in the "C" locale supported by newlib,
+it is ignored, and treated as %<<x>>.
+
+o %F
+A string representing the ISO 8601:2000 date format, in the form
+`<<"%Y-%m-%d">>'. [tm_mday, tm_mon, tm_year]
+
+o %g
+The last two digits of the week-based year, see specifier %G (from
+`<<00>>' to `<<99>>'). [tm_year, tm_wday, tm_yday]
+
+o %G
+The week-based year. In the ISO 8601:2000 calendar, week 1 of the year
+includes January 4th, and begin on Mondays. Therefore, if January 1st,
+2nd, or 3rd falls on a Sunday, that day and earlier belong to the last
+week of the previous year; and if December 29th, 30th, or 31st falls
+on Monday, that day and later belong to week 1 of the next year.  For
+consistency with %Y, it always has at least four characters. 
+Example: "%G" for Saturday 2nd January 1999 gives "1998", and for
+Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday]
+
+o %h
+A three-letter abbreviation for the month name (synonym for
+"%b"). [tm_mon]
+
+o %H
+The hour (on a 24-hour clock), formatted with two digits (from
+`<<00>>' to `<<23>>'). [tm_hour]
+
+o %I
+The hour (on a 12-hour clock), formatted with two digits (from
+`<<01>>' to `<<12>>'). [tm_hour]
+
+o %j
+The count of days in the year, formatted with three digits
+(from `<<001>>' to `<<366>>'). [tm_yday]
+
+o %k
+The hour (on a 24-hour clock), formatted with leading space if single
+digit (from `<<0>>' to `<<23>>'). Non-POSIX extension. [tm_hour]
+
+o %l
+The hour (on a 12-hour clock), formatted with leading space if single
+digit (from `<<1>>' to `<<12>>'). Non-POSIX extension. [tm_hour]
+
+o %m
+The month number, formatted with two digits (from `<<01>>' to `<<12>>').
+[tm_mon]
+
+o %M
+The minute, formatted with two digits (from `<<00>>' to `<<59>>'). [tm_min]
+
+o %n
+A newline character (`<<\n>>').
+
+o %O<<x>>
+In some locales, the O modifier selects alternative digit characters
+for certain modifiers <<x>>.  But in the "C" locale supported by newlib, it
+is ignored, and treated as %<<x>>.
+
+o %p
+Either `<<AM>>' or `<<PM>>' as appropriate. [tm_hour]
+
+o %r
+The 12-hour time, to the second.  Equivalent to "%I:%M:%S %p". [tm_sec,
+tm_min, tm_hour]
+
+o %R
+The 24-hour time, to the minute.  Equivalent to "%H:%M". [tm_min, tm_hour]
+
+o %S
+The second, formatted with two digits (from `<<00>>' to `<<60>>').  The
+value 60 accounts for the occasional leap second. [tm_sec]
+
+o %t
+A tab character (`<<\t>>').
+
+o %T
+The 24-hour time, to the second.  Equivalent to "%H:%M:%S". [tm_sec,
+tm_min, tm_hour]
+
+o %u
+The weekday as a number, 1-based from Monday (from `<<1>>' to
+`<<7>>'). [tm_wday]
+
+o %U
+The week number, where weeks start on Sunday, week 1 contains the first
+Sunday in a year, and earlier days are in week 0.  Formatted with two
+digits (from `<<00>>' to `<<53>>').  See also <<%W>>. [tm_wday, tm_yday]
+
+o %V
+The week number, where weeks start on Monday, week 1 contains January 4th,
+and earlier days are in the previous year.  Formatted with two digits
+(from `<<01>>' to `<<53>>').  See also <<%G>>. [tm_year, tm_wday, tm_yday]
+
+o %w
+The weekday as a number, 0-based from Sunday (from `<<0>>' to `<<6>>').
+[tm_wday]
+
+o %W
+The week number, where weeks start on Monday, week 1 contains the first
+Monday in a year, and earlier days are in week 0.  Formatted with two
+digits (from `<<00>>' to `<<53>>'). [tm_wday, tm_yday]
+
+o %x
+A string representing the complete date, equivalent to "%m/%d/%y".
+[tm_mon, tm_mday, tm_year]
+
+o %X
+A string representing the full time of day (hours, minutes, and
+seconds), equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour]
+
+o %y
+The last two digits of the year (from `<<00>>' to `<<99>>'). [tm_year]
+
+o %Y
+The full year, equivalent to <<%C%y>>.  It will always have at least four
+characters, but may have more.  The year is accurate even when tm_year
+added to the offset of 1900 overflows an int. [tm_year]
+
+o %z
+The offset from UTC.  The format consists of a sign (negative is west of
+Greewich), two characters for hour, then two characters for minutes
+(-hhmm or +hhmm).  If tm_isdst is negative, the offset is unknown and no
+output is generated; if it is zero, the offset is the standard offset for
+the current time zone; and if it is positive, the offset is the daylight
+savings offset for the current timezone. The offset is determined from
+the TZ environment variable, as if by calling tzset(). [tm_isdst]
+
+o %Z
+The time zone name.  If tm_isdst is negative, no output is generated.
+Otherwise, the time zone name is based on the TZ environment variable,
+as if by calling tzset(). [tm_isdst]
+
+o %%
+A single character, `<<%>>'.
+o-
+
+RETURNS
+When the formatted time takes up no more than <[maxsize]> characters,
+the result is the length of the formatted string.  Otherwise, if the
+formatting operation was abandoned due to lack of room, the result is
+<<0>>, and the string starting at <[s]> corresponds to just those
+parts of <<*<[format]>>> that could be completely filled in within the
+<[maxsize]> limit.
+
+PORTABILITY
+ANSI C requires <<strftime>>, but does not specify the contents of
+<<*<[s]>>> when the formatted string would require more than
+<[maxsize]> characters.  Unrecognized specifiers and fields of
+<<timp>> that are out of range cause undefined results.  Since some
+formats expand to 0 bytes, it is wise to set <<*<[s]>>> to a nonzero
+value beforehand to distinguish between failure and an empty string.
+This implementation does not support <<s>> being NULL, nor overlapping
+<<s>> and <<format>>.
+
+<<strftime>> requires no supporting OS subroutines.
+*/
+
+static _CONST int dname_len[7] =
+{6, 6, 7, 9, 8, 6, 8};
+
+static _CONST char *_CONST dname[7] =
+{"Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"};
+
+static _CONST int mname_len[12] =
+{7, 8, 5, 5, 3, 4, 4, 6, 9, 7, 8, 8};
+
+static _CONST char *_CONST mname[12] =
+{"January", "February", "March", "April",
+ "May", "June", "July", "August", "September", "October", "November",
+ "December"};
+
+/* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return
+   -1, 0, or 1 as the adjustment to add to the year for the ISO week
+   numbering used in "%g%G%V", avoiding overflow.  */
+static int
+_DEFUN (iso_year_adjust, (tim_p),
+       _CONST struct tm *tim_p)
+{
+  /* Account for fact that tm_year==0 is year 1900.  */
+  int leap = isleap (tim_p->tm_year + (YEAR_BASE
+                                      - (tim_p->tm_year < 0 ? 0 : 2000)));
+
+  /* Pack the yday, wday, and leap year into a single int since there are so
+     many disparate cases.  */
+#define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp))
+  switch (PACK (tim_p->tm_yday, tim_p->tm_wday, leap))
+    {
+    case PACK (0, 5, 0): /* Jan 1 is Fri, not leap.  */
+    case PACK (0, 6, 0): /* Jan 1 is Sat, not leap.  */
+    case PACK (0, 0, 0): /* Jan 1 is Sun, not leap.  */
+    case PACK (0, 5, 1): /* Jan 1 is Fri, leap year.  */
+    case PACK (0, 6, 1): /* Jan 1 is Sat, leap year.  */
+    case PACK (0, 0, 1): /* Jan 1 is Sun, leap year.  */
+    case PACK (1, 6, 0): /* Jan 2 is Sat, not leap.  */
+    case PACK (1, 0, 0): /* Jan 2 is Sun, not leap.  */
+    case PACK (1, 6, 1): /* Jan 2 is Sat, leap year.  */
+    case PACK (1, 0, 1): /* Jan 2 is Sun, leap year.  */
+    case PACK (2, 0, 0): /* Jan 3 is Sun, not leap.  */
+    case PACK (2, 0, 1): /* Jan 3 is Sun, leap year.  */
+      return -1; /* Belongs to last week of previous year.  */
+    case PACK (362, 1, 0): /* Dec 29 is Mon, not leap.  */
+    case PACK (363, 1, 1): /* Dec 29 is Mon, leap year.  */
+    case PACK (363, 1, 0): /* Dec 30 is Mon, not leap.  */
+    case PACK (363, 2, 0): /* Dec 30 is Tue, not leap.  */
+    case PACK (364, 1, 1): /* Dec 30 is Mon, leap year.  */
+    case PACK (364, 2, 1): /* Dec 30 is Tue, leap year.  */
+    case PACK (364, 1, 0): /* Dec 31 is Mon, not leap.  */
+    case PACK (364, 2, 0): /* Dec 31 is Tue, not leap.  */
+    case PACK (364, 3, 0): /* Dec 31 is Wed, not leap.  */
+    case PACK (365, 1, 1): /* Dec 31 is Mon, leap year.  */
+    case PACK (365, 2, 1): /* Dec 31 is Tue, leap year.  */
+    case PACK (365, 3, 1): /* Dec 31 is Wed, leap year.  */
+      return 1; /* Belongs to first week of next year.  */
+    }
+  return 0; /* Belongs to specified year.  */
+#undef PACK
+}
+
+size_t
+_DEFUN (strftime, (s, maxsize, format, tim_p),
+       char *s _AND
+       size_t maxsize _AND
+       _CONST char *format _AND
+       _CONST struct tm *tim_p)
+{
+  size_t count = 0;
+  int i;
+
+  for (;;)
+    {
+      while (*format && *format != '%')
+       {
+         if (count < maxsize - 1)
+           s[count++] = *format++;
+         else
+           return 0;
+       }
+
+      if (*format == '\0')
+       break;
+
+      format++;
+      if (*format == 'E' || *format == 'O')
+       format++;
+
+      switch (*format)
+       {
+       case 'a':
+         for (i = 0; i < 3; i++)
+           {
+             if (count < maxsize - 1)
+               s[count++] =
+                 dname[tim_p->tm_wday][i];
+             else
+               return 0;
+           }
+         break;
+       case 'A':
+         for (i = 0; i < dname_len[tim_p->tm_wday]; i++)
+           {
+             if (count < maxsize - 1)
+               s[count++] =
+                 dname[tim_p->tm_wday][i];
+             else
+               return 0;
+           }
+         break;
+       case 'b':
+       case 'h':
+         for (i = 0; i < 3; i++)
+           {
+             if (count < maxsize - 1)
+               s[count++] =
+                 mname[tim_p->tm_mon][i];
+             else
+               return 0;
+           }
+         break;
+       case 'B':
+         for (i = 0; i < mname_len[tim_p->tm_mon]; i++)
+           {
+             if (count < maxsize - 1)
+               s[count++] =
+                 mname[tim_p->tm_mon][i];
+             else
+               return 0;
+           }
+         break;
+       case 'c':
+         {
+           /* Length is not known because of %C%y, so recurse. */
+           size_t adjust = strftime (&s[count], maxsize - count,
+                                     "%a %b %e %H:%M:%S %C%y", tim_p);
+           if (adjust > 0)
+             count += adjust;
+           else
+             return 0;
+         }
+         break;
+       case 'C':
+         {
+           /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y
+              with 32-bit int.
+              %Y               %C              %y
+              2147485547       21474855        47
+              10000            100             00
+              9999             99              99
+              0999             09              99
+              0099             00              99
+              0001             00              01
+              0000             00              00
+              -001             -0              01
+              -099             -0              99
+              -999             -9              99
+              -1000            -10             00
+              -10000           -100            00
+              -2147481748      -21474817       48
+
+              Be careful of both overflow and sign adjustment due to the
+              asymmetric range of years.
+           */
+           int neg = tim_p->tm_year < -YEAR_BASE;
+           int century = tim_p->tm_year >= 0
+             ? tim_p->tm_year / 100 + YEAR_BASE / 100
+             : abs (tim_p->tm_year + YEAR_BASE) / 100;
+            count += snprintf (&s[count], maxsize - count, "%s%.*d",
+                               neg ? "-" : "", 2 - neg, century);
+            if (count >= maxsize)
+              return 0;
+         }
+         break;
+       case 'd':
+       case 'e':
+         if (count < maxsize - 2)
+           {
+             sprintf (&s[count], *format == 'd' ? "%.2d" : "%2d",
+                      tim_p->tm_mday);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+       case 'D':
+       case 'x':
+         /* %m/%d/%y */
+         if (count < maxsize - 8)
+           {
+             sprintf (&s[count], "%.2d/%.2d/%.2d",
+                      tim_p->tm_mon + 1, tim_p->tm_mday,
+                      tim_p->tm_year >= 0 ? tim_p->tm_year % 100
+                      : abs (tim_p->tm_year + YEAR_BASE) % 100);
+             count += 8;
+           }
+         else
+           return 0;
+         break;
+        case 'F':
+         {
+           /* Length is not known because of %C%y, so recurse. */
+           size_t adjust = strftime (&s[count], maxsize - count,
+                                     "%C%y-%m-%d", tim_p);
+           if (adjust > 0)
+             count += adjust;
+           else
+             return 0;
+         }
+          break;
+        case 'g':
+         if (count < maxsize - 2)
+           {
+             /* Be careful of both overflow and negative years, thanks to
+                the asymmetric range of years.  */
+             int adjust = iso_year_adjust (tim_p);
+             int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
+               : abs (tim_p->tm_year + YEAR_BASE) % 100;
+             if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
+               adjust = 1;
+             else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
+               adjust = -1;
+             sprintf (&s[count], "%.2d",
+                      ((year + adjust) % 100 + 100) % 100);
+             count += 2;
+           }
+         else
+           return 0;
+          break;
+        case 'G':
+         {
+           /* See the comments for 'C' and 'Y'; this is a variable length
+              field.  Although there is no requirement for a minimum number
+              of digits, we use 4 for consistency with 'Y'.  */
+           int neg = tim_p->tm_year < -YEAR_BASE;
+           int adjust = iso_year_adjust (tim_p);
+           int century = tim_p->tm_year >= 0
+             ? tim_p->tm_year / 100 + YEAR_BASE / 100
+             : abs (tim_p->tm_year + YEAR_BASE) / 100;
+           int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
+             : abs (tim_p->tm_year + YEAR_BASE) % 100;
+           if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
+             neg = adjust = 1;
+           else if (adjust > 0 && neg)
+             adjust = -1;
+           year += adjust;
+           if (year == -1)
+             {
+               year = 99;
+               --century;
+             }
+           else if (year == 100)
+             {
+               year = 0;
+               ++century;
+             }
+            count += snprintf (&s[count], maxsize - count, "%s%.*d%.2d",
+                               neg ? "-" : "", 2 - neg, century, year);
+            if (count >= maxsize)
+              return 0;
+         }
+          break;
+       case 'H':
+       case 'k':
+         if (count < maxsize - 2)
+           {
+             sprintf (&s[count], *format == 'k' ? "%2d" : "%.2d",
+                      tim_p->tm_hour);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+       case 'I':
+       case 'l':
+         if (count < maxsize - 2)
+           {
+             if (tim_p->tm_hour == 0 ||
+                 tim_p->tm_hour == 12)
+               {
+                 s[count++] = '1';
+                 s[count++] = '2';
+               }
+             else
+               {
+                 sprintf (&s[count], *format == 'I' ? "%.2d" : "%2d",
+                          tim_p->tm_hour % 12);
+                 count += 2;
+               }
+           }
+         else
+           return 0;
+         break;
+       case 'j':
+         if (count < maxsize - 3)
+           {
+             sprintf (&s[count], "%.3d",
+                      tim_p->tm_yday + 1);
+             count += 3;
+           }
+         else
+           return 0;
+         break;
+       case 'm':
+         if (count < maxsize - 2)
+           {
+             sprintf (&s[count], "%.2d",
+                      tim_p->tm_mon + 1);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+       case 'M':
+         if (count < maxsize - 2)
+           {
+             sprintf (&s[count], "%.2d",
+                      tim_p->tm_min);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+       case 'n':
+         if (count < maxsize - 1)
+           s[count++] = '\n';
+         else
+           return 0;
+         break;
+       case 'p':
+         if (count < maxsize - 2)
+           {
+             if (tim_p->tm_hour < 12)
+               s[count++] = 'A';
+             else
+               s[count++] = 'P';
+
+             s[count++] = 'M';
+           }
+         else
+           return 0;
+         break;
+       case 'r':
+         if (count < maxsize - 11)
+           {
+             if (tim_p->tm_hour == 0 ||
+                 tim_p->tm_hour == 12)
+               {
+                 s[count++] = '1';
+                 s[count++] = '2';
+               }
+             else
+               {
+                 sprintf (&s[count], "%.2d", tim_p->tm_hour % 12);
+                 count += 2;
+               }
+             s[count++] = ':';
+             sprintf (&s[count], "%.2d",
+                      tim_p->tm_min);
+             count += 2;
+             s[count++] = ':';
+             sprintf (&s[count], "%.2d",
+                      tim_p->tm_sec);
+             count += 2;
+             s[count++] = ' ';
+             if (tim_p->tm_hour < 12)
+               s[count++] = 'A';
+             else
+               s[count++] = 'P';
+
+             s[count++] = 'M';
+           }
+         else
+           return 0;
+         break;
+        case 'R':
+          if (count < maxsize - 5)
+            {
+              sprintf (&s[count], "%.2d:%.2d", tim_p->tm_hour, tim_p->tm_min);
+              count += 5;
+            }
+          else
+            return 0;
+          break;
+       case 'S':
+         if (count < maxsize - 2)
+           {
+             sprintf (&s[count], "%.2d",
+                      tim_p->tm_sec);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+       case 't':
+         if (count < maxsize - 1)
+           s[count++] = '\t';
+         else
+           return 0;
+         break;
+        case 'T':
+        case 'X':
+          if (count < maxsize - 8)
+            {
+              sprintf (&s[count], "%.2d:%.2d:%.2d", tim_p->tm_hour,
+                       tim_p->tm_min, tim_p->tm_sec);
+              count += 8;
+            }
+          else
+            return 0;
+          break;
+        case 'u':
+          if (count < maxsize - 1)
+            {
+              if (tim_p->tm_wday == 0)
+                s[count++] = '7';
+              else
+                s[count++] = '0' + tim_p->tm_wday;
+            }
+          else
+            return 0;
+          break;
+       case 'U':
+         if (count < maxsize - 2)
+           {
+             sprintf (&s[count], "%.2d",
+                      (tim_p->tm_yday + 7 -
+                       tim_p->tm_wday) / 7);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+        case 'V':
+         if (count < maxsize - 2)
+           {
+             int adjust = iso_year_adjust (tim_p);
+             int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
+             int week = (tim_p->tm_yday + 10 - wday) / 7;
+             if (adjust > 0)
+               week = 1;
+             else if (adjust < 0)
+               /* Previous year has 53 weeks if current year starts on
+                  Fri, and also if current year starts on Sat and
+                  previous year was leap year.  */
+               week = 52 + (4 >= (wday - tim_p->tm_yday
+                                  - isleap (tim_p->tm_year
+                                            + (YEAR_BASE - 1
+                                               - (tim_p->tm_year < 0
+                                                  ? 0 : 2000)))));
+             sprintf (&s[count], "%.2d", week);
+             count += 2;
+           }
+         else
+           return 0;
+          break;
+       case 'w':
+         if (count < maxsize - 1)
+            s[count++] = '0' + tim_p->tm_wday;
+         else
+           return 0;
+         break;
+       case 'W':
+         if (count < maxsize - 2)
+           {
+             int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
+             sprintf (&s[count], "%.2d",
+                      (tim_p->tm_yday + 7 - wday) / 7);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+       case 'y':
+         if (count < maxsize - 2)
+           {
+             /* Be careful of both overflow and negative years, thanks to
+                the asymmetric range of years.  */
+             int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
+               : abs (tim_p->tm_year + YEAR_BASE) % 100;
+             sprintf (&s[count], "%.2d", year);
+             count += 2;
+           }
+         else
+           return 0;
+         break;
+       case 'Y':
+         {
+           /* Length is not known because of %C%y, so recurse. */
+           size_t adjust = strftime (&s[count], maxsize - count,
+                                     "%C%y", tim_p);
+           if (adjust > 0)
+             count += adjust;
+           else
+             return 0;
+         }
+         break;
+        case 'z':
+#ifndef _WIN32_WCE
+          if (tim_p->tm_isdst >= 0)
+            {
+             if (count < maxsize - 5)
+               {
+                 long offset;
+                 __tzinfo_type *tz = __gettzinfo ();
+                 TZ_LOCK;
+                 /* The sign of this is exactly opposite the envvar TZ.  We
+                    could directly use the global _timezone for tm_isdst==0,
+                    but have to use __tzrule for daylight savings.  */
+                 offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
+                 TZ_UNLOCK;
+                 sprintf (&s[count], "%+03ld%.2ld", offset / SECSPERHOUR,
+                          labs (offset / SECSPERMIN) % 60L);
+                 count += 5;
+               }
+             else
+               return 0;
+            }
+          break;
+#endif
+       case 'Z':
+         if (tim_p->tm_isdst >= 0)
+           {
+             int size;
+             TZ_LOCK;
+             size = strlen(_tzname[tim_p->tm_isdst > 0]);
+             for (i = 0; i < size; i++)
+               {
+                 if (count < maxsize - 1)
+                   s[count++] = _tzname[tim_p->tm_isdst > 0][i];
+                 else
+                   {
+                     TZ_UNLOCK;
+                     return 0;
+                   }
+               }
+             TZ_UNLOCK;
+           }
+         break;
+       case '%':
+         if (count < maxsize - 1)
+           s[count++] = '%';
+         else
+           return 0;
+         break;
+       }
+      if (*format)
+       format++;
+      else
+       break;
+    }
+  if (maxsize)
+    s[count] = '\0';
+
+  return count;
+}
diff --git a/libs/shttpd/compat_wince.h b/libs/shttpd/compat_wince.h
new file mode 100644 (file)
index 0000000..651ec50
--- /dev/null
@@ -0,0 +1,145 @@
+
+#ifndef INCLUDE_WINCE_COMPAT_H
+#define INCLUDE_WINCE_COMPAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*** ANSI C library ***/
+
+/* Missing ANSI C definitions */
+
+#define BUFSIZ 4096
+
+#define ENOMEM ERROR_NOT_ENOUGH_MEMORY
+#define EBADF ERROR_INVALID_HANDLE
+#define EINVAL ERROR_INVALID_PARAMETER
+#define ENOENT ERROR_FILE_NOT_FOUND
+#define ERANGE ERROR_INSUFFICIENT_BUFFER
+#define EINTR WSAEINTR
+
+/*
+ *     Because we need a per-thread errno, we define a function
+ *     pointer that we can call to return a pointer to the errno
+ *     for the current thread.  Then we define a macro for errno
+ *     that dereferences this function's result.
+ *
+ *     This makes it syntactically just like the "real" errno.
+ *
+ *     Using a function pointer allows us to use a very fast
+ *     function when there are no threads running and a slower
+ *     function when there are multiple threads running.
+ */
+void __WinCE_Errno_New_Thread(int *Errno_Pointer);
+void __WinCE_Errno_Thread_Exit(void);
+extern int *(*__WinCE_Errno_Pointer_Function)(void);
+
+#define        errno (*(*__WinCE_Errno_Pointer_Function)())
+
+char *strerror(int errnum);
+
+struct tm {
+       int tm_sec;     /* seconds after the minute - [0,59] */
+       int tm_min;     /* minutes after the hour - [0,59] */
+       int tm_hour;    /* hours since midnight - [0,23] */
+       int tm_mday;    /* day of the month - [1,31] */
+       int tm_mon;     /* months since January - [0,11] */
+       int tm_year;    /* years since 1900 */
+       int tm_wday;    /* days since Sunday - [0,6] */
+       int tm_yday;    /* days since January 1 - [0,365] */
+       int tm_isdst;   /* daylight savings time flag */
+};
+
+struct tm *gmtime(const time_t *TimeP); /* for future use */
+struct tm *localtime(const time_t *TimeP);
+time_t mktime(struct tm *tm);
+time_t time(time_t *TimeP);
+
+size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *tim_p);
+
+int _wrename(const wchar_t *oldname, const wchar_t *newname);
+int _wremove(const wchar_t *filename);
+
+/* Environment variables are not supported */
+#define getenv(x) (NULL)
+
+/* Redefine fileno so that it returns an integer */
+#undef fileno
+#define fileno(f) (int)_fileno(f)
+
+/* Signals are not supported */
+#define signal(num, handler) (0)
+#define SIGTERM 0
+#define SIGINT 0
+
+
+/*** POSIX API ***/
+
+/* Missing POSIX definitions */
+
+#define FILENAME_MAX MAX_PATH
+
+struct _stat {
+       unsigned long st_size;
+       unsigned long st_ino;
+       int st_mode;
+       unsigned long st_atime;
+       unsigned long st_mtime;
+       unsigned long st_ctime;
+       unsigned short st_dev;
+       unsigned short st_nlink;
+       unsigned short st_uid;
+       unsigned short st_gid;
+};
+
+#define S_IFMT   0170000
+#define S_IFDIR  0040000
+#define S_IFREG  0100000
+#define S_IEXEC  0000100
+#define S_IWRITE 0000200 
+#define S_IREAD  0000400
+
+#define _S_IFDIR S_IFDIR       /* MSVCRT compatibilit */
+
+int _fstat(int handle, struct _stat *buffer);
+int _wstat(const wchar_t *path, struct _stat *buffer);
+
+#define stat _stat     /* NOTE: applies to _stat() and also struct _stat */
+#define fstat _fstat
+
+#define        O_RDWR          (1<<0)
+#define        O_RDONLY        (2<<0)
+#define        O_WRONLY        (3<<0)
+#define        O_MODE_MASK     (3<<0)
+#define        O_TRUNC         (1<<2)
+#define        O_EXCL          (1<<3)
+#define        O_CREAT         (1<<4)
+#define O_BINARY 0
+
+int _wopen(const wchar_t *filename, int oflag, ...);
+int _close(int handle);
+int _write(int handle, const void *buffer, unsigned int count);
+int _read(int handle, void *buffer, unsigned int count);
+long _lseek(int handle, long offset, int origin);
+
+#define close _close
+#define write _write
+#define read _read
+#define lseek _lseek
+
+/* WinCE has only a Unicode version of this function */
+FILE *fdopen(int handle, const char *mode);
+
+int _wmkdir(const wchar_t *dirname);
+
+/* WinCE has no concept of current directory so we return a constant path */
+wchar_t *_wgetcwd(wchar_t *buffer, int maxlen);
+
+#define freopen(path, mode, stream) assert(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDE_WINCE_COMPAT_H */
diff --git a/libs/shttpd/config.c b/libs/shttpd/config.c
new file mode 100644 (file)
index 0000000..dcc6dad
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * 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"
+
+/*
+ * 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_ports", "Listening ports", set_str,
+               OFS(ports), "ports", LISTENING_PORTS, 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 */
+#if !defined(NO_SSI)
+       {'S', "ssi_extensions", "SSI extensions", set_str,
+               OFS(ssi_extensions), "ext_list", SSI_EXT, OPT_ADVANCED},
+#endif /* NO_SSI */
+       {'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
+       {'B', "auto_start", "Autostart with Windows", set_int,
+               OFS(auto_start), BOOL_OPT, "1", OPT_BOOL},
+#else
+       {'I', "inetd_mode", "Inetd mode", set_int,
+               OFS(inetd_mode), BOOL_OPT, NULL, OPT_BOOL       },
+       {'u', "runtime_uid", "Run as user", set_str,
+               OFS(uid), "user_name", NULL, 0          },
+#endif /* _WIN32 */
+       {0,   NULL, NULL, NULL, 0, NULL, NULL, 0        }
+};
+
+static const struct opt *
+find_option(int sw, const char *name)
+{
+       const struct opt        *opt;
+
+       for (opt = options; opt->sw != 0; opt++)
+               if (sw == opt->sw || (name && strcmp(opt->name, name) == 0))
+                       return (opt);
+
+       return (NULL);
+}
+
+static void
+set_option(const struct opt *opt, const char *val, char **tmpvars)
+{
+       tmpvars += opt - options;
+
+       if (*tmpvars != NULL)
+               free(*tmpvars);
+
+       *tmpvars = my_strdup(val);
+}
+
+/*
+ * Initialize shttpd context
+ */
+static void
+initialize_context(struct shttpd_ctx *ctx, const char *config_file,
+               int argc, char *argv[], char **tmpvars)
+{
+       char                    line[FILENAME_MAX], root[FILENAME_MAX],
+                                       var[sizeof(line)], val[sizeof(line)];
+       const char              *arg;
+       size_t                  i;
+       const struct opt        *opt;
+       FILE                    *fp;
+       struct tm               *tm;
+
+       current_time = time(NULL);
+       tm = localtime(&current_time);
+       tz_offset = 0;
+#if 0
+       tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
+#endif
+
+       (void) memset(ctx, 0, sizeof(*ctx));
+
+       ctx->start_time = current_time;
+       InitializeCriticalSection(&ctx->mutex);
+
+       LL_INIT(&ctx->connections);
+       LL_INIT(&ctx->mime_types);
+       LL_INIT(&ctx->registered_uris);
+       LL_INIT(&ctx->uri_auths);
+       LL_INIT(&ctx->error_handlers);
+
+#if !defined(NO_SSI)
+       LL_INIT(&ctx->ssi_funcs);
+#endif /* NO_SSI */
+
+       /* First pass: set the defaults */
+       for (opt = options; opt->sw != 0; opt++)
+               if (tmpvars[opt - options] == NULL && 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]);
+               }
+       
+       /* 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);
+}
diff --git a/libs/shttpd/defs.h b/libs/shttpd/defs.h
new file mode 100644 (file)
index 0000000..9c606f4
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * 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.38"          /* 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        LISTENING_PORTS "80"            /* Default listening ports      */
+#define        INDEX_FILES     "index.html index.htm index.php index.cgi"
+#define        CGI_EXT         ".cgi .pl .php" /* Default CGI extensions       */
+#define        SSI_EXT         ".shtml .shtm"  /* Default SSI extensions       */
+#define        REALM           "mydomain.com"  /* Default authentication realm */
+#define        DELIM_CHARS     " ,"            /* Separators for lists         */
+
+#define        EXPIRE_TIME     3600            /* Expiration time, seconds     */
+#define        ENV_MAX         4096            /* Size of environment block    */
+#define        CGI_ENV_VARS    64              /* Maximum vars passed to CGI   */
+#define        URI_MAX         32768           /* Maximum URI size             */
+#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             loc_port;       /* Local port                   */
+       int             status;         /* Reply status code            */
+       int             method;         /* Request method               */
+       char            *uri;           /* Decoded URI                  */
+       unsigned long   major_version;  /* Major HTTP version number    */
+       unsigned long   minor_version;  /* Minor HTTP version number    */
+       char            *request;       /* Request line                 */
+       char            *headers;       /* Request headers              */
+       char            *query;         /* QUERY_STRING part of the URI */
+       char            *path_info;     /* PATH_INFO thing              */
+       const char      *mime_type;     /* Mime type                    */
+
+       struct headers  ch;             /* Parsed client headers        */
+
+       struct stream   loc;            /* Local stream                 */
+       struct stream   rem;            /* Remote stream                */
+
+#if !defined(NO_SSI)
+       void                    *ssi;   /* SSI descriptor               */
+#endif /* NO_SSI */
+};
+
+
+/*
+ * SHTTPD context
+ */
+struct shttpd_ctx {
+       time_t          start_time;     /* Start time                   */
+       int             nactive;        /* # of connections now         */
+       unsigned long   nrequests;      /* Requests made                */
+       uint64_t        in, out;        /* IN/OUT traffic counters      */
+       SSL_CTX         *ssl_ctx;       /* SSL context                  */
+       struct llhead   connections;    /* List of connections          */
+
+       struct llhead   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 */
+#if !defined(NO_SSI)
+       char    *ssi_extensions;        /* SSI file extensions          */
+       struct llhead   ssi_funcs;      /* SSI callback functions       */
+#endif /* NO_SSI */
+       char    *auth_realm;            /* Auth realm                   */
+       char    *global_passwd_file;    /* Global passwords file        */
+       char    *uid;                   /* Run as user                  */
+       char    *ports;                 /* Listening ports              */
+       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 */
+};
+
+/* 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 int     url_decode(const char *, int, char *dst, int);
+extern void    send_server_error(struct conn *, int code, const char *reason);
+extern int     get_headers_len(const char *buf, size_t buflen);
+extern void    parse_headers(const char *s, int len, struct headers *parsed);
+extern void    open_listening_ports(struct shttpd_ctx *ctx);
+
+/*
+ * 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
+ */
+extern void    my_strlcpy(register char *, register const char *, size_t);
+extern int     my_strncasecmp(register const char *,
+               register const char *, size_t);
+extern char    *my_strndup(const char *ptr, size_t len);
+extern char    *my_strdup(const char *str);
+extern int     my_snprintf(char *buf, size_t buflen, const char *fmt, ...);
+extern int     match_extension(const char *path, const char *ext_list);
+
+/*
+ * compat_*.c
+ */
+extern void    set_close_on_exec(int fd);
+extern int     set_non_blocking_mode(int fd);
+extern int     my_stat(const char *, struct stat *stp);
+extern int     my_open(const char *, int flags, int mode);
+extern int     my_remove(const char *);
+extern int     my_rename(const char *, const char *);
+extern int     my_mkdir(const char *, int);
+extern char *  my_getcwd(char *, int);
+extern int     spawn_process(struct conn *c, const char *prog,
+               char *envblk, char *envp[], int sock, const char *dir);
+
+/*
+ * io_*.c
+ */
+extern const struct io_class   io_file;
+extern const struct io_class   io_socket;
+extern const struct io_class   io_ssl;
+extern const struct io_class   io_cgi;
+extern const struct io_class   io_dir;
+extern const struct io_class   io_embedded;
+extern const struct io_class   io_ssi;
+
+extern int     put_dir(const char *path);
+extern void    get_dir(struct conn *c);
+extern void    get_file(struct conn *c, struct stat *stp);
+extern void    ssl_handshake(struct stream *stream);
+extern void    setup_embedded_stream(struct conn *, union variant, void *);
+extern struct registered_uri *is_registered_uri(struct shttpd_ctx *,
+               const char *uri);
+extern void    do_ssi(struct conn *);
+extern void    free_ssi_funcs(struct shttpd_ctx *ctx);
+
+/*
+ * auth.c
+ */
+extern int     check_authorization(struct conn *c, const char *path);
+extern int     is_authorized_for_put(struct conn *c);
+extern void    send_authorization_request(struct conn *c);
+extern int     edit_passwords(const char *fname, const char *domain,
+               const char *user, const char *pass);
+
+/*
+ * cgi.c
+ */
+extern int     run_cgi(struct conn *c, const char *prog);
+extern void    do_cgi(struct conn *c);
+
+#define CGI_REPLY      "HTTP/1.1     OK\r\n"
+#define        CGI_REPLY_LEN   (sizeof(CGI_REPLY) - 1)
+
+#endif /* DEFS_HEADER_DEFINED */
diff --git a/libs/shttpd/include/shttpd.h b/libs/shttpd/include/shttpd.h
new file mode 100644 (file)
index 0000000..ba349e8
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * $Id: shttpd.h,v 1.3 2007/04/11 13:01:53 drozd Exp $
+ */
+
+#ifndef SHTTPD_HEADER_INCLUDED
+#define        SHTTPD_HEADER_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct ubuf {
+       char            *buf;           /* Buffer pointer               */
+       int             len;            /* Size of a buffer             */
+       int             num_bytes;      /* Bytes processed by callback  */
+};
+
+/*
+ * This structure is passed to the user callback function
+ */
+struct shttpd_arg {
+       void            *priv;          /* Private! Do not touch!       */
+       void            *state;         /* User state                   */
+       void            *user_data;     /* User-defined data            */
+       struct ubuf     in;             /* Input is here, POST data     */
+       struct ubuf     out;            /* Output goes here             */
+       unsigned int    flags;
+#define        SHTTPD_END_OF_OUTPUT    1
+#define        SHTTPD_CONNECTION_ERROR 2
+#define        SHTTPD_MORE_POST_DATA   4
+#define        SHTTPD_POST_BUFFER_FULL 8
+#define        SHTTPD_SSI_EVAL_TRUE    16
+};
+
+/*
+ * User callback function. Called when certain registered URLs have been
+ * requested. These are the requirements to the callback function:
+ *
+ * 1. it must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
+ *     and record how many bytes are copied, into 'out.num_bytes'
+ * 2. it must not block the execution
+ * 3. it must set SHTTPD_END_OF_OUTPUT flag when finished
+ * 4. for POST requests, it must process the incoming data (in.buf) of length
+ *     'in.len', and set 'in.num_bytes', which is how many bytes of POST
+ *     data is read and can be discarded by SHTTPD.
+ */
+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, int is_ssl);
+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);
+void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
+               shttpd_callback_t func, void *user_data);
+
+/*
+ * The following three functions are for applications that need to
+ * load-balance the connections on their own. Many threads may be spawned
+ * with one SHTTPD context per thread. Boss thread may only wait for
+ * new connections by means of shttpd_accept(). Then it may scan thread
+ * pool for the idle thread by means of shttpd_active(), and add new
+ * connection to the context by means of shttpd_add().
+ */
+void shttpd_add_socket(struct shttpd_ctx *, int sock);
+int shttpd_accept(int lsn_sock, int milliseconds);
+int shttpd_active(struct shttpd_ctx *);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SHTTPD_HEADER_INCLUDED */
diff --git a/libs/shttpd/io.h b/libs/shttpd/io.h
new file mode 100644 (file)
index 0000000..d774cc2
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#ifndef IO_HEADER_INCLUDED
+#define        IO_HEADER_INCLUDED
+
+#include <assert.h>
+#include <stddef.h>
+
+/*
+ * I/O buffer descriptor
+ */
+struct io {
+       char            *buf;           /* IO Buffer                    */
+       size_t          size;           /* IO buffer size               */
+       size_t          head;           /* Bytes read                   */
+       size_t          tail;           /* Bytes written                */
+       size_t          total;          /* Total bytes read             */
+};
+
+static __inline void
+io_clear(struct io *io)
+{
+       assert(io->buf != NULL);
+       assert(io->size > 0);
+       io->total = io->tail = io->head = 0;
+}
+
+static __inline char *
+io_space(struct io *io)
+{
+       assert(io->buf != NULL);
+       assert(io->size > 0);
+       assert(io->head <= io->size);
+       return (io->buf + io->head);
+}
+
+static __inline char *
+io_data(struct io *io)
+{
+       assert(io->buf != NULL);
+       assert(io->size > 0);
+       assert(io->tail <= io->size);
+       return (io->buf + io->tail);
+}
+
+static __inline size_t
+io_space_len(const struct io *io)
+{
+       assert(io->buf != NULL);
+       assert(io->size > 0);
+       assert(io->head <= io->size);
+       return (io->size - io->head);
+}
+
+static __inline size_t
+io_data_len(const struct io *io)
+{
+       assert(io->buf != NULL);
+       assert(io->size > 0);
+       assert(io->head <= io->size);
+       assert(io->tail <= io->head);
+       return (io->head - io->tail);
+}
+
+static __inline void
+io_inc_tail(struct io *io, size_t n)
+{
+       assert(io->buf != NULL);
+       assert(io->size > 0);
+       assert(io->tail <= io->head);
+       assert(io->head <= io->size);
+       io->tail += n;
+       assert(io->tail <= io->head);
+       if (io->tail == io->head)
+               io->head = io->tail = 0;
+}
+
+static __inline void
+io_inc_head(struct io *io, size_t n)
+{
+       assert(io->buf != NULL);
+       assert(io->size > 0);
+       assert(io->tail <= io->head);
+       io->head += n;
+       io->total += n;
+       assert(io->head <= io->size);
+}
+
+#endif /* IO_HEADER_INCLUDED */
diff --git a/libs/shttpd/io_cgi.c b/libs/shttpd/io_cgi.c
new file mode 100644 (file)
index 0000000..f7efb64
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int
+write_cgi(struct stream *stream, const void *buf, size_t len)
+{
+       assert(stream->chan.sock != -1);
+       assert(stream->flags & FLAG_W);
+
+       return (send(stream->chan.sock, buf, len, 0));
+}
+
+static int
+read_cgi(struct stream *stream, void *buf, size_t len)
+{
+       struct headers  parsed;
+       char            status[4];
+       int             n;
+
+       assert(stream->chan.sock != -1);
+       assert(stream->flags & FLAG_R);
+
+       stream->flags &= ~FLAG_DONT_CLOSE;
+
+       n = recv(stream->chan.sock, buf, len, 0);
+
+       if (stream->flags & FLAG_HEADERS_PARSED)
+               return (n);
+
+       if (n <= 0 && ERRNO != EWOULDBLOCK) {
+               send_server_error(stream->conn, 500, "Error running CGI");
+               return (n);
+       }
+
+       /*
+        * CGI script may output Status: and Location: headers, which
+        * may alter the status code. Buffer in headers, parse
+        * them, send correct status code and then forward all data
+        * from CGI script back to the remote end.
+        * Reply line was alredy appended to the IO buffer in
+        * decide_what_to_do(), with blank status code.
+        */
+
+       stream->flags |= FLAG_DONT_CLOSE;
+       io_inc_head(&stream->io, n);
+
+       stream->headers_len = get_headers_len(stream->io.buf, stream->io.head);
+       if (stream->headers_len < 0) {
+               stream->flags &= ~FLAG_DONT_CLOSE;
+               send_server_error(stream->conn, 500, "Bad headers sent");
+               elog(E_LOG, stream->conn, "CGI script sent invalid headers: "
+                   "[%.*s]", stream->io.head - CGI_REPLY_LEN,
+                   stream->io.buf + CGI_REPLY_LEN);
+               return (0);
+       }
+
+       /*
+        * If we did not received full headers yet, we must not send any
+        * data read from the CGI back to the client. Suspend sending by
+        * setting tail = head, which tells that there is no data in IO buffer
+        */
+
+       if (stream->headers_len == 0) {
+               stream->io.tail = stream->io.head;
+               return (0);
+       }
+
+       /* Received all headers. Set status code for the connection. */
+       (void) memset(&parsed, 0, sizeof(parsed));
+       parse_headers(stream->io.buf, stream->headers_len, &parsed);
+       stream->content_len = parsed.cl.v_big_int;
+       stream->conn->status = (int) parsed.status.v_big_int;
+
+       /* If script outputs 'Location:' header, set status code to 302 */
+       if (parsed.location.v_vec.len > 0)
+               stream->conn->status = 302;
+
+       /*
+        * If script did not output neither 'Location:' nor 'Status' headers,
+        * set the default status code 200, which means 'success'.
+        */
+       if (stream->conn->status == 0)
+               stream->conn->status = 200;
+
+       /* Append the status line to the beginning of the output */
+       (void) my_snprintf(status, sizeof(status), "%3d", stream->conn->status);
+       (void) memcpy(stream->io.buf + 9, status, 3);
+       DBG(("read_cgi: content len %lu status %s",
+           stream->content_len, status));
+
+       /* Next time, pass output directly back to the client */
+       assert((big_int_t) stream->headers_len <= stream->io.total);
+       stream->io.total -= stream->headers_len;
+       stream->io.tail = 0;
+       stream->flags |= FLAG_HEADERS_PARSED;
+
+       /* Return 0 because we've already shifted the head */
+       return (0);
+}
+
+static void
+close_cgi(struct stream *stream)
+{
+       assert(stream->chan.sock != -1);
+       (void) closesocket(stream->chan.sock);
+}
+
+const struct io_class  io_cgi =  {
+       "cgi",
+       read_cgi,
+       write_cgi,
+       close_cgi
+};
diff --git a/libs/shttpd/io_dir.c b/libs/shttpd/io_dir.c
new file mode 100644 (file)
index 0000000..0534c62
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+/*
+ * For a given PUT path, create all intermediate subdirectories
+ * for given path. Return 0 if the path itself is a directory,
+ * or -1 on error, 1 if OK.
+ */
+int
+put_dir(const char *path)
+{
+       char            buf[FILENAME_MAX];
+       const char      *s, *p;
+       struct stat     st;
+       size_t          len;
+
+       for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
+               len = p - path;
+               assert(len < sizeof(buf));
+               (void) memcpy(buf, path, len);
+               buf[len] = '\0';
+
+               /* Try to create intermediate directory */
+               if (my_stat(buf, &st) == -1 && my_mkdir(buf, 0755) != 0)
+                       return (-1);
+
+               /* Is path itself a directory ? */
+               if (p[1] == '\0')
+                       return (0);
+       }
+
+       return (1);
+}
+
+static int
+read_dir(struct stream *stream, void *buf, size_t len)
+{
+       struct dirent   *dp = NULL;
+       char            file[FILENAME_MAX], line[FILENAME_MAX + 512],
+                               size[64], mod[64];
+       struct stat     st;
+       struct conn     *c = stream->conn;
+       int             n, nwritten = 0;
+       const char      *slash = "";
+
+       assert(stream->chan.dir.dirp != NULL);
+       assert(stream->conn->uri[0] != '\0');
+
+       do {
+               if (len < sizeof(line))
+                       break;
+
+               if ((dp = readdir(stream->chan.dir.dirp)) == NULL) {
+                       stream->flags |= FLAG_CLOSED;
+                       break;
+               }
+               DBG(("read_dir: %s", dp->d_name));
+
+               /* Do not show current dir and passwords file */
+               if (strcmp(dp->d_name, ".") == 0 ||
+                  strcmp(dp->d_name, HTPASSWD) == 0)
+                       continue;
+
+               (void) my_snprintf(file, sizeof(file),
+                   "%s%s%s", stream->chan.dir.path, slash, dp->d_name);
+               (void) my_stat(file, &st);
+               if (S_ISDIR(st.st_mode)) {
+                       my_snprintf(size,sizeof(size),"%s","&lt;DIR&gt;");
+               } else {
+                       if (st.st_size < 1024)
+                               (void) my_snprintf(size, sizeof(size),
+                                   "%lu", (unsigned long) st.st_size);
+                       else if (st.st_size < 1024 * 1024)
+                               (void) my_snprintf(size, sizeof(size), "%luk",
+                                   (unsigned long) (st.st_size >> 10)  + 1);
+                       else
+                               (void) my_snprintf(size, sizeof(size),
+                                   "%.1fM", (float) st.st_size / 1048576);
+               }
+               (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
+                       localtime(&st.st_mtime));
+
+               n = my_snprintf(line, sizeof(line),
+                   "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
+                   "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+                   c->uri, slash, dp->d_name, dp->d_name,
+                   S_ISDIR(st.st_mode) ? "/" : "", mod, size);
+               (void) memcpy(buf, line, n);
+               buf = (char *) buf + n;
+               nwritten += n;
+               len -= n;
+       } while (dp != NULL);
+
+       return (nwritten);
+}
+
+static void
+close_dir(struct stream *stream)
+{
+       assert(stream->chan.dir.dirp != NULL);
+       assert(stream->chan.dir.path != NULL);
+       (void) closedir(stream->chan.dir.dirp);
+       free(stream->chan.dir.path);
+}
+
+void
+get_dir(struct conn *c)
+{
+       if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) {
+               (void) free(c->loc.chan.dir.path);
+               send_server_error(c, 500, "Cannot open directory");
+       } else {
+               c->loc.io.head = my_snprintf(c->loc.io.buf, c->loc.io.size,
+                   "HTTP/1.1 200 OK\r\n"
+                   "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
+};
diff --git a/libs/shttpd/io_emb.c b/libs/shttpd/io_emb.c
new file mode 100644 (file)
index 0000000..42cd5c5
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * 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(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 = arg->out.buf + arg->out.num_bytes;
+       int             buflen = arg->out.len - arg->out.num_bytes, len = 0;
+       va_list         ap;
+
+       assert(buf <= io->buf + io->size);
+
+       if (buflen > 0) {
+               va_start(ap, fmt);
+               len = vsnprintf(buf, buflen, fmt, ap);
+               va_end(ap);
+
+               if (len < 0 || len > buflen)
+                       len = buflen;
+               arg->out.num_bytes += len;
+       }
+
+       return (len);
+}
+
+const char *
+shttpd_get_header(struct shttpd_arg *arg, const char *header_name)
+{
+       struct conn     *c = arg->priv;
+       char            *p, *s, *e;
+       size_t          len;
+
+       p = c->headers;
+       e = c->request + c->rem.headers_len;
+       len = strlen(header_name);
+
+       while (p < e) {
+               if ((s = strchr(p, '\n')) != NULL)
+                       s[s[-1] == '\r' ? -1 : 0] = '\0';
+               if (my_strncasecmp(header_name, p, len) == 0)
+                       return (p + len + 2);
+
+               p += strlen(p) + 1;
+       }
+
+       return (NULL);
+}
+
+const char *
+shttpd_get_env(struct shttpd_arg *arg, const char *env_name)
+{
+       struct conn     *c = arg->priv;
+       struct vec      *vec;
+
+       if (strcmp(env_name, "REQUEST_METHOD") == 0) {
+               return (known_http_methods[c->method].ptr);
+       } else if (strcmp(env_name, "REQUEST_URI") == 0) {
+               return (c->uri);
+       } else if (strcmp(env_name, "QUERY_STRING") == 0) {
+               return (c->query);
+       } else if (strcmp(env_name, "REMOTE_USER") == 0) {
+               vec = &c->ch.user.v_vec;
+               if (vec->len > 0) {
+                       ((char *) vec->ptr)[vec->len] = '\0';
+                       return (vec->ptr);
+               }
+       } else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
+               return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
+       }
+
+       return (NULL);
+}
+
+void
+shttpd_get_http_version(struct shttpd_arg *arg, unsigned long *major, unsigned long *minor)
+{
+       struct conn *c = arg->priv;
+       
+       *major = c->major_version;
+       *minor = c->minor_version;
+}
+
+void
+shttpd_register_uri(struct shttpd_ctx *ctx,
+               const char *uri, shttpd_callback_t callback, void *data)
+{
+       struct registered_uri   *e;
+
+       if ((e = malloc(sizeof(*e))) != NULL) {
+               e->uri                  = my_strdup(uri);
+               e->callback.v_func      = (void (*)(void)) callback;
+               e->callback_data        = data;
+               LL_TAIL(&ctx->registered_uris, &e->link);
+       }
+}
+
+#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 */
+                       url_decode(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 */
diff --git a/libs/shttpd/io_file.c b/libs/shttpd/io_file.c
new file mode 100644 (file)
index 0000000..d2b3fb6
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int
+write_file(struct stream *stream, const void *buf, size_t len)
+{
+       struct stat     st;
+       struct stream   *rem = &stream->conn->rem;
+       int             n, fd = stream->chan.fd;
+
+       assert(fd != -1);
+       n = write(fd, buf, len);
+
+       DBG(("put_file(%p, %d): %d bytes", (void *) stream, len, n));
+
+       if (n <= 0 || (rem->io.total >= (big_int_t) rem->headers_len)) {
+               (void) fstat(fd, &st);
+               stream->io.head = stream->headers_len =
+                   my_snprintf(stream->io.buf,
+                   stream->io.size, "HTTP/1.1 %d OK\r\n"
+                   "Content-Length: %lu\r\nConnection: close\r\n\r\n",
+                   stream->conn->status, st.st_size);
+               stop_stream(stream);
+       }
+
+       return (n);
+}
+
+static int
+read_file(struct stream *stream, void *buf, size_t len)
+{
+#ifdef USE_SENDFILE
+       struct  iovec   vec;
+       struct  sf_hdtr hd = {&vec, 1, NULL, 0}, *hdp = &hd;
+       int             sock, fd, n;
+       size_t          nbytes;
+       off_t           sent;
+
+       sock = stream->conn->rem.chan.sock;
+       fd = stream->chan.fd;
+
+       /* If this is the first call for this file, send the headers */
+       vec.iov_base = stream->io.buf;
+       vec.iov_len = stream->headers_len;
+       if (stream->io.total > 0)
+               hdp = NULL;
+
+       nbytes = stream->content_len - stream->io.total;
+       n = sendfile(fd, sock, lseek(fd, 0, SEEK_CUR), nbytes, hdp, &sent, 0);
+
+       if (n == -1 && ERRNO != EAGAIN) {
+               stream->flags &= ~FLAG_DONT_CLOSE;
+               return (n);
+       }
+
+       stream->conn->ctx->out += sent;
+
+       /* If we have sent the HTTP headers in this turn, clear them off */
+       if (stream->io.total == 0) {
+               assert(sent >= stream->headers_len);
+               sent -= stream->headers_len;
+               io_clear(&stream->io);
+       }
+
+       (void) lseek(fd, sent, SEEK_CUR);
+       stream->io.total += sent;
+       stream->flags |= FLAG_DONT_CLOSE;
+
+       return (0);
+#endif /* USE_SENDFILE */
+
+       assert(stream->chan.fd != -1);
+       return (read(stream->chan.fd, buf, len));
+}
+
+static void
+close_file(struct stream *stream)
+{
+       assert(stream->chan.fd != -1);
+       (void) close(stream->chan.fd);
+}
+
+void
+get_file(struct conn *c, struct stat *stp)
+{
+       char            date[64], lm[64], etag[64], range[64] = "";
+       size_t          n, status = 200;
+       unsigned long   r1, r2;
+       const char      *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";
+       big_int_t       cl; /* Content-Length */
+
+       if (c->mime_type == NULL)
+               c->mime_type = get_mime_type(c->ctx, c->uri, strlen(c->uri));
+       cl = (big_int_t) stp->st_size;
+
+       /* If Range: header specified, act accordingly */
+       if (c->ch.range.v_vec.len > 0 &&
+           (n = sscanf(c->ch.range.v_vec.ptr,"bytes=%lu-%lu",&r1, &r2)) > 0) {
+               status = 206;
+               (void) lseek(c->loc.chan.fd, r1, SEEK_SET);
+               cl = n == 2 ? r2 - r1 + 1: cl - r1;
+               (void) my_snprintf(range, sizeof(range),
+                   "Content-Range: bytes %lu-%lu/%lu\r\n",
+                   r1, r1 + cl - 1, (unsigned long) stp->st_size);
+               msg = "Partial Content";
+       }
+
+       /* Prepare Etag, Date, Last-Modified headers */
+       (void) strftime(date, sizeof(date), fmt, localtime(&current_time));
+       (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
+       (void) my_snprintf(etag, sizeof(etag), "%lx.%lx",
+           (unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
+
+       /*
+        * We do not do io_inc_head here, because it will increase 'total'
+        * member in io. We want 'total' to be equal to the content size,
+        * and exclude the headers length from it.
+        */
+       c->loc.io.head = c->loc.headers_len = my_snprintf(c->loc.io.buf,
+           c->loc.io.size,
+           "HTTP/1.1 %d %s\r\n"
+           "Date: %s\r\n"
+           "Last-Modified: %s\r\n"
+           "Etag: \"%s\"\r\n"
+           "Content-Type: %s\r\n"
+           "Content-Length: %lu\r\n"
+           "Connection: close\r\n"
+           "%s\r\n",
+           status, msg, date, lm, etag, c->mime_type, cl, range);
+
+       c->status = status;
+       c->loc.content_len = cl;
+       c->loc.io_class = &io_file;
+       c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
+
+       if (c->method == METHOD_HEAD)
+               stop_stream(&c->loc);
+}
+
+const struct io_class  io_file =  {
+       "file",
+       read_file,
+       write_file,
+       close_file
+};
diff --git a/libs/shttpd/io_socket.c b/libs/shttpd/io_socket.c
new file mode 100644 (file)
index 0000000..9e2b73e
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int
+read_socket(struct stream *stream, void *buf, size_t len)
+{
+       assert(stream->chan.sock != -1);
+       return (recv(stream->chan.sock, buf, len, 0));
+}
+
+static int
+write_socket(struct stream *stream, const void *buf, size_t len)
+{
+       assert(stream->chan.sock != -1);
+       return (send(stream->chan.sock, buf, len, 0));
+}
+
+static void
+close_socket(struct stream *stream)
+{
+       assert(stream->chan.sock != -1);
+       (void) closesocket(stream->chan.sock);
+}
+
+const struct io_class  io_socket =  {
+       "socket",
+       read_socket,
+       write_socket,
+       close_socket
+};
diff --git a/libs/shttpd/io_ssi.c b/libs/shttpd/io_ssi.c
new file mode 100644 (file)
index 0000000..84176e1
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2006,2007 Steven Johnson <sjohnson@sakuraindustries.com>
+ * Copyright (c) 2007 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_SSI)
+
+#define        CMDBUFSIZ       512             /* SSI command buffer size      */
+#define        NEST_MAX        6               /* Maximum nesting level        */
+
+struct ssi_func {
+       struct llhead   link;
+       void            *user_data;
+       char            *name;
+       shttpd_callback_t func;
+};
+
+struct ssi_inc {
+       int             state;          /* Buffering state              */
+       int             cond;           /* Conditional state            */
+       FILE            *fp;            /* Icluded file stream          */
+       char            buf[CMDBUFSIZ]; /* SSI command buffer           */
+       size_t          nbuf;           /* Bytes in a command buffer    */
+       FILE            *pipe;          /* #exec stream                 */
+       struct ssi_func func;           /* #call function               */
+};
+
+struct ssi {
+       struct conn     *conn;          /* Connection we belong to      */
+       int             nest;           /* Current nesting level        */
+       struct ssi_inc  incs[NEST_MAX]; /* Nested includes              */
+};
+
+enum { SSI_PASS, SSI_BUF, SSI_EXEC, SSI_CALL };
+enum { SSI_GO, SSI_STOP };             /* Conditional states           */
+
+static const struct vec        st = {"<!--#", 5};
+
+void
+shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
+               shttpd_callback_t func, void *user_data)
+{
+       struct ssi_func *e;
+
+       if ((e = malloc(sizeof(*e))) != NULL) {
+               e->name         = my_strdup(name);
+               e->func         = func;
+               e->user_data    = user_data;
+               LL_TAIL(&ctx->ssi_funcs, &e->link);
+       }
+}
+
+void
+free_ssi_funcs(struct shttpd_ctx *ctx)
+{
+       struct llhead   *lp, *tmp;
+       struct ssi_func *e;
+
+       LL_FOREACH_SAFE(&ctx->ssi_funcs, lp, tmp) {
+               e = LL_ENTRY(lp, struct ssi_func, link);
+               free(e->name);
+               free(e);
+       }
+}
+
+static const struct ssi_func *
+find_ssi_func(struct ssi *ssi, const char *name)
+{
+       struct ssi_func *e;
+       struct llhead   *lp;
+
+       LL_FOREACH(&ssi->conn->ctx->ssi_funcs, lp) {
+               e = LL_ENTRY(lp, struct ssi_func, link);
+               if (!strcmp(name, e->name))
+                       return (e);
+       }
+
+       return (NULL);
+}
+
+static void
+call(struct ssi *ssi, const char *name,
+               struct shttpd_arg *arg, char *buf, int len)
+{
+       const struct ssi_func   *ssi_func;
+
+       (void) memset(arg, 0, sizeof(*arg));
+
+       if ((ssi_func = find_ssi_func(ssi, name)) != NULL) {
+               arg->priv = ssi->conn;
+               arg->user_data = ssi_func->user_data;
+               arg->out.buf = buf;
+               arg->out.len = len;
+               ssi_func->func(arg);
+       }
+}
+
+static int
+evaluate(struct ssi *ssi, const char *name)
+{
+       struct shttpd_arg       arg;
+
+       call(ssi, name, &arg, NULL, 0);
+
+       return (arg.flags & SHTTPD_SSI_EVAL_TRUE);
+}
+
+static void
+pass(struct ssi_inc *inc, void *buf, int *n)
+{
+       if (inc->cond == SSI_GO) {
+               (void) memcpy(buf, inc->buf, inc->nbuf);
+               (*n) += inc->nbuf;
+       }
+       inc->nbuf = 0;
+       inc->state = SSI_PASS;
+}
+
+static int
+get_path(struct conn *conn, const char *src,
+               int src_len, char *dst, int dst_len)
+{
+       static struct vec       accepted[] = {
+               {"\"",          1},     /* Relative to webserver CWD    */
+               {"file=\"",     6},     /* Relative to current URI      */
+               {"virtual=\"",  9},     /* Relative to document root    */
+               {NULL,          0},
+       };
+       struct vec      *vec;
+       const char      *p;
+       int             len;
+
+       for (vec = accepted; vec->len > 0; vec++)
+               if (src_len > vec->len && !memcmp(src, vec->ptr, vec->len)) {
+                       src += vec->len;
+                       src_len -= vec->len;
+                       if ((p = memchr(src, '"', src_len)) == NULL)
+                               break;
+                       if (vec->len == 6) {
+                               len = my_snprintf(dst, dst_len, "%s%c%s",
+                                   conn->ctx->document_root, DIRSEP, conn->uri);
+                               while (len > 0 && dst[len] != '/')
+                                       len--;
+                               dst += len;
+                               dst_len -= len;
+                       } else if (vec->len == 9) {
+                               len = my_snprintf(dst, dst_len, "%s%c",
+                                   conn->ctx->document_root, DIRSEP);
+                               dst += len;
+                               dst_len -= len;
+                       }
+                       url_decode(src, p - src, dst, dst_len);
+                       return (1);
+               }
+
+       return (0);
+}
+
+static void
+do_include(struct ssi *ssi)
+{
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+       char            buf[FILENAME_MAX];
+       FILE            *fp;
+
+       assert(inc->nbuf >= 13);
+
+       if (inc->cond == SSI_STOP) {
+               /* Do nothing - conditional FALSE */
+       } else if (ssi->nest >= (int) NELEMS(ssi->incs) - 1) {
+               elog(E_LOG, ssi->conn,
+                   "ssi: #include: maximum nested level reached");
+       } else if (!get_path(ssi->conn,
+           inc->buf + 13, inc->nbuf - 13, buf, sizeof(buf))) {
+               elog(E_LOG, ssi->conn, "ssi: bad #include: [%.*s]",
+                   inc->nbuf, inc->buf);
+       } else if ((fp = fopen(buf, "r")) == NULL) {
+               elog(E_LOG, ssi->conn, 
+                   "ssi: fopen(%s): %s", buf, strerror(errno));
+       } else {
+               ssi->nest++;
+               ssi->incs[ssi->nest].fp = fp;
+               ssi->incs[ssi->nest].nbuf = 0;
+               ssi->incs[ssi->nest].cond = SSI_GO;
+       }
+}
+
+static char *
+trim_spaces(struct ssi_inc *inc)
+{
+       unsigned char   *p = inc->buf + inc->nbuf - 2;
+
+       /* Trim spaces from the right */
+       *p-- = '\0';
+       while (isspace(*p))
+               *p-- = '\0';
+
+       /* Shift pointer to the start of attributes */
+       for (p = inc->buf; !isspace(*p); p++);
+       while (*p && isspace(*p)) p++;
+
+       return (p);
+}
+
+static void
+do_if(struct ssi *ssi)
+{
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+       unsigned char   *name = trim_spaces(inc);
+
+       inc->cond = evaluate(ssi, name) ? SSI_GO : SSI_STOP;
+}
+static void
+do_elif(struct ssi *ssi)
+{
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+       unsigned char   *name = trim_spaces(inc);
+
+       if (inc->cond == SSI_STOP && evaluate(ssi, name))
+               inc->cond = SSI_GO;
+       else
+               inc->cond = SSI_STOP;
+}
+static void
+do_endif(struct ssi *ssi)
+{
+       ssi->incs[ssi->nest].cond = SSI_GO;
+}
+
+static void
+do_else(struct ssi *ssi)
+{
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+
+       inc->cond = inc->cond == SSI_GO ? SSI_STOP : SSI_GO;
+}
+
+static void
+do_call2(struct ssi *ssi, char *buf, int len, int *n)
+{
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+       struct shttpd_arg       arg;
+
+       call(ssi, inc->buf, &arg, buf, len);
+       (*n) += arg.out.num_bytes;
+       if (arg.flags & SHTTPD_END_OF_OUTPUT)
+               inc->state = SSI_PASS;
+}
+
+static void
+do_call(struct ssi *ssi, char *buf, int len, int *n)
+{
+       struct ssi_inc          *inc = ssi->incs + ssi->nest;
+       unsigned char           *name = trim_spaces(inc);
+
+       if (inc->cond == SSI_GO) {
+               (void) memmove(inc->buf, name, strlen(name) + 1);
+               inc->state = SSI_CALL;
+               do_call2(ssi, buf, len, n);
+       }
+}
+
+static void
+do_exec2(struct ssi *ssi, char *buf, int len, int *n)
+{
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+       int             i, ch;
+
+       for (i = 0; i < len; i++) {
+               if ((ch = fgetc(inc->pipe)) == EOF) {
+                       inc->state = SSI_PASS;
+                       (void) pclose(inc->pipe);
+                       inc->pipe = NULL;
+                       break;
+               }
+               *buf++ = ch;
+               (*n)++;
+       }
+}
+
+static void
+do_exec(struct ssi *ssi, char *buf, int len, int *n)
+{
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+       unsigned char   *e, *p;
+       char            cmd[sizeof(inc->buf)];
+
+       p = trim_spaces(inc);
+
+       if (inc->cond == SSI_STOP) {
+               /* Do nothing - conditional FALSE */
+       } else if (*p != '"' || (e = strchr(p + 1, '"')) == NULL) {
+               elog(E_LOG, ssi->conn, "ssi: bad exec(%s)", p);
+       } else if (!url_decode(p + 1, e - p - 1, cmd, sizeof(cmd))) {
+               elog(E_LOG, ssi->conn, "ssi: cannot url_decode: exec(%s)", p);
+       } else if ((inc->pipe = popen(cmd, "r")) == NULL) {
+               elog(E_LOG, ssi->conn, "ssi: popen(%s)", cmd);
+       } else {
+               inc->state = SSI_EXEC;
+               do_exec2(ssi, buf, len, n);
+       }
+}
+
+static const struct ssi_cmd {
+       struct vec      vec;
+       void (*func)();
+} known_ssi_commands [] = {
+       {{"include ",   8}, do_include  },
+       {{"if ",        3}, do_if       },
+       {{"elif ",      5}, do_elif     },
+       {{"else",       4}, do_else     },
+       {{"endif",      5}, do_endif    },
+       {{"call ",      5}, do_call     },
+       {{"exec ",      5}, do_exec     },
+       {{NULL,         0}, NULL        }
+};
+
+static void
+do_command(struct ssi *ssi, char *buf, size_t len, int *n)
+{
+       struct ssi_inc          *inc = ssi->incs + ssi->nest;
+       const struct ssi_cmd    *cmd;
+
+       assert(len > 0);
+       assert(inc->nbuf <= len);
+       inc->state = SSI_PASS;
+
+       for (cmd = known_ssi_commands; cmd->func != NULL; cmd++)
+               if (inc->nbuf > (size_t) st.len + cmd->vec.len &&
+                   !memcmp(inc->buf + st.len, cmd->vec.ptr, cmd->vec.len)) {
+                       cmd->func(ssi, buf, len, n);
+                       break;
+               }
+
+       if (cmd->func == NULL)
+               pass(inc, buf, n);
+
+       inc->nbuf = 0;
+}
+
+static int
+read_ssi(struct stream *stream, void *vbuf, size_t len)
+{
+       struct ssi      *ssi = stream->conn->ssi;
+       struct ssi_inc  *inc = ssi->incs + ssi->nest;
+       char            *buf = vbuf;
+       int             ch = EOF, n = 0;
+
+
+again:
+
+       if (inc->state == SSI_CALL)
+               do_call2(ssi, buf, len, &n);
+       else if (inc->state == SSI_EXEC)
+               do_exec2(ssi, buf, len, &n);
+
+       while (n + inc->nbuf < len && (ch = fgetc(inc->fp)) != EOF)
+       
+               switch (inc->state) {
+
+               case SSI_PASS:
+                       if (ch == '<') {
+                               inc->nbuf = 0;
+                               inc->buf[inc->nbuf++] = ch;
+                               inc->state = SSI_BUF;
+                       } else if (inc->cond == SSI_GO) {
+                               buf[n++] = ch;
+                       }
+                       break;
+
+               case SSI_BUF:
+                       if (inc->nbuf >= sizeof(inc->buf) - 1) {
+                               pass(inc, buf + n, &n);
+                       } else if (ch == '>' &&
+                           !memcmp(inc->buf + inc->nbuf - 2, "--", 2)) {
+                               do_command(ssi, buf + n, len - n, &n);
+                               inc = ssi->incs + ssi->nest;
+                       } else {
+                               inc->buf[inc->nbuf++] = ch;
+
+                               /* If not SSI tag, pass it */
+                               if (inc->nbuf <= (size_t) st.len &&
+                                   memcmp(inc->buf, st.ptr, inc->nbuf) != 0)
+                                       pass(inc, buf + n, &n);
+                       }
+                       break;
+
+               default:
+                       abort();
+                       break;
+               }
+
+       if (ssi->nest > 0 && n + inc->nbuf < len && ch == EOF) {
+               (void) fclose(inc->fp);
+               inc->fp = NULL;
+               ssi->nest--;
+               inc--;
+               goto again;
+       }
+       
+       return (n);
+}
+
+static void
+close_ssi(struct stream *stream)
+{
+       struct ssi      *ssi = stream->conn->ssi;
+       size_t          i;
+
+       for (i = 0; i < NELEMS(ssi->incs); i++) {
+               if (ssi->incs[i].fp != NULL)
+                       (void) fclose(ssi->incs[i].fp);
+               if (ssi->incs[i].pipe != NULL)
+                       (void) pclose(ssi->incs[i].pipe);
+       }
+
+       free(ssi);
+}
+
+void
+do_ssi(struct conn *c)
+{
+       char            date[64];
+       struct ssi      *ssi;
+
+       (void) strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT",
+           localtime(&current_time));
+
+       c->loc.io.head = c->loc.headers_len = my_snprintf(c->loc.io.buf,
+           c->loc.io.size,
+           "HTTP/1.1 200 OK\r\n"
+           "Date: %s\r\n"
+           "Content-Type: text/html\r\n"
+           "Connection: close\r\n\r\n",
+           date);
+
+       c->status = 200;
+       c->loc.io_class = &io_ssi;
+       c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
+
+       if (c->method == METHOD_HEAD) {
+               stop_stream(&c->loc);
+       } else if ((ssi = calloc(1, sizeof(struct ssi))) == NULL) {
+               send_server_error(c, 500, "Cannot allocate SSI descriptor");
+       } else {
+               ssi->incs[0].fp = fdopen(c->loc.chan.fd, "r");
+               ssi->conn = c;
+               c->ssi = ssi;
+       }
+}
+
+const struct io_class  io_ssi =  {
+       "ssi",
+       read_ssi,
+       NULL,
+       close_ssi
+};
+
+#endif /* NO_SSI */
diff --git a/libs/shttpd/io_ssl.c b/libs/shttpd/io_ssl.c
new file mode 100644 (file)
index 0000000..753257f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_SSL)
+struct ssl_func        ssl_sw[] = {
+       {"SSL_free",                    {0}},
+       {"SSL_accept",                  {0}},
+       {"SSL_connect",                 {0}},
+       {"SSL_read",                    {0}},
+       {"SSL_write",                   {0}},
+       {"SSL_get_error",               {0}},
+       {"SSL_set_fd",                  {0}},
+       {"SSL_new",                     {0}},
+       {"SSL_CTX_new",                 {0}},
+       {"SSLv23_server_method",        {0}},
+       {"SSL_library_init",            {0}},
+       {"SSL_CTX_use_PrivateKey_file", {0}},
+       {"SSL_CTX_use_certificate_file",{0}},
+       {NULL,                          {0}}
+};
+
+void
+ssl_handshake(struct stream *stream)
+{
+       int     n;
+
+       if ((n = SSL_accept(stream->chan.ssl.ssl)) == 0) {
+               n = SSL_get_error(stream->chan.ssl.ssl, n);
+               if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
+                       stream->flags |= FLAG_CLOSED;
+               elog(E_LOG, stream->conn, "SSL_accept error %d", n);
+       } else {
+               DBG(("handshake: SSL accepted"));
+               stream->flags |= FLAG_SSL_ACCEPTED;
+       }
+}
+
+static int
+read_ssl(struct stream *stream, void *buf, size_t len)
+{
+       int     nread = 0;
+
+       assert(stream->chan.ssl.ssl != NULL);
+
+       if (!(stream->flags & FLAG_SSL_ACCEPTED))
+               ssl_handshake(stream);
+
+       if (stream->flags & FLAG_SSL_ACCEPTED)
+               nread = SSL_read(stream->chan.ssl.ssl, buf, len);
+
+       return (nread);
+}
+
+static int
+write_ssl(struct stream *stream, const void *buf, size_t len)
+{
+       assert(stream->chan.ssl.ssl != NULL);
+       return (SSL_write(stream->chan.ssl.ssl, buf, len));
+}
+
+static void
+close_ssl(struct stream *stream)
+{
+       assert(stream->chan.ssl.sock != -1);
+       assert(stream->chan.ssl.ssl != NULL);
+       (void) closesocket(stream->chan.ssl.sock);
+       SSL_free(stream->chan.ssl.ssl);
+}
+
+const struct io_class  io_ssl =  {
+       "ssl",
+       read_ssl,
+       write_ssl,
+       close_ssl
+};
+#endif /* !NO_SSL */
diff --git a/libs/shttpd/llist.h b/libs/shttpd/llist.h
new file mode 100644 (file)
index 0000000..04e79bb
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#ifndef LLIST_HEADER_INCLUDED
+#define        LLIST_HEADER_INCLUDED
+
+/*
+ * Linked list macros.
+ */
+struct llhead {
+       struct llhead   *prev;
+       struct llhead   *next;
+};
+
+#define        LL_INIT(N)      ((N)->next = (N)->prev = (N))
+
+#define LL_HEAD(H)     struct llhead H = { &H, &H }
+
+#define LL_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N)))
+
+#define        LL_ADD(H, N)                                                    \
+       do {                                                            \
+               ((H)->next)->prev = (N);                                \
+               (N)->next = ((H)->next);                                \
+               (N)->prev = (H);                                        \
+               (H)->next = (N);                                        \
+       } while (0)
+
+#define        LL_TAIL(H, N)                                                   \
+       do {                                                            \
+               ((H)->prev)->next = (N);                                \
+               (N)->prev = ((H)->prev);                                \
+               (N)->next = (H);                                        \
+               (H)->prev = (N);                                        \
+       } while (0)
+
+#define        LL_DEL(N)                                                       \
+       do {                                                            \
+               ((N)->next)->prev = ((N)->prev);                        \
+               ((N)->prev)->next = ((N)->next);                        \
+               LL_INIT(N);                                             \
+       } while (0)
+
+#define        LL_EMPTY(N)     ((N)->next == (N))
+
+#define        LL_FOREACH(H,N) for (N = (H)->next; N != (H); N = (N)->next)
+
+#define LL_FOREACH_SAFE(H,N,T)                                         \
+       for (N = (H)->next, T = (N)->next; N != (H);                    \
+                       N = (T), T = (N)->next)
+
+#endif /* LLIST_HEADER_INCLUDED */
diff --git a/libs/shttpd/log.c b/libs/shttpd/log.c
new file mode 100644 (file)
index 0000000..4b43610
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+/*
+ * Log function
+ */
+void
+elog(int flags, struct conn *c, const char *fmt, ...)
+{
+       char    date[64], buf[URI_MAX];
+       int     len;
+       FILE    *fp = c == NULL ? NULL : c->ctx->error_log;
+       va_list ap;
+
+       /* Print to stderr */
+       if (c == NULL || c->ctx->inetd_mode == 0) {
+               va_start(ap, fmt);
+               (void) vfprintf(stderr, fmt, ap);
+               (void) fputc('\n', stderr);
+               va_end(ap);
+       }
+
+       strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y",
+           localtime(&current_time));
+
+       len = my_snprintf(buf, sizeof(buf),
+           "[%s] [error] [client %s] \"%s\" ",
+           date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
+           c && c->request ? c->request : "-");
+
+       va_start(ap, fmt);
+       (void) vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
+       va_end(ap);
+
+       buf[sizeof(buf) - 1] = '\0';
+
+       if (fp != NULL && (flags & (E_FATAL | E_LOG))) {
+               (void) fprintf(fp, "%s\n", buf);
+               (void) fflush(fp);
+       }
+
+#if defined(_WIN32) && !defined(NO_GUI)
+       {
+               extern HWND     hLog;
+
+               if (hLog != NULL)
+                       SendMessage(hLog, WM_APP, 0, (LPARAM) buf);
+       }
+#endif /* _WIN32 */
+
+       if (flags & E_FATAL)
+               exit(EXIT_FAILURE);
+}
+
+void
+log_access(FILE *fp, const struct conn *c)
+{
+       static const struct vec dash = {"-", 1};
+
+       const struct vec        *user = &c->ch.user.v_vec;
+       const struct vec        *referer = &c->ch.referer.v_vec;
+       const struct vec        *user_agent = &c->ch.useragent.v_vec;
+       char                    date[64], buf[URI_MAX], *q1 = "\"", *q2 = "\"";
+
+       if (user->len == 0)
+               user = &dash;
+
+       if (referer->len == 0) {
+               referer = &dash;
+               q1 = "";
+       }
+
+       if (user_agent->len == 0) {
+               user_agent = &dash;
+               q2 = "";
+       }
+
+       (void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
+                       localtime(&current_time));
+
+       (void) my_snprintf(buf, sizeof(buf),
+           "%s - %.*s [%s %+05d] \"%s\" %d %lu %s%.*s%s %s%.*s%s",
+           inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr,
+           date, tz_offset, c->request ? c->request : "-",
+           c->status, (unsigned long) c->loc.io.total,
+           q1, referer->len, referer->ptr, q1,
+           q2, user_agent->len, user_agent->ptr, q2);
+
+       if (fp != NULL) {
+               (void) fprintf(fp, "%s\n", buf);
+               (void) fflush(fp);
+       }
+
+#if defined(_WIN32) && !defined(NO_GUI)
+       {
+               extern HWND     hLog;
+
+               if (hLog != NULL)
+                       SendMessage(hLog, WM_APP, 0, (LPARAM) buf);
+       }
+#endif /* _WIN32 */
+}
diff --git a/libs/shttpd/md5.c b/libs/shttpd/md5.c
new file mode 100644 (file)
index 0000000..ddd90e1
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "defs.h"
+
+#ifndef HAVE_MD5
+#if __BYTE_ORDER == 1234
+#define byteReverse(buf, len)  /* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+       uint32_t t;
+       do {
+               t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+                       ((unsigned) buf[1] << 8 | buf[0]);
+               *(uint32_t *) buf = t;
+               buf += 4;
+       } while (--longs);
+}
+#endif /* __BYTE_ORDER */
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5_CTX *ctx)
+{
+       ctx->buf[0] = 0x67452301;
+       ctx->buf[1] = 0xefcdab89;
+       ctx->buf[2] = 0x98badcfe;
+       ctx->buf[3] = 0x10325476;
+
+       ctx->bits[0] = 0;
+       ctx->bits[1] = 0;
+}
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+       register uint32_t a, b, c, d;
+
+       a = buf[0];
+       b = buf[1];
+       c = buf[2];
+       d = buf[3];
+
+       MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+       MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+       MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+       MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+       MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+       MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+       MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+       MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+       MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+       MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+       MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+       MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+       MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+       MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+       MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+       MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+       MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+       MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+       MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+       MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+       MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+       MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+       MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+       MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+       MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+       MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+       MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+       MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+       MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+       MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+       MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+       MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+       MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+       MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+       MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+       MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+       MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+       MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+       MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+       MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+       MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+       MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+       MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+       MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+       MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+       MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+       MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+       MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+       MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+       MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+       MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+       MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+       MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+       MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+       MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+       MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+       MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+       MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+       MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+       MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+       MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+       MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+       MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+       MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+       buf[0] += a;
+       buf[1] += b;
+       buf[2] += c;
+       buf[3] += d;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
+{
+       uint32_t t;
+
+       /* Update bitcount */
+
+       t = ctx->bits[0];
+       if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+               ctx->bits[1]++;         /* Carry from low to high */
+       ctx->bits[1] += len >> 29;
+
+       t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
+
+       /* Handle any leading odd-sized chunks */
+
+       if (t) {
+               unsigned char *p = (unsigned char *) ctx->in + t;
+
+               t = 64 - t;
+               if (len < t) {
+                       memcpy(p, buf, len);
+                       return;
+               }
+               memcpy(p, buf, t);
+               byteReverse(ctx->in, 16);
+               MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+               buf += t;
+               len -= t;
+       }
+       /* Process data in 64-byte chunks */
+
+       while (len >= 64) {
+               memcpy(ctx->in, buf, 64);
+               byteReverse(ctx->in, 16);
+               MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+               buf += 64;
+               len -= 64;
+       }
+
+       /* Handle any remaining bytes of data. */
+
+       memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+       unsigned count;
+       unsigned char *p;
+
+       /* Compute number of bytes mod 64 */
+       count = (ctx->bits[0] >> 3) & 0x3F;
+
+       /* Set the first char of padding to 0x80.  This is safe since there is
+          always at least one byte free */
+       p = ctx->in + count;
+       *p++ = 0x80;
+
+       /* Bytes of padding needed to make 64 bytes */
+       count = 64 - 1 - count;
+
+       /* Pad out to 56 mod 64 */
+       if (count < 8) {
+               /* Two lots of padding:  Pad the first block to 64 bytes */
+               memset(p, 0, count);
+               byteReverse(ctx->in, 16);
+               MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+               /* Now fill the next block with 56 bytes */
+               memset(ctx->in, 0, 56);
+       } else {
+               /* Pad block to 56 bytes */
+               memset(p, 0, count - 8);
+       }
+       byteReverse(ctx->in, 14);
+
+       /* Append length in bits and transform */
+       ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+       ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+       MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+       byteReverse((unsigned char *) ctx->buf, 4);
+       memcpy(digest, ctx->buf, 16);
+       memset((char *) ctx, 0, sizeof(ctx));   /* In case it's sensitive */
+}
+
+#endif /* !HAVE_MD5 */
diff --git a/libs/shttpd/md5.h b/libs/shttpd/md5.h
new file mode 100644 (file)
index 0000000..fcca00e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#ifndef MD5_HEADER_INCLUDED
+#define        MD5_HEADER_INCLUDED
+
+typedef struct MD5Context {
+       uint32_t        buf[4];
+       uint32_t        bits[2];
+       unsigned char   in[64];
+} MD5_CTX;
+
+extern void MD5Init(MD5_CTX *ctx);
+extern void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len);
+extern void MD5Final(unsigned char digest[16], MD5_CTX *ctx);
+
+#endif /*MD5_HEADER_INCLUDED */
diff --git a/libs/shttpd/mime_type.c b/libs/shttpd/mime_type.c
new file mode 100644 (file)
index 0000000..61f8150
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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 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);
+}
diff --git a/libs/shttpd/shttpd.c b/libs/shttpd/shttpd.c
new file mode 100644 (file)
index 0000000..36372d2
--- /dev/null
@@ -0,0 +1,1165 @@
+/*
+ * 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.
+ */
+
+/*
+ * Small and portable HTTP server, http://shttpd.sourceforge.net
+ * $Id: shttpd.c,v 1.10 2007/06/01 17:59:32 drozd Exp $
+ */
+
+#include "defs.h"
+
+time_t         current_time;   /* Current UTC time             */
+int            tz_offset;      /* Time zone offset from UTC    */
+
+static LL_HEAD(listeners);     /* List of listening sockets    */
+
+const struct vec known_http_methods[] = {
+       {"GET",         3},
+       {"POST",        4},
+       {"PUT",         3},
+       {"DELETE",      6},
+       {"HEAD",        4},
+       {NULL,          0}
+};
+
+struct listener {
+       struct llhead   link;
+       struct shttpd_ctx *ctx;         /* Context that socket belongs  */
+       int             sock;           /* Listening socket             */
+       int             is_ssl;         /* Should be SSL-ed             */
+};
+
+/*
+ * This structure tells how HTTP headers must be parsed.
+ * Used by parse_headers() function.
+ */
+#define        OFFSET(x)       offsetof(struct headers, x)
+static const struct http_header http_headers[] = {
+       {16, HDR_INT,    OFFSET(cl),            "Content-Length: "      },
+       {14, HDR_STRING, OFFSET(ct),            "Content-Type: "        },
+       {12, HDR_STRING, OFFSET(useragent),     "User-Agent: "          },
+       {19, HDR_DATE,   OFFSET(ims),           "If-Modified-Since: "   },
+       {15, HDR_STRING, OFFSET(auth),          "Authorization: "       },
+       {9,  HDR_STRING, OFFSET(referer),       "Referer: "             },
+       {8,  HDR_STRING, OFFSET(cookie),        "Cookie: "              },
+       {10, HDR_STRING, OFFSET(location),      "Location: "            },
+       {8,  HDR_INT,    OFFSET(status),        "Status: "              },
+       {7,  HDR_STRING, OFFSET(range),         "Range: "               },
+       {12, HDR_STRING, OFFSET(connection),    "Connection: "          },
+       {19, HDR_STRING, OFFSET(transenc),      "Transfer-Encoding: "   },
+       {0,  HDR_INT,    0,                     NULL                    }
+};
+
+struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
+
+int
+url_decode(const char *src, int src_len, char *dst, int dst_len)
+{
+       int     i, j, a, b;
+#define        HEXTOI(x)  (isdigit(x) ? x - '0' : x - 'W')
+
+       for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
+               switch (src[i]) {
+               case '%':
+                       if (isxdigit(((unsigned char *) src)[i + 1]) &&
+                           isxdigit(((unsigned char *) src)[i + 2])) {
+                               a = tolower(((unsigned char *)src)[i + 1]);
+                               b = tolower(((unsigned char *)src)[i + 2]);
+                               dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
+                               i += 2;
+                       } else {
+                               dst[j] = '%';
+                       }
+                       break;
+               case '+':
+                       dst[j] = ' ';
+                       break;
+               default:
+                       dst[j] = src[i];
+                       break;
+               }
+
+       dst[j] = '\0';  /* Null-terminate the destination */
+
+       return (j);
+}
+
+void
+shttpd_add_mime_type(struct shttpd_ctx *ctx, const char *ext, const char *mime)
+{
+       struct mime_type_link   *e;
+       const char              *error_msg = "shttpd_add_mime_type: no memory";
+
+       if ((e = malloc(sizeof(*e))) == NULL) {
+               elog(E_FATAL, 0, error_msg);
+       } else if ((e->ext= my_strdup(ext)) == NULL) {
+               elog(E_FATAL, 0, error_msg);
+       } else if ((e->mime = my_strdup(mime)) == NULL) {
+               elog(E_FATAL, 0, error_msg);
+       } else {
+               e->ext_len = strlen(ext);
+               LL_TAIL(&ctx->mime_types, &e->link);
+       }
+}
+
+
+static const char *
+is_alias(struct shttpd_ctx *ctx, const char *uri,
+               struct vec *a_uri, struct vec *a_path)
+{
+       const char      *p, *s = ctx->aliases;
+       size_t          len;
+
+       DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
+
+       FOR_EACH_WORD_IN_LIST(s, len) {
+               if ((p = memchr(s, '=', len)) != NULL &&
+                   memcmp(uri, s, p - s) == 0) {
+                       a_uri->ptr = s;
+                       a_uri->len = p - s;
+                       a_path->ptr = ++p;
+                       a_path->len = (s + len) - p;
+                       return (s);
+               }
+       }
+
+       return (NULL);
+}
+
+void
+stop_stream(struct stream *stream)
+{
+       if (stream->io_class != NULL && stream->io_class->close != NULL)
+               stream->io_class->close(stream);
+
+       stream->io_class= NULL;
+       stream->flags |= FLAG_CLOSED;
+       stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
+
+       DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
+           stream->conn->rem.chan.sock, 
+           stream->io_class ? stream->io_class->name : "(null)",
+           (unsigned long) stream->io.total, io_data_len(&stream->io)));
+}
+
+/*
+ * Setup listening socket on given port, return socket
+ */
+static int
+open_listening_port(int port)
+{
+       int             sock, on = 1;
+       struct usa      sa;
+
+#ifdef _WIN32
+       {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
+#endif /* _WIN32 */
+
+       sa.len                          = sizeof(sa.u.sin);
+       sa.u.sin.sin_family             = AF_INET;
+       sa.u.sin.sin_port               = htons((uint16_t) port);
+       sa.u.sin.sin_addr.s_addr        = htonl(INADDR_ANY);
+
+       if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
+               goto fail;
+       if (set_non_blocking_mode(sock) != 0)
+               goto fail;
+       if (setsockopt(sock, SOL_SOCKET,
+           SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
+               goto fail;
+       if (bind(sock, &sa.u.sa, sa.len) < 0)
+               goto fail;
+       if (listen(sock, 128) != 0)
+               goto fail;
+
+#ifndef _WIN32
+       (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
+#endif /* !_WIN32 */
+
+       return (sock);
+fail:
+       if (sock != -1)
+               (void) closesocket(sock);
+       elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
+       return (-1);
+}
+
+/*
+ * Check whether full request is buffered Return headers length, or 0
+ */
+int
+get_headers_len(const char *buf, size_t buflen)
+{
+       const char      *s, *e;
+       int             len = 0;
+
+       for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
+               /* Control characters are not allowed but >=128 is. */
+               if (!isprint(*(unsigned char *)s) && *s != '\r' && *s != '\n' && *(unsigned char *)s < 128)
+                       len = -1;
+               else if (s[0] == '\n' && s[1] == '\n')
+                       len = s - buf + 2;
+               else if (s[0] == '\n' && &s[1] < e &&
+                   s[1] == '\r' && s[2] == '\n')
+                       len = s - buf + 3;
+
+       return (len);
+}
+
+/*
+ * Send error message back to a client.
+ */
+void
+send_server_error(struct conn *c, int status, const char *reason)
+{
+#ifdef EMBEDDED
+       struct llhead           *lp;
+       struct error_handler    *e;
+
+       LL_FOREACH(&c->ctx->error_handlers, lp) {
+               e = LL_ENTRY(lp, struct error_handler, link);
+
+               if (e->code == status) {
+                       if (c->loc.io_class != NULL &&
+                           c->loc.io_class->close != NULL)
+                               c->loc.io_class->close(&c->loc);
+                       io_clear(&c->loc.io);
+                       setup_embedded_stream(c, e->callback, NULL);
+                       return;
+               }
+       }
+#endif /* EMBEDDED */
+
+       io_clear(&c->loc.io);
+       c->loc.headers_len = c->loc.io.head = my_snprintf(c->loc.io.buf,
+           c->loc.io.size, "HTTP/1.1 %d %s\r\n\r\n%d %s",
+           status, reason, status, reason);
+       c->status = status;
+       stop_stream(&c->loc);
+}
+
+/*
+ * Convert month to the month number. Return -1 on error, or month number
+ */
+static int
+montoi(const char *s)
+{
+       static const char *ar[] = {
+               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+       };
+       size_t  i;
+
+       for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
+               if (!strcmp(s, ar[i]))
+                       return (i);
+
+       return (-1);
+}
+
+/*
+ * Parse date-time string, and return the corresponding time_t value
+ */
+static time_t
+date_to_epoch(const char *s)
+{
+       struct tm       tm, *tmp;
+       char            mon[32];
+       int             sec, min, hour, mday, month, year;
+
+       (void) memset(&tm, 0, sizeof(tm));
+       sec = min = hour = mday = month = year = 0;
+
+       if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
+           &mday, mon, &year, &hour, &min, &sec) == 6) ||
+           (sscanf(s, "%d %3s %d %d:%d:%d",
+           &mday, mon, &year, &hour, &min, &sec) == 6) ||
+           (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
+           &mday, mon, &year, &hour, &min, &sec) == 6) ||
+           (sscanf(s, "%d-%3s-%d %d:%d:%d",
+           &mday, mon, &year, &hour, &min, &sec) == 6)) &&
+           (month = montoi(mon)) != -1) {
+               tm.tm_mday      = mday;
+               tm.tm_mon       = month;
+               tm.tm_year      = year;
+               tm.tm_hour      = hour;
+               tm.tm_min       = min;
+               tm.tm_sec       = sec;
+       }
+
+       if (tm.tm_year > 1900)
+               tm.tm_year -= 1900;
+       else if (tm.tm_year < 70)
+               tm.tm_year += 100;
+
+       /* Set Daylight Saving Time field */
+       tmp = localtime(&current_time);
+       tm.tm_isdst = tmp->tm_isdst;
+
+       return (mktime(&tm));
+}
+
+static void
+remove_double_dots(char *s)
+{
+       char    *p = s;
+
+       while (*s != '\0') {
+               *p++ = *s++;
+               if (s[-1] == '/')
+                       while (*s == '.' || *s == '/')
+                               s++;
+       }
+       *p = '\0';
+}
+
+void
+parse_headers(const char *s, int len, struct headers *parsed)
+{
+       const struct http_header        *h;
+       union variant                   *v;
+       const char                      *p, *e = s + len;
+
+       DBG(("parsing headers (len %d): [%.*s]", len, len, s));
+
+       /* Loop through all headers in the request */
+       while (s < e) {
+
+               /* Find where this header ends */
+               for (p = s; p < e && *p != '\n'; ) p++;
+
+               /* Is this header known to us ? */
+               for (h = http_headers; h->len != 0; h++)
+                       if (e - s > h->len &&
+                           !my_strncasecmp(s, h->name, h->len))
+                               break;
+
+               /* If the header is known to us, store its value */
+               if (h->len != 0) {
+
+                       /* Shift to where value starts */
+                       s += h->len;
+
+                       /* Find place to store the value */
+                       v = (union variant *) ((char *) parsed + h->offset);
+
+                       /* Fetch header value into the connection structure */
+                       if (h->type == HDR_STRING) {
+                               v->v_vec.ptr = s;
+                               v->v_vec.len = p - s;
+                               if (p[-1] == '\r' && v->v_vec.len > 0)
+                                       v->v_vec.len--;
+                       } else if (h->type == HDR_INT) {
+                               v->v_big_int = strtoul(s, NULL, 10);
+                       } else if (h->type == HDR_DATE) {
+                               v->v_time = date_to_epoch(s);
+                       }
+               }
+
+               s = p + 1;      /* Shift to the next header */
+       }
+}
+
+/*
+ * For given directory path, substitute it to valid index file.
+ * Return 0 if index file has been found, -1 if not found
+ */
+static int
+find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
+{
+       char            buf[FILENAME_MAX];
+       const char      *s = c->ctx->index_files;
+       size_t          len;
+
+       FOR_EACH_WORD_IN_LIST(s, len) {
+               my_snprintf(buf, sizeof(buf), "%s%c%.*s",path, DIRSEP, len, s);
+               if (my_stat(buf, stp) == 0) {
+                       my_strlcpy(path, buf, maxpath);
+                       c->mime_type = get_mime_type(c->ctx, s, len);
+                       return (0);
+               }
+       }
+
+       return (-1);
+}
+
+/*
+ * Try to open requested file, return 0 if OK, -1 if error.
+ * If the file is given arguments using PATH_INFO mechanism,
+ * initialize pathinfo pointer.
+ */
+static int
+get_path_info(struct conn *c, char *path, struct stat *stp)
+{
+       char    *p, *e;
+
+       if (my_stat(path, stp) == 0)
+               return (0);
+
+       p = path + strlen(path);
+       e = path + strlen(c->ctx->document_root) + 2;
+       
+       /* Strip directory parts of the path one by one */
+       for (; p > e; p--)
+               if (*p == '/') {
+                       *p = '\0';
+                       if (!my_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
+                               c->path_info = p + 1;
+                               return (0);
+                       } else {
+                               *p = '/';
+                       }
+               }
+
+       return (-1);
+}
+
+
+static void
+decide_what_to_do(struct conn *c)
+{
+       char            path[URI_MAX], buf[1024];
+       struct vec      alias_uri, alias_path;
+       struct stat     st;
+       int             rc;
+#ifdef EMBEDDED
+       struct registered_uri   *ruri;
+#endif /* EMBEDDED */
+
+       DBG(("decide_what_to_do: [%s]", c->uri));
+
+       if ((c->query = strchr(c->uri, '?')) != NULL)
+               *c->query++ = '\0';
+
+       url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
+       remove_double_dots(c->uri);
+       
+       if (strlen(c->uri) + strlen(c->ctx->document_root) >= sizeof(path)) {
+               send_server_error(c, 400, "URI is too long");
+               return;
+       }
+
+       (void) my_snprintf(path, sizeof(path), "%s%s",
+           c->ctx->document_root, c->uri);
+
+       /* User may use the aliases - check URI for mount point */
+       if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
+               (void) my_snprintf(path, sizeof(path), "%.*s%s",
+                   alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
+               DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
+                   alias_path.len, alias_path.ptr));
+       }
+
+#if !defined(NO_AUTH)
+       if (check_authorization(c, path) != 1) {
+               send_authorization_request(c);
+       } else
+#endif /* NO_AUTH */
+#ifdef EMBEDDED
+       if ((ruri = is_registered_uri(c->ctx, c->uri)) != NULL) {
+               setup_embedded_stream(c, ruri->callback, ruri->callback_data);
+       } else
+#endif /* EMBEDDED */
+       if (strstr(path, HTPASSWD)) {
+               /* Do not allow to view passwords files */
+               send_server_error(c, 403, "Forbidden");
+       } else
+#if !defined(NO_AUTH)
+       if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
+           (c->ctx->put_auth_file == NULL || !is_authorized_for_put(c))) {
+               send_authorization_request(c);
+       } else
+#endif /* NO_AUTH */
+       if (c->method == METHOD_PUT) {
+               c->status = my_stat(path, &st) == 0 ? 200 : 201;
+
+               if (c->ch.range.v_vec.len > 0) {
+                       send_server_error(c, 501, "PUT Range Not Implemented");
+               } else if ((rc = put_dir(path)) == 0) {
+                       send_server_error(c, 200, "OK");
+               } else if (rc == -1) {
+                       send_server_error(c, 500, "PUT Directory Error");
+               } else if (c->rem.content_len == 0) {
+                       send_server_error(c, 411, "Length Required");
+               } else if ((c->loc.chan.fd = my_open(path, O_WRONLY | O_BINARY |
+                   O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
+                       send_server_error(c, 500, "PUT Error");
+               } else {
+                       DBG(("PUT file [%s]", c->uri));
+                       c->loc.io_class = &io_file;
+                       c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
+               }
+       } else if (c->method == METHOD_DELETE) {
+               DBG(("DELETE [%s]", c->uri));
+               if (my_remove(path) == 0)
+                       send_server_error(c, 200, "OK");
+               else
+                       send_server_error(c, 500, "DELETE Error");
+       } else if (get_path_info(c, path, &st) != 0) {
+               send_server_error(c, 404, "Not Found");
+       } else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
+               (void) my_snprintf(buf, sizeof(buf),
+                       "Moved Permanently\r\nLocation: %s/", c->uri);
+               send_server_error(c, 301, buf);
+       } else if (S_ISDIR(st.st_mode) &&
+           find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
+           c->ctx->dirlist == 0) {
+               send_server_error(c, 403, "Directory Listing Denied");
+       } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist) {
+               if ((c->loc.chan.dir.path = my_strdup(path)) != NULL)
+                       get_dir(c);
+               else
+                       send_server_error(c, 500, "GET Directory Error");
+       } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist == 0) {
+               send_server_error(c, 403, "Directory listing denied");
+#if !defined(NO_CGI)
+       } else if (match_extension(path, c->ctx->cgi_extensions)) {
+               if (c->method != METHOD_POST && c->method != METHOD_GET) {
+                       send_server_error(c, 501, "Bad method ");
+               } else if ((run_cgi(c, path)) == -1) {
+                       send_server_error(c, 500, "Cannot exec CGI");
+               } else {
+                       do_cgi(c);
+               }
+#endif /* NO_CGI */
+#if !defined(NO_SSI)
+       } else if (match_extension(path, c->ctx->ssi_extensions)) {
+               if ((c->loc.chan.fd = my_open(path,
+                   O_RDONLY | O_BINARY, 0644)) == -1) {
+                       send_server_error(c, 500, "SSI open error");
+               } else {
+                       do_ssi(c);
+               }
+#endif /* NO_CGI */
+       } else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
+               send_server_error(c, 304, "Not Modified");
+       } else if ((c->loc.chan.fd = my_open(path,
+           O_RDONLY | O_BINARY, 0644)) != -1) {
+               get_file(c, &st);
+       } else {
+               send_server_error(c, 500, "Internal Error");
+       }
+}
+
+static int
+set_request_method(struct conn *c)
+{
+       const struct vec        *v;
+
+       assert(c->rem.io.head >= MIN_REQ_LEN);
+
+       /* Set the request method */
+       for (v = known_http_methods; v->ptr != NULL; v++)
+               if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
+                       c->method = v - known_http_methods;
+                       break;
+               }
+
+       return (v->ptr == NULL);
+}
+
+static void
+parse_http_request(struct conn *c)
+{
+       char    *s, *e, *p, *start;
+       char    *end_number;
+       int     uri_len, req_len;
+
+       s = c->rem.io.buf;
+       req_len = c->rem.headers_len = get_headers_len(s, c->rem.io.head);
+
+       if (req_len == 0 && io_space_len(&c->rem.io) == 0)
+               send_server_error(c, 400, "Request is too big");
+
+       if (req_len == 0)
+               return;
+       else if (req_len < MIN_REQ_LEN)
+               send_server_error(c, 400, "Bad request");
+       else if (set_request_method(c))
+               send_server_error(c, 501, "Method Not Implemented");
+       else if ((c->request = my_strndup(s, req_len)) == NULL)
+               send_server_error(c, 500, "Cannot allocate request");
+
+       if (c->loc.flags & FLAG_CLOSED)
+               return;
+
+       DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
+       c->rem.flags |= FLAG_HEADERS_PARSED;
+
+       /* Set headers pointer. Headers follow the request line */
+       c->headers = memchr(c->request, '\n', req_len);
+       assert(c->headers != NULL);
+       assert(c->headers < c->request + req_len);
+       if (c->headers > c->request && c->headers[-1] == '\r')
+               c->headers[-1] = '\0';
+       *c->headers++ = '\0';
+
+       /*
+        * Now make a copy of the URI, because it will be URL-decoded,
+        * and we need a copy of unmodified URI for the access log.
+        * First, we skip the REQUEST_METHOD and shift to the URI.
+        */
+       for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
+       while (p < e && *p == ' ') p++;
+
+       /* Now remember where URI starts, and shift to the end of URI */
+       for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
+       uri_len = p - start;
+       /* Skip space following the URI */
+       while (p < e && *p == ' ') p++;
+
+       /* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
+       if (strncmp(p, "HTTP/", 5) != 0) {
+               send_server_error(c, 400, "Bad HTTP version");
+               return;
+       }
+       p += 5;
+       /* Parse the HTTP major version number */
+       c->major_version = strtoul(p, &end_number, 10);
+       if (end_number == p || *end_number != '.') {
+               send_server_error(c, 400, "Bad HTTP major version");
+               return;
+       }
+       p = end_number + 1;
+       /* Parse the minor version number */
+       c->minor_version = strtoul(p, &end_number, 10);
+       if (end_number == p || *end_number != '\0') {
+               send_server_error(c, 400, "Bad HTTP minor version");
+               return;
+       }
+       /* Version must be <=1.1 */
+       if (c->major_version > 1 ||
+           (c->major_version == 1 && c->minor_version > 1)) {
+               send_server_error(c, 505, "HTTP version not supported");
+               return;
+       }
+
+       if (uri_len <= 0) {
+               send_server_error(c, 400, "Bad URI");
+       } else if ((c->uri = malloc(uri_len + 1)) == NULL) {
+               send_server_error(c, 500, "Cannot allocate URI");
+       } else {
+               my_strlcpy(c->uri, (char *) start, uri_len + 1);
+               parse_headers(c->headers,
+                   (c->request + req_len) - c->headers, &c->ch);
+
+               /* Remove the length of request from total, count only data */
+               assert(c->rem.io.total >= (big_int_t) req_len);
+               c->rem.io.total -= req_len;
+
+               c->rem.content_len = c->ch.cl.v_big_int;
+               io_inc_tail(&c->rem.io, req_len);
+
+               decide_what_to_do(c);
+       }
+}
+
+void
+shttpd_add_socket(struct shttpd_ctx *ctx, int sock)
+{
+       struct conn     *c;
+       struct usa      sa;
+       int             l = ctx->inetd_mode ? E_FATAL : E_LOG;
+#if !defined(NO_SSL)
+       SSL             *ssl = NULL;
+#endif /* NO_SSL */
+
+       sa.len = sizeof(sa.u.sin);
+       (void) set_non_blocking_mode(sock);
+
+       if (getpeername(sock, &sa.u.sa, &sa.len)) {
+               elog(l, NULL, "add_socket: %s", strerror(errno));
+#if !defined(NO_SSL)
+       } else if (ctx->ssl_ctx && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
+               elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
+               (void) closesocket(sock);
+       } else if (ctx->ssl_ctx && SSL_set_fd(ssl, sock) == 0) {
+               elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
+               (void) closesocket(sock);
+               SSL_free(ssl);
+#endif /* NO_SSL */
+       } else if ((c = calloc(1, sizeof(*c) + 2 * ctx->io_buf_size)) == NULL) {
+
+#if !defined(NO_SSL)
+               if (ssl)
+                       SSL_free(ssl);
+#endif /* NO_SSL */
+               (void) closesocket(sock);
+               elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
+       } else {
+               ctx->nrequests++;
+               c->rem.conn = c->loc.conn = c;
+               c->ctx          = ctx;
+               c->sa           = sa;
+               c->birth_time   = current_time;
+               c->expire_time  = current_time + EXPIRE_TIME;
+
+               (void) getsockname(sock, &sa.u.sa, &sa.len);
+               c->loc_port = sa.u.sin.sin_port;
+
+               set_close_on_exec(sock);
+
+               c->loc.io_class = NULL;
+       
+               c->rem.io_class = &io_socket;
+               c->rem.chan.sock = sock;
+
+               /* Set IO buffers */
+               c->loc.io.buf   = (char *) (c + 1);
+               c->rem.io.buf   = c->loc.io.buf + ctx->io_buf_size;
+               c->loc.io.size  = c->rem.io.size = ctx->io_buf_size;
+
+#if !defined(NO_SSL)
+               if (ssl) {
+                       c->rem.io_class = &io_ssl;
+                       c->rem.chan.ssl.sock = sock;
+                       c->rem.chan.ssl.ssl = ssl;
+                       ssl_handshake(&c->rem);
+               }
+#endif /* NO_SSL */
+
+               EnterCriticalSection(&ctx->mutex);
+               LL_TAIL(&ctx->connections, &c->link);
+               ctx->nactive++;
+               LeaveCriticalSection(&ctx->mutex);
+               
+               DBG(("%s:%hu connected (socket %d)",
+                   inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
+                   ntohs(sa.u.sin.sin_port), sock));
+       }
+}
+
+int
+shttpd_active(struct shttpd_ctx *ctx)
+{
+       return (ctx->nactive);
+}
+
+/*
+ * Setup a listening socket on given port. Return opened socket or -1
+ */
+int
+shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl)
+{
+       struct listener *l;
+       int             sock;
+
+       if ((sock = open_listening_port(port)) == -1) {
+               elog(E_FATAL, NULL, "cannot open port %d", port);
+       } else if ((l = calloc(1, sizeof(*l))) == NULL) {
+               (void) closesocket(sock);
+               elog(E_FATAL, NULL, "cannot allocate listener");
+       } else if (is_ssl && ctx->ssl_ctx == NULL) {
+               (void) closesocket(sock);
+               elog(E_FATAL, NULL, "cannot add SSL socket, "
+                   "please specify certificate file");
+       } else {
+               l->is_ssl = is_ssl;
+               l->sock = sock;
+               l->ctx  = ctx;
+               LL_TAIL(&listeners, &l->link);
+               DBG(("shttpd_listen: added socket %d", sock));
+       }
+
+       return (sock);
+}
+
+int
+shttpd_accept(int lsn_sock, int milliseconds)
+{
+       struct timeval  tv;
+       struct usa      sa;
+       fd_set          read_set;
+       int             sock = -1;
+       
+       tv.tv_sec       = milliseconds / 1000;
+       tv.tv_usec      = milliseconds % 1000;
+       sa.len          = sizeof(sa.u.sin);
+       FD_ZERO(&read_set);
+       FD_SET(lsn_sock, &read_set);
+       
+       if (select(lsn_sock + 1, &read_set, NULL, NULL, &tv) == 1)
+               sock = accept(lsn_sock, &sa.u.sa, &sa.len);
+
+       return (sock);
+}
+
+static void
+read_stream(struct stream *stream)
+{
+       int     n, len;
+
+       len = io_space_len(&stream->io);
+       assert(len > 0);
+
+       /* Do not read more that needed */
+       if (stream->content_len > 0 &&
+           stream->io.total + len > stream->content_len)
+               len = stream->content_len - stream->io.total;
+
+       /* Read from underlying channel */
+       n = stream->nread_last = stream->io_class->read(stream,
+           io_space(&stream->io), len);
+
+       if (n > 0)
+               io_inc_head(&stream->io, n);
+       else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
+               n = n;  /* Ignore EINTR and EAGAIN */
+       else if (!(stream->flags & FLAG_DONT_CLOSE))
+               stop_stream(stream);
+
+       DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
+           stream->conn->rem.chan.sock,
+           stream->io_class ? stream->io_class->name : "(null)",
+           n, len, (unsigned long) stream->io.total, ERRNO));
+
+       /*
+        * Close the local stream if everything was read
+        * XXX We do not close the remote stream though! It may be
+        * a POST data completed transfer, we do not want the socket
+        * to be closed.
+        */
+       if (stream->content_len > 0 && stream == &stream->conn->loc) {
+               assert(stream->io.total <= stream->content_len);
+               if (stream->io.total == stream->content_len)
+                       stop_stream(stream);
+       }
+
+       stream->conn->expire_time = current_time + EXPIRE_TIME;
+}
+
+static void
+write_stream(struct stream *from, struct stream *to)
+{
+       int     n, len;
+
+       len = io_data_len(&from->io);
+       assert(len > 0);
+
+       /* TODO: should be assert on CAN_WRITE flag */
+       n = to->io_class->write(to, io_data(&from->io), len);
+       to->conn->expire_time = current_time + EXPIRE_TIME;
+       DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
+           to->conn->rem.chan.sock,
+           to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
+
+       if (n > 0)
+               io_inc_tail(&from->io, n);
+       else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
+               n = n;  /* Ignore EINTR and EAGAIN */
+       else if (!(to->flags & FLAG_DONT_CLOSE))
+               stop_stream(to);
+}
+
+
+static void
+disconnect(struct conn *c)
+{
+       static const struct vec ka = {"keep-alive", 10};
+       int                     dont_close;
+
+       DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
+           c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
+
+#if !defined(_WIN32) || defined(NO_GUI)
+       if (c->ctx->access_log != NULL)
+#endif /* _WIN32 */
+                       log_access(c->ctx->access_log, c);
+
+       /* In inetd mode, exit if request is finished. */
+       if (c->ctx->inetd_mode)
+               exit(0);
+
+       if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
+               c->loc.io_class->close(&c->loc);
+
+       /*
+        * Check the "Connection: " header before we free c->request
+        * If it its 'keep-alive', then do not close the connection
+        */
+       dont_close =  c->ch.connection.v_vec.len >= ka.len &&
+           !my_strncasecmp(ka.ptr, c->ch.connection.v_vec.ptr, ka.len);
+
+       if (c->request)
+               free(c->request);
+       if (c->uri)
+               free(c->uri);
+
+       /* Handle Keep-Alive */
+       dont_close = 0;
+       if (dont_close) {
+               c->loc.io_class = NULL;
+               c->loc.flags = c->rem.flags = 0;
+               c->query = c->request = c->uri = c->path_info = NULL;
+               c->mime_type = NULL;
+               (void) memset(&c->ch, 0, sizeof(c->ch));
+               io_clear(&c->rem.io);
+               io_clear(&c->loc.io);
+               c->rem.io.total = c->loc.io.total = 0;
+       } else {
+               if (c->rem.io_class != NULL)
+                       c->rem.io_class->close(&c->rem);
+
+               EnterCriticalSection(&c->ctx->mutex);
+               LL_DEL(&c->link);
+               c->ctx->nactive--;
+               assert(c->ctx->nactive >= 0);
+               LeaveCriticalSection(&c->ctx->mutex);
+
+               free(c);
+       }
+}
+
+static void
+add_to_set(int fd, fd_set *set, int *max_fd)
+{
+       FD_SET(fd, set);
+       if (fd > *max_fd)
+               *max_fd = fd;
+}
+
+/*
+ * One iteration of server loop. This is the core of the data exchange.
+ */
+void
+shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
+{
+       struct llhead   *lp, *tmp;
+       struct listener *l;
+       struct conn     *c;
+       struct timeval  tv;                     /* Timeout for select() */
+       fd_set          read_set, write_set;
+       int             sock, max_fd = -1, msec = milliseconds;
+       struct usa      sa;
+
+       current_time = time(0);
+       FD_ZERO(&read_set);
+       FD_ZERO(&write_set);
+
+       /* Add listening sockets to the read set */
+       LL_FOREACH(&listeners, lp) {
+               l = LL_ENTRY(lp, struct listener, link);
+               FD_SET(l->sock, &read_set);
+               if (l->sock > max_fd)
+                       max_fd = l->sock;
+               DBG(("FD_SET(%d) (listening)", l->sock));
+       }
+
+       /* Multiplex streams */
+       LL_FOREACH(&ctx->connections, lp) {
+               c = LL_ENTRY(lp, struct conn, link);
+               
+               /* If there is a space in remote IO, check remote socket */
+               if (io_space_len(&c->rem.io))
+                       add_to_set(c->rem.chan.fd, &read_set, &max_fd);
+
+#if !defined(NO_CGI)
+               /*
+                * If there is a space in local IO, and local endpoint is
+                * CGI, check local socket for read availability
+                */
+               if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
+                   c->loc.io_class == &io_cgi)
+                       add_to_set(c->loc.chan.fd, &read_set, &max_fd);
+
+               /*
+                * If there is some data read from remote socket, and
+                * local endpoint is CGI, check local for write availability
+                */
+               if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
+                   c->loc.io_class == &io_cgi)
+                       add_to_set(c->loc.chan.fd, &write_set, &max_fd);
+#endif /* NO_CGI */
+
+               /*
+                * If there is some data read from local endpoint, check the
+                * remote socket for write availability
+                */
+               if (io_data_len(&c->loc.io))
+                       add_to_set(c->rem.chan.fd, &write_set, &max_fd);
+
+               if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
+                   (c->loc.flags & FLAG_ALWAYS_READY))
+                       msec = 0;
+               
+               if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
+                   (c->loc.flags & FLAG_ALWAYS_READY))
+                       msec = 0;
+       }
+
+       tv.tv_sec = msec / 1000;
+       tv.tv_usec = msec % 1000;
+
+       /* Check IO readiness */
+       if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
+#ifdef _WIN32
+               /*
+                * On windows, if read_set and write_set are empty,
+                * select() returns "Invalid parameter" error
+                * (at least on my Windows XP Pro). So in this case,
+                * we sleep here.
+                */
+               Sleep(milliseconds);
+#endif /* _WIN32 */
+               DBG(("select: %d", ERRNO));
+               return;
+       }
+
+       /* Check for incoming connections on listener sockets */
+       LL_FOREACH(&listeners, lp) {
+               l = LL_ENTRY(lp, struct listener, link);
+               if (!FD_ISSET(l->sock, &read_set))
+                       continue;
+               do {
+                       sa.len = sizeof(sa.u.sin);
+                       if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1) {
+#if defined(_WIN32)
+                               shttpd_add_socket(ctx, sock);
+#else
+                               if (sock < (int) FD_SETSIZE) {
+                                       shttpd_add_socket(ctx, sock);
+                               } else {
+                                       elog(E_LOG, NULL,
+                                          "shttpd_poll: ctx %p: disarding "
+                                          "socket %d, too busy", ctx, sock);
+                                       (void) closesocket(sock);
+                               }
+#endif /* _WIN32 */
+                       }
+               } while (sock != -1);
+       }
+
+       /* Process all connections */
+       LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
+               c = LL_ENTRY(lp, struct conn, link);
+
+               /* Read from remote end if it is ready */
+               if (FD_ISSET(c->rem.chan.fd, &read_set) &&
+                   io_space_len(&c->rem.io))
+                       read_stream(&c->rem);
+
+               /* If the request is not parsed yet, do so */
+               if (!(c->rem.flags & FLAG_HEADERS_PARSED))
+                       parse_http_request(c);
+
+               DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
+                   io_data_len(&c->loc.io), io_data(&c->loc.io)));
+               DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
+                   io_data_len(&c->rem.io), io_data(&c->rem.io)));
+
+               /* Read from the local end if it is ready */
+               if (io_space_len(&c->loc.io) &&
+                   ((c->loc.flags & FLAG_ALWAYS_READY)
+                   
+#if !defined(NO_CGI)
+                   ||(c->loc.io_class == &io_cgi &&
+                    FD_ISSET(c->loc.chan.fd, &read_set))
+#endif /* NO_CGI */
+                   ))
+                       read_stream(&c->loc);
+
+               if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
+                   c->loc.io_class != NULL && c->loc.io_class->write != NULL)
+                       write_stream(&c->rem, &c->loc);
+
+               if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
+                       write_stream(&c->loc, &c->rem); 
+
+               if (c->rem.nread_last > 0)
+                       c->ctx->in += c->rem.nread_last;
+               if (c->loc.nread_last > 0)
+                       c->ctx->out += c->loc.nread_last;
+
+               /* Check whether we should close this connection */
+               if ((current_time > c->expire_time) ||
+                   (c->rem.flags & FLAG_CLOSED) ||
+                   ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
+                       disconnect(c);
+       }
+}
+
+/*
+ * Deallocate shttpd object, free up the resources
+ */
+void
+shttpd_fini(struct shttpd_ctx *ctx)
+{
+       struct llhead           *lp, *tmp;
+       struct mime_type_link   *mtl;
+       struct conn             *c;
+       struct listener         *l;
+       struct registered_uri   *ruri;
+
+       /* Free configured mime types */
+       LL_FOREACH_SAFE(&ctx->mime_types, lp, tmp) {
+               mtl = LL_ENTRY(lp, struct mime_type_link, link);
+               free(mtl->mime);
+               free(mtl->ext);
+               free(mtl);
+       }
+
+       /* Free all connections */
+       LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
+               c = LL_ENTRY(lp, struct conn, link);
+               disconnect(c);
+       }
+
+       /* Free registered URIs (must be done after disconnect()) */
+       LL_FOREACH_SAFE(&ctx->registered_uris, lp, tmp) {
+               ruri = LL_ENTRY(lp, struct registered_uri, link);
+               free((void *)ruri->uri);
+               free(ruri);
+       }
+
+       /* Free listener sockets for this context */
+       LL_FOREACH_SAFE(&listeners, lp, tmp) {
+               l = LL_ENTRY(lp, struct listener, link);
+               (void) closesocket(l->sock);
+               LL_DEL(&l->link);
+               free(l);
+       }
+
+#if !defined(NO_SSI)
+       free_ssi_funcs(ctx);
+#endif /* NO_SSI */
+
+       if (ctx->access_log)            (void) fclose(ctx->access_log);
+       if (ctx->error_log)             (void) fclose(ctx->error_log);
+       if (ctx->put_auth_file)         free(ctx->put_auth_file);
+       if (ctx->document_root)         free(ctx->document_root);
+       if (ctx->index_files)           free(ctx->index_files);
+       if (ctx->aliases)               free(ctx->aliases);
+#if !defined(NO_CGI)
+       if (ctx->cgi_vars)              free(ctx->cgi_vars);
+       if (ctx->cgi_extensions)        free(ctx->cgi_extensions);
+       if (ctx->cgi_interpreter)       free(ctx->cgi_interpreter);
+#endif /* NO_CGI */
+       if (ctx->auth_realm)            free(ctx->auth_realm);
+       if (ctx->global_passwd_file)    free(ctx->global_passwd_file);
+       if (ctx->uid)                   free(ctx->uid);
+
+       /* TODO: free SSL context */
+
+       free(ctx);
+}
+
+void
+open_listening_ports(struct shttpd_ctx *ctx)
+{
+       const char      *p = ctx->ports;
+       int             len, is_ssl;
+       
+       FOR_EACH_WORD_IN_LIST(p, len) {
+               is_ssl = p[len - 1] == 's' ? 1 : 0;
+               if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
+                       elog(E_FATAL, NULL,
+                           "Cannot open socket on port %d", atoi(p));
+       }
+}
diff --git a/libs/shttpd/shttpd.h b/libs/shttpd/shttpd.h
new file mode 100644 (file)
index 0000000..ba349e8
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * $Id: shttpd.h,v 1.3 2007/04/11 13:01:53 drozd Exp $
+ */
+
+#ifndef SHTTPD_HEADER_INCLUDED
+#define        SHTTPD_HEADER_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct ubuf {
+       char            *buf;           /* Buffer pointer               */
+       int             len;            /* Size of a buffer             */
+       int             num_bytes;      /* Bytes processed by callback  */
+};
+
+/*
+ * This structure is passed to the user callback function
+ */
+struct shttpd_arg {
+       void            *priv;          /* Private! Do not touch!       */
+       void            *state;         /* User state                   */
+       void            *user_data;     /* User-defined data            */
+       struct ubuf     in;             /* Input is here, POST data     */
+       struct ubuf     out;            /* Output goes here             */
+       unsigned int    flags;
+#define        SHTTPD_END_OF_OUTPUT    1
+#define        SHTTPD_CONNECTION_ERROR 2
+#define        SHTTPD_MORE_POST_DATA   4
+#define        SHTTPD_POST_BUFFER_FULL 8
+#define        SHTTPD_SSI_EVAL_TRUE    16
+};
+
+/*
+ * User callback function. Called when certain registered URLs have been
+ * requested. These are the requirements to the callback function:
+ *
+ * 1. it must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
+ *     and record how many bytes are copied, into 'out.num_bytes'
+ * 2. it must not block the execution
+ * 3. it must set SHTTPD_END_OF_OUTPUT flag when finished
+ * 4. for POST requests, it must process the incoming data (in.buf) of length
+ *     'in.len', and set 'in.num_bytes', which is how many bytes of POST
+ *     data is read and can be discarded by SHTTPD.
+ */
+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, int is_ssl);
+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);
+void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
+               shttpd_callback_t func, void *user_data);
+
+/*
+ * The following three functions are for applications that need to
+ * load-balance the connections on their own. Many threads may be spawned
+ * with one SHTTPD context per thread. Boss thread may only wait for
+ * new connections by means of shttpd_accept(). Then it may scan thread
+ * pool for the idle thread by means of shttpd_active(), and add new
+ * connection to the context by means of shttpd_add().
+ */
+void shttpd_add_socket(struct shttpd_ctx *, int sock);
+int shttpd_accept(int lsn_sock, int milliseconds);
+int shttpd_active(struct shttpd_ctx *);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SHTTPD_HEADER_INCLUDED */
diff --git a/libs/shttpd/ssl.h b/libs/shttpd/ssl.h
new file mode 100644 (file)
index 0000000..5aeae4a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+/*
+ * Snatched from OpenSSL includes. I put the prototypes here to be independent
+ * from the OpenSSL source installation. Having this, shttpd + SSL can be
+ * built on any system with binary SSL libraries installed.
+ */
+
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+
+#define        SSL_ERROR_WANT_READ     2
+#define        SSL_ERROR_WANT_WRITE    3
+#define SSL_FILETYPE_PEM       1
+
+/*
+ * Dynamically loaded SSL functionality
+ */
+struct ssl_func {
+       const char      *name;          /* SSL function name    */
+       union variant   ptr;            /* Function pointer     */
+};
+
+extern struct ssl_func ssl_sw[];
+
+#define        FUNC(x) ssl_sw[x].ptr.v_func
+
+#define        SSL_free(x)     (* (void (*)(SSL *)) FUNC(0))(x)
+#define        SSL_accept(x)   (* (int (*)(SSL *)) FUNC(1))(x)
+#define        SSL_connect(x)  (* (int (*)(SSL *)) FUNC(2))(x)
+#define        SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) FUNC(3))((x),(y),(z))
+#define        SSL_write(x,y,z) \
+       (* (int (*)(SSL *, const void *,int)) FUNC(4))((x), (y), (z))
+#define        SSL_get_error(x,y)(* (int (*)(SSL *, int)) FUNC(5))((x), (y))
+#define        SSL_set_fd(x,y) (* (int (*)(SSL *, int)) FUNC(6))((x), (y))
+#define        SSL_new(x)      (* (SSL * (*)(SSL_CTX *)) FUNC(7))(x)
+#define        SSL_CTX_new(x)  (* (SSL_CTX * (*)(SSL_METHOD *)) FUNC(8))(x)
+#define        SSLv23_server_method()  (* (SSL_METHOD * (*)(void)) FUNC(9))()
+#define        SSL_library_init() (* (int (*)(void)) FUNC(10))()
+#define        SSL_CTX_use_PrivateKey_file(x,y,z)      (* (int (*)(SSL_CTX *, \
+               const char *, int)) FUNC(11))((x), (y), (z))
+#define        SSL_CTX_use_certificate_file(x,y,z)     (* (int (*)(SSL_CTX *, \
+               const char *, int)) FUNC(12))((x), (y), (z))
diff --git a/libs/shttpd/standalone.c b/libs/shttpd/standalone.c
new file mode 100644 (file)
index 0000000..41b361e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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);
+
+#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);
+
+       if (ctx->inetd_mode) {
+               (void) freopen("/dev/null", "a", stderr);
+               shttpd_add_socket(ctx, fileno(stdin));
+       } else {
+               open_listening_ports(ctx);
+       }
+
+       elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
+           VERSION, ctx->ports, 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);
+}
diff --git a/libs/shttpd/std_includes.h b/libs/shttpd/std_includes.h
new file mode 100644 (file)
index 0000000..f61503c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#ifndef STD_HEADERS_INCLUDED
+#define        STD_HEADERS_INCLUDED
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* _WIN32_WCE */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <wchar.h>
+
+#if defined(_WIN32)            /* Windows specific     */
+#include "compat_win32.h"
+#elif defined(__rtems__)       /* RTEMS specific       */
+#include "compat_rtems.h"
+#else                          /* UNIX  specific       */
+#include "compat_unix.h"
+#endif /* _WIN32 */
+
+#endif /* STD_HEADERS_INCLUDED */
diff --git a/libs/shttpd/string.c b/libs/shttpd/string.c
new file mode 100644 (file)
index 0000000..44f0094
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+void
+my_strlcpy(register char *dst, register const char *src, size_t n)
+{
+       for (; *src != '\0' && n > 1; n--)
+               *dst++ = *src++;
+       *dst = '\0';
+}
+
+int
+my_strncasecmp(const char *str1, const char *str2, size_t len)
+{
+       register const unsigned char    *s1 = (unsigned char *) str1,
+                                       *s2 = (unsigned char *) str2, *e;
+       int                             ret;
+
+       for (e = s1 + len - 1; s1 < e && *s1 != '\0' && *s2 != '\0' &&
+           tolower(*s1) == tolower(*s2); s1++, s2++) ;
+       ret = tolower(*s1) - tolower(*s2);
+
+       return (ret);
+}
+
+char *
+my_strndup(const char *ptr, size_t len)
+{
+       char    *p;
+
+       if ((p = malloc(len + 1)) != NULL)
+               my_strlcpy(p, ptr, len + 1);
+
+       return (p);
+
+}
+
+char *
+my_strdup(const char *str)
+{
+       return (my_strndup(str, strlen(str)));
+}
+
+/*
+ * Sane snprintf(). Acts like snprintf(), but never return -1 or the
+ * value bigger than supplied buffer.
+ * Thanks Adam Zeldis to pointing snprintf()-caused vulnerability
+ * in his audit report.
+ */
+int
+my_snprintf(char *buf, size_t buflen, const char *fmt, ...)
+{
+       va_list         ap;
+       int             n;
+
+       if (buflen == 0)
+               return (0);
+
+       va_start(ap, fmt);
+       n = vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+
+       if (n < 0 || (size_t) n >= buflen)
+               n = buflen - 1;
+       buf[n] = '\0';
+
+       return (n);
+}
+
+/*
+ * Verify that given file has certain extension
+ */
+int
+match_extension(const char *path, const char *ext_list)
+{
+       size_t          len, path_len;
+       
+       path_len = strlen(path);
+
+       FOR_EACH_WORD_IN_LIST(ext_list, len)
+               if (len < path_len &&
+                   !my_strncasecmp(path + path_len - len, ext_list, len))
+                       return (1);
+
+       return (0);
+}
index cca101c..9f127a0 100644 (file)
@@ -231,6 +231,8 @@ void FreenetMasterThread::Run()
 \r
        Shutdown();\r
 \r
+       m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"FreenetMasterThread::run thread exiting.");\r
+\r
 }\r
 \r
 void FreenetMasterThread::Setup()\r
index 80a27c2..c408f48 100644 (file)
@@ -243,7 +243,7 @@ void IdentityIntroductionRequester::Process()
        // this will recheck for ids every minute\r
        DateTime now;\r
        now.SetToGMTime();\r
-       if(m_tempdate<(now-(1.0/1440.0)))\r
+       if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0)))\r
        {\r
                PopulateIDList();\r
                m_tempdate=now;\r
index 22b4b0b..2009962 100644 (file)
@@ -262,7 +262,7 @@ void IdentityRequester::Process()
        // this will recheck for ids every minute\r
        DateTime now;\r
        now.SetToGMTime();\r
-       if(m_tempdate<(now-(1.0/1440.0)))\r
+       if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0)))\r
        {\r
                PopulateIDList();\r
                m_tempdate=now;\r
index 91c127e..44c165a 100644 (file)
@@ -233,7 +233,7 @@ void IntroductionPuzzleRequester::Process()
        // this will recheck for ids every minute\r
        DateTime now;\r
        now.SetToGMTime();\r
-       if(m_tempdate<(now-(1.0/1440.0)))\r
+       if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0)))\r
        {\r
                PopulateIDList();\r
                m_tempdate=now;\r
index 1d20869..3d7f129 100644 (file)
@@ -274,7 +274,7 @@ void TrustListRequester::Process()
        // this will recheck for ids every minute\r
        DateTime now;\r
        now.SetToGMTime();\r
-       if(m_tempdate<(now-(1.0/1440.0)))\r
+       if(m_ids.size()==0 && m_tempdate<(now-(1.0/1440.0)))\r
        {\r
                PopulateIDList();\r
                m_tempdate=now;\r
index 291f06b..6430d85 100644 (file)
@@ -6,6 +6,7 @@
 #include "../include/db/sqlite3db.h"\r
 #include "../include/freenet/freenetmasterthread.h"\r
 #include "../include/nntp/nntplistener.h"\r
+#include "../include/http/httpthread.h"\r
 \r
 #ifdef _WIN32\r
        #include <winsock2.h>\r
@@ -334,7 +335,19 @@ void SetupDefaultOptions()
        // StartNNTP\r
        st.Bind(0,"StartNNTP");\r
        st.Bind(1,"true");\r
-       st.Bind(2,"Start NNTP service.");\r
+       st.Bind(2,"Start NNTP server.");\r
+       st.Step();\r
+       st.Reset();\r
+\r
+       st.Bind(0,"StartHTTP");\r
+       st.Bind(1,"true");\r
+       st.Bind(2,"Start HTTP server.");\r
+       st.Step();\r
+       st.Reset();\r
+\r
+       st.Bind(0,"HTTPListenPort");\r
+       st.Bind(1,"8080");\r
+       st.Bind(2,"Port HTTP server will listen on.");\r
        st.Step();\r
        st.Reset();\r
 \r
@@ -516,6 +529,7 @@ void StartThreads(std::vector<PThread::Thread *> &threads)
 {\r
        std::string startfreenet;\r
        std::string startnntp;\r
+       std::string starthttp;\r
 \r
        if(Option::Instance()->Get("StartFreenetUpdater",startfreenet)==false)\r
        {\r
@@ -529,6 +543,12 @@ void StartThreads(std::vector<PThread::Thread *> &threads)
                Option::Instance()->Set("StartNNTP","true");\r
        }\r
 \r
+       if(Option::Instance()->Get("StartHTTP",starthttp)==false)\r
+       {\r
+               starthttp="true";\r
+               Option::Instance()->Set("StartHTTP","true");\r
+       }\r
+\r
        if(startfreenet=="true")\r
        {\r
                PThread::Thread *t=new PThread::Thread(new FreenetMasterThread());\r
@@ -541,4 +561,10 @@ void StartThreads(std::vector<PThread::Thread *> &threads)
                threads.push_back(t);\r
        }\r
 \r
+       if(starthttp=="true")\r
+       {\r
+               PThread::Thread *t=new PThread::Thread(new HTTPThread());\r
+               threads.push_back(t);\r
+       }\r
+\r
 }\r
diff --git a/src/http/httpthread.cpp b/src/http/httpthread.cpp
new file mode 100644 (file)
index 0000000..dd4bd9b
--- /dev/null
@@ -0,0 +1,71 @@
+#include "../../include/http/httpthread.h"\r
+#include "../../include/option.h"\r
+#include "../../include/stringfunctions.h"\r
+#include "../../include/http/pages/homepage.h"\r
+\r
+#include <iostream>\r
+\r
+#ifdef XMEM\r
+       #include <xmem.h>\r
+#endif\r
+\r
+HTTPThread::HTTPThread()\r
+{\r
+\r
+       int port;\r
+       std::string portstr;\r
+       Option::Instance()->Get("HTTPListenPort",portstr);\r
+       StringFunctions::Convert(portstr,port);\r
+\r
+       m_pagehandlers.push_back(new HomePage());\r
+\r
+       m_ctx=0;\r
+       m_ctx=shttpd_init(NULL,"listen_ports",portstr.c_str(),NULL);\r
+       shttpd_listen(m_ctx,port,false);\r
+\r
+       shttpd_register_uri(m_ctx,"*",HTTPThread::PageCallback,this);\r
+\r
+}\r
+\r
+HTTPThread::~HTTPThread()\r
+{\r
+       shttpd_fini(m_ctx);\r
+\r
+       for(std::vector<IPageHandler *>::iterator i=m_pagehandlers.begin(); i!=m_pagehandlers.end(); i++)\r
+       {\r
+               delete (*i);\r
+       }\r
+\r
+}\r
+\r
+void HTTPThread::PageCallback(shttpd_arg *arg)\r
+{\r
+\r
+       HTTPThread *thread=(HTTPThread *)arg->user_data;\r
+\r
+       for(std::vector<IPageHandler *>::iterator i=thread->m_pagehandlers.begin(); i!=thread->m_pagehandlers.end(); )\r
+       {\r
+               if((*i)->Handle(arg)==true)\r
+               {\r
+                       i=thread->m_pagehandlers.end();\r
+               }\r
+               else\r
+               {\r
+                       i++;\r
+               }\r
+       }\r
+\r
+}\r
+\r
+void HTTPThread::Run()\r
+{\r
+       m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"HTTPThread::run thread started.");\r
+\r
+       do\r
+       {\r
+               shttpd_poll(m_ctx,1000);\r
+       }while(!IsCancelled());\r
+\r
+       m_log->WriteLog(LogFile::LOGLEVEL_DEBUG,"HTTPThread::run thread exiting.");\r
+\r
+}\r
diff --git a/src/http/ipagehandler.cpp b/src/http/ipagehandler.cpp
new file mode 100644 (file)
index 0000000..230da65
--- /dev/null
@@ -0,0 +1,100 @@
+#include "../../include/http/ipagehandler.h"\r
+#include "../../include/http/httpdefs.h"\r
+#include "../../include/stringfunctions.h"\r
+\r
+#ifdef XMEM\r
+       #include <xmem.h>\r
+#endif\r
+\r
+const bool IPageHandler::Handle(shttpd_arg *arg)\r
+{\r
+       const char *uri=shttpd_get_env(arg,"REQUEST_URI");\r
+       const char *method=shttpd_get_env(arg,"REQUEST_METHOD");\r
+       std::string methodstr="";\r
+       if(method)\r
+       {\r
+               methodstr=method;\r
+       }\r
+\r
+       if(uri && WillHandleURI(std::string(uri)))\r
+       {\r
+               httpstate *mystate=(httpstate *)arg->state;\r
+               // this is a new request - create a new arg object\r
+               if(arg->state==NULL)\r
+               {\r
+                       arg->state=new httpstate;\r
+                       memset(arg->state,0,sizeof(httpstate));\r
+                       mystate=(httpstate *)arg->state;\r
+\r
+                       // if post then create input buffer\r
+                       if(methodstr=="POST")\r
+                       {\r
+                               const char *lenstr=shttpd_get_header(arg,"Content-Length");\r
+                               if(lenstr)\r
+                               {\r
+                                       long len;\r
+                                       StringFunctions::Convert(std::string(lenstr),len);\r
+                                       mystate->m_indata=new char[len];\r
+                                       mystate->m_indatalen=len;\r
+                                       mystate->m_indatapos=0;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // we have more POST data to get\r
+               if(arg->in.len>0)\r
+               {\r
+                       int pos=0;\r
+                       while(arg->in.num_bytes<arg->in.len)\r
+                       {\r
+                               mystate->m_indata[mystate->m_indatapos++]=arg->in.buf[pos++];\r
+                       }\r
+                       arg->in.num_bytes=arg->in.len;\r
+               }\r
+\r
+               // we have all POST data (or it was 0 to begin with) - generate the page\r
+               if(mystate->m_indatalen==mystate->m_indatapos && mystate->m_outdata==NULL)\r
+               {\r
+                       //TODO parse POST data and any QUERY_STRING before generating page\r
+                       std::string page=GeneratePage(methodstr,std::map<std::string,std::string>());\r
+                       mystate->m_outdata=new char[page.size()];\r
+                       memcpy(mystate->m_outdata,page.c_str(),page.size());\r
+                       mystate->m_outdatalen=page.size();\r
+                       mystate->m_outdatapos=0;\r
+               }\r
+\r
+               // if we have output data, push next block of data onto out buffer\r
+               if(mystate->m_outdata && mystate->m_outdatapos<mystate->m_outdatalen)\r
+               {\r
+                       int pos=0;\r
+                       while(mystate->m_outdatapos<mystate->m_outdatalen && pos<arg->out.len)\r
+                       {\r
+                               arg->out.buf[pos++]=mystate->m_outdata[mystate->m_outdatapos++];\r
+                       }\r
+                       arg->out.num_bytes=pos;\r
+               }\r
+\r
+               // if we have no more output data to send - delete the data pointers and set end of output flag\r
+               if(mystate->m_outdata && mystate->m_outdatapos==mystate->m_outdatalen)\r
+               {\r
+                       if(mystate->m_indata)\r
+                       {\r
+                               delete mystate->m_indata;\r
+                       }\r
+                       if(mystate->m_outdata)\r
+                       {\r
+                               delete mystate->m_outdata;\r
+                       }\r
+                       delete mystate;\r
+                       arg->state=NULL;\r
+\r
+                       arg->flags|=SHTTPD_END_OF_OUTPUT;\r
+               }\r
+\r
+               return true;\r
+       }\r
+       else\r
+       {\r
+               return false;\r
+       }\r
+}\r
diff --git a/src/http/pages/homepage.cpp b/src/http/pages/homepage.cpp
new file mode 100644 (file)
index 0000000..28bfa0f
--- /dev/null
@@ -0,0 +1,15 @@
+#include "../../../include/http/pages/homepage.h"\r
+\r
+#ifdef XMEM\r
+       #include <xmem.h>\r
+#endif\r
+\r
+const std::string HomePage::GeneratePage(const std::string &method, const std::map<std::string,std::string> &queryvars)\r
+{\r
+       return "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body>Home</body></html>";\r
+}\r
+\r
+const bool HomePage::WillHandleURI(const std::string &uri)\r
+{\r
+       return true;\r
+}\r
index fcadcd5..fa20dcc 100644 (file)
@@ -474,7 +474,9 @@ list<CMimeField>::iterator CMimeHeader::FindField(const char* pszFieldName)
 #ifdef _WIN32\r
        #include <io.h>\r
 #else\r
-       #include <sys/io.h>\r
+       #if !defined(__APPLE__) && !defined(__DARWIN__)\r
+               #include <sys/io.h>\r
+       #endif\r
 #endif\r
 \r
 #ifndef O_BINARY\r
index 0ccef18..d909c36 100644 (file)
@@ -319,7 +319,7 @@ const bool NNTPConnection::HandleListCommand(const NNTPCommand &command)
 \r
                        if(show==true)\r
                        {\r
-                               tempstr << (*i).GetBoardName() << "\t" << (*i).GetHighMessageID() << "\t" << (*i).GetLowMessageID() << "\t" << (m_status.m_allowpost ? "y" : "n");\r
+                               tempstr << (*i).GetBoardName() << " " << (*i).GetHighMessageID() << " " << (*i).GetLowMessageID() << " " << (m_status.m_allowpost ? "y" : "n");\r
                                SendBufferedLine(tempstr.str());\r
                        }\r
                }\r
index 26f68bc..cfb72d7 100644 (file)
@@ -137,6 +137,8 @@ void NNTPListener::Run()
        }\r
        m_listensockets.clear();\r
 \r
+       LogFile::Instance()->WriteLog(LogFile::LOGLEVEL_DEBUG,"NNTPListener::run thread exiting.");\r
+\r
 }\r
 \r
 void NNTPListener::StartListen()\r