Merge pull request #96 from korizza/master

merge #95 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb616a6..b261b67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,9 @@
 if(COMMAND cmake_policy)
   cmake_policy(SET CMP0003 OLD)
   cmake_policy(SET CMP0005 OLD)
+  if ( POLICY CMP0042 )
+    cmake_policy(SET CMP0042 NEW)
+  endif ( POLICY CMP0042 )
   if ( POLICY CMP0046 )
   cmake_policy(SET CMP0046 OLD)
   endif ( POLICY CMP0046 )
@@ -32,7 +35,7 @@
 
 # Package architecture
 IF ( NOT DEFINED PACKAGE_ARCHITECTURE )
- EXECUTE_PROCESS(COMMAND "/bin/uname" "-m" OUTPUT_VARIABLE UNAME_M)
+ EXECUTE_PROCESS(COMMAND "uname" "-m" OUTPUT_VARIABLE UNAME_M)
  # strip trailing newline
  STRING(REPLACE "\n" "" PACKAGE_ARCHITECTURE ${UNAME_M})
 ENDIF ( NOT DEFINED PACKAGE_ARCHITECTURE )
@@ -44,9 +47,13 @@
 IF ( DEFINED LIB )
   SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB}" )
 ELSE ( DEFINED  LIB )
-  IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
-    SET( LIB_SUFFIX "64" )
-  ENDIF(CMAKE_SIZEOF_VOID_P MATCHES "8")
+  IF (APPLE)
+    SET(LIB_SUFFIX "")
+  ELSE (APPLE)
+    IF (CMAKE_SIZEOF_VOID_P MATCHES "8")
+      SET( LIB_SUFFIX "64" )
+    ENDIF(CMAKE_SIZEOF_VOID_P MATCHES "8")
+  ENDIF (APPLE)
   SET ( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" )
 ENDIF ( DEFINED  LIB )
 MESSAGE(STATUS "Libraries will be installed in ${LIB_INSTALL_DIR}" )
@@ -170,9 +177,9 @@
 
 IF( BUILD_PYTHON )
   MESSAGE(STATUS "Building Python bindings" )
-  FIND_PACKAGE(PythonLibs)
+  FIND_PACKAGE(PythonLibs 2.6 REQUIRED)
   IF (PYTHON_LIBRARY)
-    FIND_PACKAGE(PythonInterp REQUIRED)
+    FIND_PACKAGE(PythonInterp 2.6 REQUIRED)
     MESSAGE(STATUS "Found PythonLibs...")
     FIND_PACKAGE(PythonLinkLibs)
     IF (PYTHON_LINK_LIBS)
@@ -378,6 +385,10 @@
   SET(HAVE_ALLOCA 0)
   SET(C_ALLOCA 0)
   CHECK_INCLUDE_FILE( "alloca.h" HAVE_ALLOCA_H )
+ELSE (NOT HAVE_ALLOCA)
+  SET(HAVE_ALLOCA 1)
+  SET(C_ALLOCA 1)
+  SET(HAVE_ALLOCA_H 0)
 ENDIF (NOT HAVE_ALLOCA)
 
 #
diff --git a/ChangeLog b/ChangeLog
index 3481a80..13d4ba9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,10 @@
 - Bugfixes
   - Prevent buffer overflow if digest realm too long
   - Ensure path starts with '/' in client data endpoint
+  - Java bindings build fix
+  - Fix SSL library, usage is not thread safe (vcrhonek)
+  - Improve NetBSD and OSX builds (apjanke)
+  - Install winrs with correct name and permissions
 
 2.6.2
 - Features
diff --git a/VERSION.cmake b/VERSION.cmake
index 0ad437e..4e6d033 100644
--- a/VERSION.cmake
+++ b/VERSION.cmake
@@ -44,10 +44,10 @@
 #    set COMPATMINOR to MINOR. (binary incompatible change)
 #
 
-# Package version 2.6.2
+# Package version 2.6.3
 SET(OPENWSMAN_MAJOR "2")
 SET(OPENWSMAN_MINOR "6")
-SET(OPENWSMAN_PATCH "2")
+SET(OPENWSMAN_PATCH "3")
 
 # Plugin API 2.2
 SET(OPENWSMAN_PLUGIN_API_MAJOR "2")
diff --git a/autoconfiscate.sh b/autoconfiscate.sh
index 254ccf5..3c09572 100755
--- a/autoconfiscate.sh
+++ b/autoconfiscate.sh
@@ -1,8 +1,18 @@
-#!/bin/sh 
+#!/bin/sh
 
-echo "*** Autoconf/automake is deprecated for Openwsman"
-echo "*** and might not fully work."
-echo "*** Use cmake instead !"
+cat <<EOS >&2
+*** Autoconf/automake is deprecated for Openwsman and might not fully work.
+*** Please use CMake instead!
+*** Pull requests welcome ;-)
+EOS
+
+if [ "$1" != "--ignore-deprecation-warning" ]; then
+  cat <<EOS >&2
+*** To ignore this warning and proceed regardless, re-run as follows:
+***   $0 --ignore-deprecation-warning
+EOS
+  exit 1
+fi
 
 UNAME=`uname`
 
diff --git a/bindings/java/CMakeLists.txt b/bindings/java/CMakeLists.txt
index b961916..b3c95a9 100644
--- a/bindings/java/CMakeLists.txt
+++ b/bindings/java/CMakeLists.txt
@@ -41,7 +41,7 @@
    COMMAND ${JAVA_COMPILE} ${java_SOURCE} ${EXPLICIT_SOURCE} ${java_TARGET} ${EXPLICIT_TARGET} -d . *.java
    COMMAND ${CMAKE_COMMAND} -E echo_append "Creating JAR ..."
    COMMAND ${JAVA_ARCHIVE} cvf ${jar_NAME} *.so org/*
-   DEPENDS ${SWIG_OUTPUT}
+   DEPENDS jwsman
 )
 
 ADD_CUSTOM_TARGET ( jwsman_all ALL
diff --git a/bindings/java/tests/identify.java b/bindings/java/tests/identify.java
index 180afa6..6432b45 100644
--- a/bindings/java/tests/identify.java
+++ b/bindings/java/tests/identify.java
@@ -4,7 +4,10 @@
 // Java test for WS-Identify
 //
 
-import org.openwsman.*;
+import org.openwsman.Client;
+import org.openwsman.ClientOptions;
+import org.openwsman.OpenWSManConstants;
+import org.openwsman.XmlDoc;
 
 public class identify {
 
diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt
index 59f60aa..e27b70e 100644
--- a/bindings/python/CMakeLists.txt
+++ b/bindings/python/CMakeLists.txt
@@ -12,7 +12,7 @@
 
 add_subdirectory(tests)
 
-EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)" OUTPUT_VARIABLE PYTHON_LIB_DIR)
+EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))" OUTPUT_VARIABLE PYTHON_LIB_DIR)
 STRING(REPLACE "\n" "" PYTHON_LIB_DIR "${PYTHON_LIB_DIR}")
 
 MESSAGE(STATUS "Python executable: ${PYTHON_EXECUTABLE}")
diff --git a/package/openwsman.spec.in b/package/openwsman.spec.in
index 7f079e8..8926198 100644
--- a/package/openwsman.spec.in
+++ b/package/openwsman.spec.in
@@ -388,9 +388,6 @@
 install -D -m 644 %{S:3} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/openwsman
 %endif
 
-# rename winrs.rb -> winrs
-mv $RPM_BUILD_ROOT%{_bindir}/winrs.rb $RPM_BUILD_ROOT%{_bindir}/winrs
-
 %post -n libwsman3 -p /sbin/ldconfig
 
 %postun -n libwsman3
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index d625c47..2e23b27 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -53,6 +53,9 @@
 SET( wsman_curl_client_transport_SOURCES wsman-client-transport.c wsman-curl-client-transport.c )
 ADD_LIBRARY( ${WSMAN_CLIENT_TRANSPORT_PKG} SHARED ${wsman_curl_client_transport_SOURCES} )
 TARGET_LINK_LIBRARIES( ${WSMAN_CLIENT_TRANSPORT_PKG} ${CURL_LIBRARIES} )
+IF( ENABLE_EVENTING_SUPPORT )
+TARGET_LINK_LIBRARIES( ${WSMAN_CLIENT_TRANSPORT_PKG} ${OPENSSL_LIBRARIES} )
+ENDIF( ENABLE_EVENTING_SUPPORT )
 SET_TARGET_PROPERTIES( ${WSMAN_CLIENT_TRANSPORT_PKG} PROPERTIES VERSION 1.0.0 SOVERSION 1)
 INSTALL(TARGETS ${WSMAN_CLIENT_TRANSPORT_PKG} DESTINATION ${LIB_INSTALL_DIR})
 
diff --git a/src/lib/wsman-curl-client-transport.c b/src/lib/wsman-curl-client-transport.c
index d58fd72..cd7f517 100644
--- a/src/lib/wsman-curl-client-transport.c
+++ b/src/lib/wsman-curl-client-transport.c
@@ -46,8 +46,10 @@
 #include <curl/curl.h>
 #include <curl/easy.h>
 
+#ifdef ENABLE_EVENTING_SUPPORT
 #include <openssl/opensslv.h>
 #include <openssl/ssl.h>
+#endif
 
 #include "u/libu.h"
 #include "wsman-types.h"
diff --git a/src/plugins/cim/cim_data_stubs.c b/src/plugins/cim/cim_data_stubs.c
index 7bd84bb..079746a 100644
--- a/src/plugins/cim/cim_data_stubs.c
+++ b/src/plugins/cim/cim_data_stubs.c
@@ -170,27 +170,30 @@
 	hscan_t hs;
 	hnode_t *hn;
 	int rv = 0;
-	if(!client){
+	if (!client) {
 		return 0;
 	}
-	if (client->resource_uri && (strcmp( client->resource_uri, CIM_ALL_AVAILABLE_CLASSES ) ==0) ) {
+        if (!client->resource_uri) {
+          return 0;
+        }
+	if (strcmp( client->resource_uri, CIM_ALL_AVAILABLE_CLASSES ) == 0 ) {
 		return 1;
 	}
-        if ( client->resource_uri && (strstr( client->resource_uri, XML_NS_CIM_INTRINSIC ) != NULL )) {
+        if (strstr( client->resource_uri, XML_NS_CIM_INTRINSIC ) != NULL ) {
 	        return 1;
 	}
   
         /* Ok if class schema is CIM, uri starts with XML_NS_CIM_CLASS
          * and method is not 'Create' (CIM_ is abstract, cannot be created)
          */
-	if (client->requested_class && client->resource_uri && client->method
+	if (client->requested_class && client->method
             && (strncmp(client->requested_class, "CIM_", 4) == 0 )
 	    && (strstr(client->resource_uri , XML_NS_CIM_CLASS) == client->resource_uri )
 	    && (strcmp(client->method, TRANSFER_CREATE) != 0)) {
 		return 1;
 	}
 
-	if (client->requested_class && client->namespaces && client->resource_uri) {
+	if (client->requested_class && client->namespaces) {
 		hash_scan_begin(&hs, client->namespaces);
 		while ((hn = hash_scan_next(&hs))) {
 			if ( ( strstr(client->requested_class,
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
index e48b086..f4ec4a8 100644
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -6,9 +6,9 @@
 SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g" )
 
 SET(openwsmand_SOURCES shttpd/string.c shttpd/shttpd.c shttpd/auth.c shttpd/md5.c shttpd/adapter.c shttpd/cgi.c)
-SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/mime_type.c shttpd/config.c shttpd/io_socket.c)
+SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/log.c shttpd/io_socket.c)
 SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/io_ssl.c shttpd/io_emb.c shttpd/compat_unix.c shttpd/io_dir.c shttpd/io_file.c)
-SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/shttpd_defs.h shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h)
+SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/defs.h shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h)
 SET(openwsmand_SOURCES ${openwsmand_SOURCES} shttpd/compat_unix.h shttpd/compat_win32.h shttpd/compat_rtems.h shttpd/adapter.h)
 SET(openwsmand_SOURCES ${openwsmand_SOURCES} wsmand-listener.h wsmand-daemon.c wsmand-daemon.h wsmand-listener.c)
 SET(openwsmand_SOURCES ${openwsmand_SOURCES} gss.c wsmand.c)
diff --git a/src/server/Makefile.am b/src/server/Makefile.am
index 4c11de7..b050f65 100644
--- a/src/server/Makefile.am
+++ b/src/server/Makefile.am
@@ -28,16 +28,16 @@
 sbin_PROGRAMS = openwsmand
 
 openwsmand_SOURCES = \
+		shttpd/adapter.c \
 		shttpd/string.c \
 		shttpd/shttpd.c \
 		shttpd/auth.c \
 		shttpd/md5.c \
-		shttpd/adapter.c \
 		shttpd/cgi.c \
-		shttpd/mime_type.c shttpd/config.c shttpd/io_socket.c \
-		shttpd/io_ssl.c shttpd/io_emb.c shttpd/compat_unix.c shttpd/io_dir.c shttpd/io_file.c \
-		shttpd/shttpd_defs.h shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h \
-		shttpd/compat_unix.h shttpd/compat_win32.h shttpd/compat_rtems.h shttpd/adapter.h\
+		shttpd/io_socket.c \
+		shttpd/io_ssl.c shttpd/io_emb.c shttpd/compat_unix.c shttpd/io_dir.c shttpd/io_file.c shttpd/log.c \
+		shttpd/llist.h shttpd/shttpd.h shttpd/std_includes.h shttpd/io.h shttpd/md5.h shttpd/ssl.h shttpd/adapter.h \
+		shttpd/compat_unix.h shttpd/compat_win32.h shttpd/compat_rtems.h \
 		wsmand-listener.h \
 		wsmand-daemon.c \
 		wsmand-daemon.h \
diff --git a/src/server/shttpd/adapter.c b/src/server/shttpd/adapter.c
index 58a2d9a..65215c4 100644
--- a/src/server/shttpd/adapter.c
+++ b/src/server/shttpd/adapter.c
@@ -1,6 +1,6 @@
 
 
-#include "shttpd_defs.h"
+#include "defs.h"
 #include "adapter.h"
 #ifdef SHTTPD_GSS
 void getGssName(struct conn *c, char **user);
diff --git a/src/server/shttpd/auth.c b/src/server/shttpd/auth.c
index cf70d23..1e07485 100644
--- a/src/server/shttpd/auth.c
+++ b/src/server/shttpd/auth.c
@@ -8,10 +8,7 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
-#ifdef SHTTPD_GSS
-void do_gss(struct conn *c);
-#endif
+#include "defs.h"
 
 #if !defined(NO_AUTH)
 /*
@@ -161,16 +158,18 @@
 	    now - strtoul(dig->nonce, NULL, 10) > 3600 */)
 		return (0);
 
-	md5(a2, &known_http_methods[method], &digest->uri, NULL);
+	md5(a2, &_shttpd_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);
+	DBG(("%s: uri [%.*s] expected_resp [%.*s] resp [%.*s]",
+	    "check_password", digest->uri.len, digest->uri.ptr,
+	    32, resp, digest->resp.len, digest->resp.ptr));
 
 	return (!memcmp(resp, digest->resp.ptr, 32));
 }
 
-#if 0
 static FILE *
 open_auth_file(struct shttpd_ctx *ctx, const char *path)
 {
@@ -179,21 +178,32 @@
 	FILE		*fp = NULL;
 	int		fd;
 
-	if (ctx->global_passwd_file) {
+	if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
 		/* Use global passwords file */
-		snprintf(name, sizeof(name), "%s", ctx->global_passwd_file);
+		_shttpd_snprintf(name, sizeof(name), "%s",
+		    ctx->options[OPT_AUTH_GPASSWD]);
 	} else {
-		/* Try to find .htpasswd in requested directory */
+		/*
+		 * Try to find .htpasswd in requested directory.
+		 * Given the path, create the path to .htpasswd file
+		 * in the same directory. Find the right-most
+		 * directory separator character first. That would be the
+		 * directory name. If directory separator character is not
+		 * found, 'e' will point to 'p'.
+		 */
 		for (p = path, e = p + strlen(p) - 1; e > p; e--)
 			if (IS_DIRSEP_CHAR(*e))
 				break;
 
-		assert(IS_DIRSEP_CHAR(*e));
-		(void) snprintf(name, sizeof(name), "%.*s/%s",
+		/*
+		 * Make up the path by concatenating directory name and
+		 * .htpasswd file name.
+		 */
+		(void) _shttpd_snprintf(name, sizeof(name), "%.*s/%s",
 		    (int) (e - p), p, HTPASSWD);
 	}
 
-	if ((fd = my_open(name, O_RDONLY, 0)) == -1) {
+	if ((fd = _shttpd_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));
@@ -202,7 +212,6 @@
 
 	return (fp);
 }
-#endif
 
 /*
  * Parse the line from htpasswd file. Line should be in form of
@@ -237,149 +246,129 @@
 static int
 authorize_digest(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];
+        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];
 
-	parse_authorization_header(auth_vec, &digest);
-	*user_vec = digest.user;
+        parse_authorization_header(auth_vec, &digest);
+        *user_vec = digest.user;
 
-	while (fgets(line, sizeof(line), fp) != NULL) {
+        while (fgets(line, sizeof(line), fp) != NULL) {
 
-		if (!parse_htpasswd_line(line, &user, &domain, &ha1))
-			continue;
+                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));
+                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;
-		}
-	}
+                if (vcmp(user_vec, &user) && !memcmp(c->ctx->options[OPT_AUTH_REALM],
+                    domain.ptr, domain.len)) {
+                        ok = check_password(c->method, &ha1, &digest);
+                        break;
+                }
+        }
 
-	return (ok);
+        return (ok);
 }
 
 int
-check_authorization(struct conn *c, const char *path)
+_shttpd_check_authorization(struct conn *c, const char *path)
 {
-	FILE			*fp = NULL;
-	int			authorized = 0;
-	struct vec 	*auth_vec = &c->ch.auth.v_vec;
+        FILE                    *fp = NULL;
+        int                     authorized = 0;
+        struct vec      *auth_vec = &c->ch.auth.v_vec;
 
-#ifdef EMBEDDED
-	struct llhead	*lp;
-	struct uri_auth	*auth;
-	int digest = 0, basic = 0;
-#ifdef SHTTPD_GSS
-    int kerberos = 0;
-#endif
+        struct llhead   *lp;
+        struct uri_auth *auth;
+        int digest = 0, basic = 0;
 
-	basic_auth_callback cb = NULL;
-	char *p, *pp;
-	/* Check, is this URL protected by shttpd_protect_url() */
-#ifdef SHTTPD_GSS
-    /* already authenticated */
-	if(c->gss_ctx != GSS_C_NO_CONTEXT)
-		return 1;
-#endif
+        basic_auth_callback cb = NULL;
+        char *p, *pp;
 
-	LL_FOREACH(&c->ctx->uri_auths, lp) {
-		auth = LL_ENTRY(lp, struct uri_auth, link);
-		if (!strncmp(c->uri, auth->uri, strlen(c->uri))) {
-#ifdef SHTTPD_GSS
-			if (!strncasecmp(auth_vec->ptr, "Kerberos ", 9))
-			{
-				kerberos = 1;
-            }
-#endif
-			if (auth->type == DIGEST_AUTH &&
-			    auth_vec->len > 20 &&
-			    !strncasecmp(auth_vec->ptr, "Digest ", 7)) {
-				fp = fopen(auth->file_name, "r");
-				digest = 1;
-			}
-			if (auth->type == BASIC_AUTH &&
-			    auth_vec->len > 10 &&
-			    !strncasecmp(auth_vec->ptr, "Basic ", 6)) {
-				cb = (int (*)(char *, char *)) auth->callback.v_func;
-				basic = 1;
-			}
-			break;
-		}
-	}
-	if (lp == &c->ctx->uri_auths) //not a protected uri
-		return 1;
-#ifdef SHTTPD_GSS
-    if(kerberos == 1)
-    {
-        do_gss(c);
-        return 2;
-    }
-#endif
-	if (digest == 1) {
-		if (fp != NULL) {
-				authorized = authorize_digest(c, fp);
-				(void) fclose(fp);
-				return (authorized);
-		} else
-			return (0);
+        /* 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, strlen(c->uri))) {
+                        if (auth->type == DIGEST_AUTH &&
+                            auth_vec->len > 20 &&
+                            !strncasecmp(auth_vec->ptr, "Digest ", 7)) {
+                                fp = fopen(auth->file_name, "r");
+                                digest = 1;
+                        }
+                        if (auth->type == BASIC_AUTH &&
+                            auth_vec->len > 10 &&
+                            !strncasecmp(auth_vec->ptr, "Basic ", 6)) {
+                                cb = (int (*)(char *, char *)) auth->callback.v_func;
+                                basic = 1;
+                        }
+                        break;
+                }
+        }
+        if (lp == &c->ctx->uri_auths) { //not a protected uri
+                return 1;
 	}
 
-	if (basic == 1) {
-		char buf[4096];
-		int l;
-
-		p = (char *) auth_vec->ptr + 5;
-		while ((*p == ' ') || (*p == '\t')) {
-			p++;
-		}
-		pp = p;
-		while ((*p != ' ') && (*p != '\t') && (*p != '\r')
-				&& (*p != '\n') && (*p != 0)) {
-			p++;
-		}
-
-		if (pp == p) {
-			return 0;
-		}
-		*p = 0;
-
-		l = ws_base64_decode(pp, p - pp, buf, 4095);
-		if (l <= 0) {
-			return 0;
-		}
-
-		buf[l] = 0;
-		p = buf;
-		pp = p;
-		p = strchr(p, ':');
-		if (p == NULL) {
-			return 0;
-		}
-		*p++ = 0;
-		authorized = cb(pp, p);
-	} else {
+        if (digest == 1) {
 		return 0;
-	}
+                if (fp != NULL) {
+                                authorized = authorize_digest(c, fp);
+                                (void) fclose(fp);
+                                return (authorized);
+                } else
+                        return (0);
+        }
 
-#endif /* EMBEDDED */
+        if (basic == 1) {
+                char buf[4096];
+                int l;
+
+                p = (char *) auth_vec->ptr + 5;
+                while ((*p == ' ') || (*p == '\t')) {
+                        p++;
+                }
+                pp = p;
+                while ((*p != ' ') && (*p != '\t') && (*p != '\r')
+                                && (*p != '\n') && (*p != 0)) {
+                        p++;
+                }
+
+                if (pp == p) {
+                        return 0;
+                }
+                *p = 0;
+
+                l = ws_base64_decode(pp, p - pp, buf, 4095);
+                if (l <= 0) {
+                        return 0;
+                }
+
+                buf[l] = 0;
+                p = buf;
+                pp = p;
+                p = strchr(p, ':');
+                if (p == NULL) {
+                        return 0;
+                }
+                *p++ = 0;
+                authorized = cb(pp, p);
+        } else {
+                return 0;
+        }
 
 	return (authorized);
 }
 
 int
-is_authorized_for_put(struct conn *c)
+_shttpd_is_authorized_for_put(struct conn *c)
 {
 	FILE	*fp;
 	int	ret = 0;
 
-	if ((fp = fopen(c->ctx->put_auth_file, "r")) != NULL) {
+	if ((fp = fopen(c->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {
 		ret = authorize_digest(c, fp);
 		(void) fclose(fp);
 	}
@@ -387,73 +376,48 @@
 	return (ret);
 }
 
-int
-send_authorization_request(struct conn *c)
+
+void
+_shttpd_send_authorization_request(struct conn *c)
 {
-#define BUFSIZE 512
-	char buf[BUFSIZE];
-        char *bufptr = buf;
-	int n;
-        size_t remaining = BUFSIZE;
-	int b = 0, d = 0;
+        char    buf[512];
+        int n = 0;
+        int b = 0, d = 0;
 
-	struct llhead	*lp;
-	struct uri_auth	*auth;
+        struct llhead   *lp;
+        struct uri_auth *auth;
 
-	n = snprintf(bufptr, remaining, "Unauthorized\r\n");
-        bufptr += n;
-        remaining -= n;
-	LL_FOREACH(&c->ctx->uri_auths, lp) {
-		auth = LL_ENTRY(lp, struct uri_auth, link);
-		if (auth->type == DIGEST_AUTH && d == 0) {
-			if (b) {
-				n = snprintf(bufptr, remaining, "\r\n");
-                                if (n >= remaining) {
-                                  return -1;
+        n = snprintf(buf, sizeof(buf), "Unauthorized\r\n");
+        LL_FOREACH(&c->ctx->uri_auths, lp) {
+                auth = LL_ENTRY(lp, struct uri_auth, link);
+                if (auth->type == DIGEST_AUTH && d == 0) {
+                                if (b ) {
+                                                n += snprintf(buf +n, sizeof(buf) - n, "\r\n");
                                 }
-                                bufptr += n;
-                                remaining -= n;
-			}
-			n = snprintf(bufptr, remaining,
-                                      "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
-                                      "nonce=\"%lu\"", c->ctx->auth_realm, (unsigned long) current_time);
-                        if (n >= remaining) {
-                          return -1;
-                        }
-                        bufptr += n;
-                        remaining -= n;
-                        d = 1;
-		}
-		if (auth->type == BASIC_AUTH && b == 0) {
-			if (d) {
-				n = snprintf(bufptr, remaining, "\r\n");
-                                if (n >= remaining) {
-                                  return -1;
+                                n += snprintf(buf +n, sizeof(buf) - n,
+                        "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
+                        "nonce=\"%lu\"", c->ctx->options[OPT_AUTH_REALM], (unsigned long) _shttpd_current_time);
+                                d = 1;
+                }
+                if (auth->type == BASIC_AUTH && b == 0) {
+                                if (d) {
+                                                n += snprintf(buf +n, sizeof(buf) - n, "\r\n");
                                 }
-                                bufptr += n;
-                                remaining -= n;
-			}
-			n = snprintf(bufptr, remaining,
-                                      "WWW-Authenticate: Basic realm=\"%s\"", c->ctx->auth_realm);
-                        if (n >= remaining) {
-                          return -1;
-                        }
-                        bufptr += n;
-                        remaining -= n;
-                        b = 1;
-		}
-	}
+                                n +=  snprintf(buf +n, sizeof(buf) - n,
+                        "WWW-Authenticate: Basic realm=\"%s\"", c->ctx->options[OPT_AUTH_REALM]);
+                                b = 1;
+                }
+        }
 
-	send_server_error(c, 401, buf);
-#undef BUFSIZE
-        return 0;
+	_shttpd_send_server_error(c, 401, buf);
 }
 
+
 /*
  * Edit the passwords file.
  */
 int
-edit_passwords(const char *fname, const char *domain,
+_shttpd_edit_passwords(const char *fname, const char *domain,
 		const char *user, const char *pass)
 {
 	int		ret = EXIT_SUCCESS, found = 0;
@@ -461,7 +425,7 @@
 	char		line[512], tmp[FILENAME_MAX], ha1[32];
 	FILE		*fp = NULL, *fp2 = NULL;
 
-	(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
+	(void) _shttpd_snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
 
 	/* Create the file if does not exist */
 	if ((fp = fopen(fname, "a+")))
@@ -469,9 +433,11 @@
 
 	/* Open the given file and temporary file */
 	if ((fp = fopen(fname, "r")) == NULL)
-		elog(E_FATAL, 0, "Cannot open %s: %s", fname, strerror(errno));
+		_shttpd_elog(E_FATAL, NULL,
+		    "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));
+		_shttpd_elog(E_FATAL, NULL,
+		    "Cannot open %s: %s", tmp, strerror(errno));
 
 	p.ptr = pass;
 	p.len = strlen(pass);
@@ -512,8 +478,8 @@
 	(void) fclose(fp2);
 
 	/* Put the temp file in place of real file */
-	(void) remove(fname);
-	(void) rename(tmp, fname);
+	(void) _shttpd_remove(fname);
+	(void) _shttpd_rename(tmp, fname);
 
 	return (ret);
 }
diff --git a/src/server/shttpd/cgi.c b/src/server/shttpd/cgi.c
index 2e22f28..f9623dc 100644
--- a/src/server/shttpd/cgi.c
+++ b/src/server/shttpd/cgi.c
@@ -8,7 +8,7 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 #if !defined(NO_CGI)
 struct env_block {
@@ -18,94 +18,6 @@
 	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])
-{
-
-
-#ifdef ENABLE_IPV6
-	struct sockaddr_in6	sa;
-#else
-	struct sockaddr_in      sa;
-#endif
-	int			sock = -1, ret = -1;
-	socklen_t		len = sizeof(sa);
-
-	(void) memset(&sa, 0, sizeof(sa));
-#ifdef ENABLE_IPV6
-	sa.sin6_family          = AF_INET6;
-        sa.sin6_addr		= in6addr_loopback;
-	sa.sin6_port           	= htons(0);
-
-#else
-	sa.sin_family           = AF_INET;
-	sa.sin_addr.s_addr      = htonl(INADDR_LOOPBACK);
-	sa.sin_port             = htons(0);
-#endif
-	
-#ifdef ENABLE_IPV6
-	 if (!wsmand_options_get_use_ipv6()
-	     || (sock = socket(AF_INET6, SOCK_STREAM, 0)) == -1) {
-		if(!wsmand_options_get_use_ipv4()
-		   || (sock = socket(AF_INET,SOCK_STREAM,0)) == -1)
-	                elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
-        } 
-
-#else
-	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
-		elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
-	} 
-
-#endif
-	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);
-	} 
-
-#ifdef ENABLE_IPV6
-	else if ((sp[0] = socket(AF_INET6, SOCK_STREAM, 6)) == -1) {
-                elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
-                (void) closesocket(sock);
-        }
-#else
-	else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
-		elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
-		(void) closesocket(sock);
-	} 
-#endif
-
-	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, ...)
 {
@@ -136,13 +48,13 @@
 
 		/* Find where this header ends. Remember where value starts */
 		for (p = s, v = NULL; p < e && *p != '\n'; p++)
-			if (v == NULL && *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) {
 
@@ -181,42 +93,35 @@
 		struct env_block *blk)
 {
 	const struct headers	*h = &c->ch;
-	const char		*s;
+	const char		*s, *fname, *root = c->ctx->options[OPT_ROOT];
 	size_t			len;
 
 	blk->len = blk->nvars = 0;
 
+	/* SCRIPT_FILENAME */
+	fname = prog;
+	if ((s = strrchr(prog, '/')))
+		fname = s + 1;
+
 	/* 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);
-#ifdef ENABLE_IPV6
-	if (wsmand_options_get_use_ipv6()) {
-		char str[INET6_ADDRSTRLEN];
-		inet_ntop( AF_INET6,&c->sa.u.sin.sin6_addr, str, sizeof(str));
-		addenv(blk, "REMOTE_ADDR=%s", str);
-		addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin6_port));
-	}
-	else {
-#endif
-		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));
-#ifdef ENABLE_IPV6
-	}
-#endif
-
+	addenv(blk, "SERVER_NAME=%s", c->ctx->options[OPT_AUTH_REALM]);
+	addenv(blk, "SERVER_ROOT=%s", root);
+	addenv(blk, "DOCUMENT_ROOT=%s", root);
+	addenv(blk, "REQUEST_METHOD=%s",
+			_shttpd_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, "SCRIPT_NAME=%s", prog + strlen(root));
+	addenv(blk, "SCRIPT_FILENAME=%s", fname);	/* PHP */
 	addenv(blk, "PATH_TRANSLATED=%s", prog);
 
 	if (h->ct.v_vec.len > 0)
-		addenv(blk, "CONTENT_TYPE=%.*s", 
+		addenv(blk, "CONTENT_TYPE=%.*s",
 		    h->ct.v_vec.len, h->ct.v_vec.ptr);
 
 	if (c->query != NULL)
@@ -251,7 +156,7 @@
 	}
 
 	/* Add user-specified variables */
-	s = c->ctx->cgi_vars;
+	s = c->ctx->options[OPT_CGI_ENVIRONMENT];
 	FOR_EACH_WORD_IN_LIST(s, len)
 		addenv(blk, "%.*s", len, s);
 
@@ -276,7 +181,7 @@
 }
 
 int
-run_cgi(struct conn *c, const char *prog)
+_shttpd_run_cgi(struct conn *c, const char *prog)
 {
 	struct env_block	blk;
 	char			dir[FILENAME_MAX], *p;
@@ -286,16 +191,17 @@
 	pair[0] = pair[1] = -1;
 
 	/* CGI must be executed in its own directory */
-	(void) snprintf(dir, sizeof(dir), "%s", prog);
+	(void) _shttpd_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) {
+
+	if (shttpd_socketpair(pair) != 0) {
 		ret = -1;
-	} else if (spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) {
+	} else if (_shttpd_spawn_process(c,
+	    prog, blk.buf, blk.vars, pair[1], dir)) {
 		ret = -1;
 		(void) closesocket(pair[0]);
 		(void) closesocket(pair[1]);
@@ -308,13 +214,13 @@
 }
 
 void
-do_cgi(struct conn *c)
+_shttpd_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.io_class = &_shttpd_io_cgi;
 	c->loc.flags = FLAG_R;
 	if (c->method == METHOD_POST)
 		c->loc.flags |= FLAG_W;
diff --git a/src/server/shttpd/compat_rtems.c b/src/server/shttpd/compat_rtems.c
index 7ccbf38..2a01a07 100644
--- a/src/server/shttpd/compat_rtems.c
+++ b/src/server/shttpd/compat_rtems.c
@@ -18,7 +18,7 @@
  * INCLUDED MODULES
  **********************************************************************/
 #include <rtems.h>
-#include "shttpd_defs.h"
+#include "defs.h"
 
 #define MAX_WEB_BASE_PATH_LENGTH 256
 #define MIN_SHTTPD_STACK         (8*1024)
diff --git a/src/server/shttpd/compat_unix.c b/src/server/shttpd/compat_unix.c
index 00e00e3..a0d5070 100644
--- a/src/server/shttpd/compat_unix.c
+++ b/src/server/shttpd/compat_unix.c
@@ -8,52 +8,52 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
-void 
-set_close_on_exec(int fd)
+void
+_shttpd_set_close_on_exec(int fd)
 {
 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
 }
 
 int
-my_stat(const char *path, struct stat *stp)
+_shttpd_stat(const char *path, struct stat *stp)
 {
 	return (stat(path, stp));
 }
 
 int
-my_open(const char *path, int flags, int mode)
+_shttpd_open(const char *path, int flags, int mode)
 {
 	return (open(path, flags, mode));
 }
 
 int
-my_remove(const char *path)
+_shttpd_remove(const char *path)
 {
 	return (remove(path));
 }
 
 int
-my_rename(const char *path1, const char *path2)
+_shttpd_rename(const char *path1, const char *path2)
 {
 	return (rename(path1, path2));
 }
 
 int
-my_mkdir(const char *path, int mode)
+_shttpd_mkdir(const char *path, int mode)
 {
 	return (mkdir(path, mode));
 }
 
 char *
-my_getcwd(char *buffer, int maxlen)
+_shttpd_getcwd(char *buffer, int maxlen)
 {
 	return (getcwd(buffer, maxlen));
 }
 
 int
-set_non_blocking_mode(int fd)
+_shttpd_set_non_blocking_mode(int fd)
 {
 	int	ret = -1;
 	int	flags;
@@ -71,22 +71,24 @@
 
 #ifndef NO_CGI
 int
-spawn_process(struct conn *c, const char *prog, char *envblk,
+_shttpd_spawn_process(struct conn *c, const char *prog, char *envblk,
 		char *envp[], int sock, const char *dir)
 {
-	int	ret;
-	pid_t	pid;
+	int		ret;
+	pid_t		pid;
+	const char	*p, *interp = c->ctx->options[OPT_CGI_INTERPRETER];
 
 	envblk = NULL;	/* unused */
 
 	if ((pid = vfork()) == -1) {
 
 		ret = -1;
-		elog(E_LOG, c, "redirect: fork: %s", strerror(errno));
+		_shttpd_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);
@@ -96,19 +98,22 @@
 		if (c->ctx->error_log)
 			(void) dup2(fileno(c->ctx->error_log), 2);
 
+		if ((p = strrchr(prog, '/')) != NULL)
+			p++;
+		else
+			p = prog;
+
 		/* Execute CGI program */
-		if (c->ctx->cgi_interpreter == NULL) {
-			(void) execle(prog, prog, NULL, envp);
-			elog(E_FATAL, c, "redirect: exec(%s)", prog);
+		if (interp == NULL) {
+			(void) execle(p, p, NULL, envp);
+			_shttpd_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);
+			(void) execle(interp, interp, p, NULL, envp);
+			_shttpd_elog(E_FATAL, c, "redirect: exec(%s %s)",
+			    interp, prog);
 		}
 
 		/* UNREACHED */
-		ret = -1;
 		exit(EXIT_FAILURE);
 
 	} else {
diff --git a/src/server/shttpd/compat_unix.h b/src/server/shttpd/compat_unix.h
index aa498ef..53c7f03 100644
--- a/src/server/shttpd/compat_unix.h
+++ b/src/server/shttpd/compat_unix.h
@@ -20,16 +20,16 @@
 #include <unistd.h>
 #include <dirent.h>
 #include <dlfcn.h>
-#ifndef SSL_LIB
+
+#if !defined(NO_THREADS)
+#include "pthread.h"
+#define	_beginthread(a, b, c) do { pthread_t tid; \
+	pthread_create(&tid, NULL, (void *(*)(void *))a, c); } while (0)
+#endif /* !NO_THREADS */
+
 #define	SSL_LIB				"libssl.so"
-#endif
 #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/src/server/shttpd/compat_win32.c b/src/server/shttpd/compat_win32.c
index 9b11cc7..36717f8 100644
--- a/src/server/shttpd/compat_win32.c
+++ b/src/server/shttpd/compat_win32.c
@@ -8,523 +8,11 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#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) {
-			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;
-		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) 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) snprintf(text, sizeof(text),
-		    " Requests: %u", ctx->nrequests);
-		SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM) text);
-		(void) snprintf(text, sizeof(text),
-		    " Sent: %4.2f Mb", (double) ctx->out / 1048576);
-		SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM) text);
-		(void) 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;
-		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 SERVICE_STATUS		ss;
+static SERVICE_STATUS_HANDLE	hStatus;
+static SERVICE_DESCRIPTION	service_descr = {"Web server"};
 
 static void
 fix_directory_separators(char *path)
@@ -533,32 +21,68 @@
 		if (*path == '/')
 			*path = '\\';
 		if (*path == '\\')
-			while (path[1] == '\\' || path[1] == '/') 
+			while (path[1] == '\\' || path[1] == '/')
 				(void) memmove(path + 1,
 				    path + 2, strlen(path + 2) + 1);
 	}
 }
 
+static int
+protect_against_code_disclosure(const wchar_t *path)
+{
+	WIN32_FIND_DATAW	data;
+	HANDLE			handle;
+	const wchar_t		*p;
+
+	/*
+	 * Protect against CGI code disclosure under Windows.
+	 * This is very nasty hole. Windows happily opens files with
+	 * some garbage in the end of file name. So fopen("a.cgi    ", "r")
+	 * actually opens "a.cgi", and does not return an error! And since
+	 * "a.cgi    " does not have valid CGI extension, this leads to
+	 * the CGI code disclosure.
+	 * To protect, here we delete all fishy characters from the
+	 * end of file name.
+	 */
+
+	if ((handle = FindFirstFileW(path, &data)) == INVALID_HANDLE_VALUE)
+		return (FALSE);
+
+	FindClose(handle);
+
+	for (p = path + wcslen(path); p > path && p[-1] != L'\\';)
+		p--;
+
+	if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+	    wcscmp(data.cFileName, p) != 0)
+		return (FALSE);
+
+	return (TRUE);
+}
+
 int
-my_open(const char *path, int flags, int mode)
+_shttpd_open(const char *path, int flags, int mode)
 {
 	char	buf[FILENAME_MAX];
 	wchar_t	wbuf[FILENAME_MAX];
 
-	my_strlcpy(buf, path, sizeof(buf));
+	_shttpd_strlcpy(buf, path, sizeof(buf));
 	fix_directory_separators(buf);
 	MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
 
+	if (protect_against_code_disclosure(wbuf) == FALSE)
+		return (-1);
+
 	return (_wopen(wbuf, flags));
 }
 
 int
-my_stat(const char *path, struct stat *stp)
+_shttpd_stat(const char *path, struct stat *stp)
 {
 	char	buf[FILENAME_MAX], *p;
 	wchar_t	wbuf[FILENAME_MAX];
 
-	my_strlcpy(buf, path, sizeof(buf));
+	_shttpd_strlcpy(buf, path, sizeof(buf));
 	fix_directory_separators(buf);
 
 	p = buf + strlen(buf) - 1;
@@ -571,12 +95,12 @@
 }
 
 int
-my_remove(const char *path)
+_shttpd_remove(const char *path)
 {
 	char	buf[FILENAME_MAX];
 	wchar_t	wbuf[FILENAME_MAX];
 
-	my_strlcpy(buf, path, sizeof(buf));
+	_shttpd_strlcpy(buf, path, sizeof(buf));
 	fix_directory_separators(buf);
 
 	MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
@@ -585,15 +109,15 @@
 }
 
 int
-my_rename(const char *path1, const char *path2)
+_shttpd_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));
+	_shttpd_strlcpy(buf1, path1, sizeof(buf1));
+	_shttpd_strlcpy(buf2, path2, sizeof(buf2));
 	fix_directory_separators(buf1);
 	fix_directory_separators(buf2);
 
@@ -604,12 +128,12 @@
 }
 
 int
-my_mkdir(const char *path, int mode)
+_shttpd_mkdir(const char *path, int mode)
 {
 	char	buf[FILENAME_MAX];
 	wchar_t	wbuf[FILENAME_MAX];
 
-	my_strlcpy(buf, path, sizeof(buf));
+	_shttpd_strlcpy(buf, path, sizeof(buf));
 	fix_directory_separators(buf);
 
 	MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
@@ -640,7 +164,7 @@
 }
 
 char *
-my_getcwd(char *buffer, int maxlen)
+_shttpd_getcwd(char *buffer, int maxlen)
 {
 	char *result = NULL;
 	wchar_t *wbuffer, *wresult;
@@ -686,7 +210,7 @@
 	} else if ((dir = malloc(sizeof(*dir))) == NULL) {
 		errno = ENOMEM;
 	} else {
-		snprintf(path, sizeof(path), "%s/*", name);
+		_shttpd_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);
@@ -714,7 +238,7 @@
 		free(dir);
 	}
 
-	if (result == -1) 
+	if (result == -1)
 		errno = EBADF;
 
 	return (result);
@@ -742,7 +266,7 @@
 }
 
 int
-set_non_blocking_mode(int fd)
+_shttpd_set_non_blocking_mode(int fd)
 {
 	unsigned long	on = 1;
 
@@ -750,7 +274,7 @@
 }
 
 void
-set_close_on_exec(int fd)
+_shttpd_set_close_on_exec(int fd)
 {
 	fd = 0;	/* Do nothing. There is no FD_CLOEXEC on Windows */
 }
@@ -763,6 +287,30 @@
 	big_int_t content_len;
 };
 
+
+enum ready_mode_t {IS_READY_FOR_READ, IS_READY_FOR_WRITE};
+
+/*
+ * Wait until given socket is in ready state. Always return TRUE.
+ */
+static int
+is_socket_ready(int sock, enum ready_mode_t mode)
+{
+	fd_set		read_set, write_set;
+
+	FD_ZERO(&read_set);
+	FD_ZERO(&write_set);
+
+	if (mode == IS_READY_FOR_READ)
+		FD_SET(sock, &read_set);
+	else
+		FD_SET(sock, &write_set);
+
+	select(sock + 1, &read_set, &write_set, NULL, NULL);
+
+	return (TRUE);
+}
+
 /*
  * Thread function that reads POST data from the socket pair
  * and writes it to the CGI process.
@@ -778,14 +326,19 @@
 	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) {
+	while (!stop &&
+	    max_recv > 0 &&
+	    is_socket_ready(tp->s, IS_READY_FOR_READ) &&
+	    (n = recv(tp->s, buf, max_recv, 0)) > 0) {
+		if (n == -1 && ERRNO == EWOULDBLOCK)
+			continue;
 		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);
 }
@@ -804,12 +357,19 @@
 
 	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)
+		for (sent = 0; !stop && sent < n; sent += k) {
+			if (is_socket_ready(tp->s, IS_READY_FOR_WRITE) &&
+			    (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) {
+				if (k == -1 && ERRNO == EWOULDBLOCK) {
+					k = 0;
+					continue;
+				}
 				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
@@ -846,14 +406,14 @@
 }
 
 int
-spawn_process(struct conn *c, const char *prog, char *envblk,
+_shttpd_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;
+	HANDLE	a[2], b[2], h[2], me;
+	DWORD	flags;
+	char	*p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX];
+	FILE	*fp;
+	STARTUPINFOA		si;
 	PROCESS_INFORMATION	pi;
 
 	me = GetCurrentProcess();
@@ -864,7 +424,7 @@
 	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));
 
@@ -872,11 +432,12 @@
 	si.cb		= sizeof(si);
 	si.dwFlags	= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
 	si.wShowWindow	= SW_HIDE;
-	si.hStdOutput	= si.hStdError = h[1];
+	si.hStdOutput	= h[1];
 	si.hStdInput	= h[0];
 
 	/* If CGI file is a script, try to read the interpreter line */
-	if (c->ctx->cgi_interpreter == NULL) {
+	interp = c->ctx->options[OPT_CGI_INTERPRETER];
+	if (interp == NULL) {
 		if ((fp = fopen(prog, "r")) != NULL) {
 			(void) fgets(line, sizeof(line), fp);
 			if (memcmp(line, "#!", 2) != 0)
@@ -887,14 +448,17 @@
 				*p = '\0';
 			(void) fclose(fp);
 		}
-		(void) snprintf(cmdline, sizeof(cmdline), "%s%s%s",
+		interp = line + 2;
+		(void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
 		    line + 2, line[2] == '\0' ? "" : " ", prog);
-	} else {
-		(void) snprintf(cmdline, sizeof(cmdline), "%s %s",
-		    c->ctx->cgi_interpreter, prog);
 	}
 
-	(void) snprintf(line, sizeof(line), "%s", dir);
+	if ((p = strrchr(prog, '/')) != NULL)
+		prog = p + 1;
+
+	(void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog);
+
+	(void) _shttpd_snprintf(line, sizeof(line), "%s", dir);
 	fix_directory_separators(line);
 	fix_directory_separators(cmdline);
 
@@ -907,7 +471,8 @@
 
 	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);
+		_shttpd_elog(E_LOG, c,
+		    "redirect: CreateProcess(%s): %d", cmdline, ERRNO);
 		return (-1);
 	} else {
 		CloseHandle(h[0]);
@@ -920,3 +485,203 @@
 }
 
 #endif /* !NO_CGI */
+
+#define	ID_TRAYICON	100
+#define	ID_QUIT		101
+static NOTIFYICONDATA	ni;
+
+static LRESULT CALLBACK
+WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	POINT	pt;
+	HMENU	hMenu;
+
+	switch (msg) {
+	case WM_COMMAND:
+		switch (LOWORD(wParam)) {
+		case ID_QUIT:
+			exit(EXIT_SUCCESS);
+			break;
+		}
+		break;
+	case WM_USER:
+		switch (lParam) {
+		case WM_RBUTTONUP:
+		case WM_LBUTTONUP:
+		case WM_LBUTTONDBLCLK:
+			hMenu = CreatePopupMenu();
+			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));
+}
+
+static void
+systray(void *arg)
+{
+	WNDCLASS	cls;
+	HWND		hWnd;
+	MSG		msg;
+
+	(void) memset(&cls, 0, sizeof(cls));
+
+	cls.lpfnWndProc = (WNDPROC) WindowProc;
+	cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+	cls.lpszClassName = "shttpd v." SHTTPD_VERSION;
+
+	if (!RegisterClass(&cls))
+		_shttpd_elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
+	else if ((hWnd = CreateWindow(cls.lpszClassName, "",
+	    WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, arg)) == NULL)
+		_shttpd_elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
+	ShowWindow(hWnd, SW_HIDE);
+
+	ni.cbSize = sizeof(ni);
+	ni.uID = ID_TRAYICON;
+	ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+	ni.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+	ni.hWnd = hWnd;
+	_shttpd_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
+	ni.uCallbackMessage = WM_USER;
+	Shell_NotifyIcon(NIM_ADD, &ni);
+
+	while (GetMessage(&msg, hWnd, 0, 0)) {
+		TranslateMessage(&msg);
+		DispatchMessage(&msg);
+	}
+}
+
+int
+_shttpd_set_systray(struct shttpd_ctx *ctx, const char *opt)
+{
+	HWND		hWnd;
+	char		title[512];
+	static WNDPROC	oldproc;
+
+	if (!_shttpd_is_true(opt))
+		return (TRUE);
+
+	FreeConsole();
+	GetConsoleTitle(title, sizeof(title));
+	hWnd = FindWindow(NULL, title);
+	ShowWindow(hWnd, SW_HIDE);
+	_beginthread(systray, 0, hWnd);
+
+	return (TRUE);
+}
+
+int
+_shttpd_set_nt_service(struct shttpd_ctx *ctx, const char *action)
+{
+	SC_HANDLE	hSCM, hService;
+	char		path[FILENAME_MAX], key[128];
+	HKEY		hKey;
+	DWORD		dwData;
+
+
+	if (!strcmp(action, "install")) {
+		if ((hSCM = OpenSCManager(NULL, NULL,
+		    SC_MANAGER_ALL_ACCESS)) == NULL)
+			_shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
+
+		GetModuleFileName(NULL, path, sizeof(path));
+
+		hService = CreateService(hSCM, SERVICE_NAME, SERVICE_NAME,
+		    SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+		    SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
+		    NULL, NULL, NULL, NULL, NULL);
+
+		if (!hService)
+			_shttpd_elog(E_FATAL, NULL,
+			    "Error installing service (%d)", ERRNO);
+
+		ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION,
+		    &service_descr);
+		_shttpd_elog(E_FATAL, NULL, "Service successfully installed");
+
+
+	} else if (!strcmp(action, "uninstall")) {
+
+		if ((hSCM = OpenSCManager(NULL, NULL,
+		    SC_MANAGER_ALL_ACCESS)) == NULL) {
+			_shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO);
+		} else if ((hService = OpenService(hSCM,
+		    SERVICE_NAME, DELETE)) == NULL) {
+			_shttpd_elog(E_FATAL, NULL,
+			    "Error opening service (%d)", ERRNO);
+		} else if (!DeleteService(hService)) {
+			_shttpd_elog(E_FATAL, NULL,
+			    "Error deleting service (%d)", ERRNO);
+		} else {
+			_shttpd_elog(E_FATAL, NULL, "Service deleted");
+		}
+
+	} else {
+		_shttpd_elog(E_FATAL, NULL, "Use -service <install|uninstall>");
+	}
+
+	/* NOTREACHED */
+	return (TRUE);
+}
+
+static void WINAPI
+ControlHandler(DWORD code)
+{
+	if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
+		ss.dwWin32ExitCode	= 0;
+		ss.dwCurrentState	= SERVICE_STOPPED;
+	}
+
+	SetServiceStatus(hStatus, &ss);
+}
+
+static void WINAPI
+ServiceMain(int argc, char *argv[])
+{
+	char	path[MAX_PATH], *p, *av[] = {"shttpd_service", path, NULL};
+	struct shttpd_ctx	*ctx;
+
+	ss.dwServiceType      = SERVICE_WIN32;
+	ss.dwCurrentState     = SERVICE_RUNNING;
+	ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+
+	hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ControlHandler);
+	SetServiceStatus(hStatus, &ss);
+
+	GetModuleFileName(NULL, path, sizeof(path));
+
+	if ((p = strrchr(path, DIRSEP)) != NULL)
+		*++p = '\0';
+
+	strcat(path, CONFIG_FILE);	/* woo ! */
+
+	ctx = shttpd_init(NELEMS(av) - 1, av);
+	if ((ctx = shttpd_init(NELEMS(av) - 1, av)) == NULL)
+		_shttpd_elog(E_FATAL, NULL, "Cannot initialize SHTTP context");
+
+	while (ss.dwCurrentState == SERVICE_RUNNING)
+		shttpd_poll(ctx, INT_MAX);
+	shttpd_fini(ctx);
+
+	ss.dwCurrentState  = SERVICE_STOPPED;
+	ss.dwWin32ExitCode = -1;
+	SetServiceStatus(hStatus, &ss);
+}
+
+void
+try_to_run_as_nt_service(void)
+{
+	static SERVICE_TABLE_ENTRY service_table[] = {
+		{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
+		{NULL, NULL}
+	};
+
+	if (StartServiceCtrlDispatcher(service_table))
+		exit(EXIT_SUCCESS);
+}
diff --git a/src/server/shttpd/compat_win32.h b/src/server/shttpd/compat_win32.h
index a5c1dce..99ba99d 100644
--- a/src/server/shttpd/compat_win32.h
+++ b/src/server/shttpd/compat_win32.h
@@ -20,18 +20,6 @@
 
 #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>
@@ -51,7 +39,7 @@
 
 #define	ERRNO			GetLastError()
 #define	NO_SOCKLEN_T
-#define	SSL_LIB			L"libssl32.dll"
+#define	SSL_LIB			L"ssleay32.dll"
 #define	DIRSEP			'\\'
 #define	IS_DIRSEP_CHAR(c)	((c) == '/' || (c) == '\\')
 #define	O_NONBLOCK		0
diff --git a/src/server/shttpd/compat_wince.c b/src/server/shttpd/compat_wince.c
index 36702f6..edf8da2 100644
--- a/src/server/shttpd/compat_wince.c
+++ b/src/server/shttpd/compat_wince.c
@@ -860,7 +860,7 @@
 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]
@@ -892,7 +892,7 @@
 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. 
+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]
 
diff --git a/src/server/shttpd/compat_wince.h b/src/server/shttpd/compat_wince.h
index 651ec50..02f6557 100644
--- a/src/server/shttpd/compat_wince.h
+++ b/src/server/shttpd/compat_wince.h
@@ -97,7 +97,7 @@
 #define S_IFDIR  0040000
 #define S_IFREG  0100000
 #define S_IEXEC  0000100
-#define S_IWRITE 0000200 
+#define S_IWRITE 0000200
 #define S_IREAD  0000400
 
 #define _S_IFDIR S_IFDIR	/* MSVCRT compatibilit */
diff --git a/src/server/shttpd/config.c b/src/server/shttpd/config.c
deleted file mode 100644
index 67b904d..0000000
--- a/src/server/shttpd/config.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file.  As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "shttpd_defs.h"
-#include "wsmand-daemon.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 = 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));
-}
-
-static int isbyte(int n) { return (n >= 0 && n <= 255); }
-
-static void
-set_acl(struct shttpd_ctx *ctx, void *ptr, const char *s)
-{
-	struct llhead	*head = ptr;
-	struct acl	*acl;
-	char		flag;
-	int		len, a, b, c, d, n, mask;
-
-	ctx = NULL;
-
-	FOR_EACH_WORD_IN_LIST(s, len) {
-
-		mask = 32;
-
-		if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
-			elog(E_FATAL, NULL, "[%s]: subnet must be "
-			    "[+|-]x.x.x.x[/x]", s);
-		} else if (flag != '+' && flag != '-') {
-			elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
-		} else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
-			elog(E_FATAL, NULL, "bad ip address: [%s]", s);
-		} else	if ((acl = malloc(sizeof(*acl))) == NULL) {
-			elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
-		} else if (sscanf(s + n, "/%d", &mask) == 0) {
-			/* Do nothing, no mask specified */
-		} else if (mask < 0 || mask > 32) {
-			elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
-		}
-
-		acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
-		acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
-		acl->flag = flag;
-		LL_TAIL(head, &acl->link);
-	}
-}
-
-#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;
-        char *ssl_disabled_protocols = wsmand_options_get_ssl_disabled_protocols();
-
-	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);
-                ctx->ssl_ctx = NULL;
-                return;
-        }
-
-	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);
-                  ctx->ssl_ctx = NULL;
-                  return;
-                }
-        }
-
-	/* Initialize SSL crap */
-	static int ssl_library_initialized = 0;
-	if(!ssl_library_initialized) {
-		debug("Initialize SSL");
-		SSL_library_init();
-		ssl_library_initialized = 1;
-	}
-	if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL) {
-		elog(E_FATAL, NULL, "SSL_CTX_new error");
-        }
-        else if (wsmand_options_get_ssl_cert_file() && SSL_CTX_use_certificate_file(CTX, wsmand_options_get_ssl_cert_file(),SSL_FILETYPE_PEM) == 0) {
-		elog(E_FATAL, NULL, "cannot open %s : %s", pem, strerror(errno));
-                SSL_CTX_free(CTX);
-                CTX = NULL;
-        }
-	else if (wsmand_options_get_ssl_key_file() && SSL_CTX_use_PrivateKey_file(CTX, wsmand_options_get_ssl_key_file(), SSL_FILETYPE_PEM) == 0) {
-		elog(E_FATAL, NULL, "cannot open %s : %s", pem, strerror(errno));
-                SSL_CTX_free(CTX);
-                CTX = NULL;
-        }
-	while (ssl_disabled_protocols) {
-          struct ctx_opts_t {
-            char *name;
-            long opt;
-          } protocols[] = {
-            { "SSLv2", SSL_OP_NO_SSLv2 },
-            { "SSLv3", SSL_OP_NO_SSLv3 },
-            { "TLSv1", SSL_OP_NO_TLSv1 },
-# if OPENSSL_VERSION_NUMBER >= 0x10001000L
-            { "TLSv1_1", SSL_OP_NO_TLSv1_1 },
-            { "TLSv1_2", SSL_OP_NO_TLSv1_2 },
-# endif
-            { NULL, 0 }
-          };
-          char *blank_ptr;
-          int idx;
-          if (*ssl_disabled_protocols == 0)
-            break;
-          blank_ptr = strchr(ssl_disabled_protocols, ' ');
-          if (blank_ptr == NULL)
-            blank_ptr = ssl_disabled_protocols + strlen(ssl_disabled_protocols);
-          for (idx = 0; protocols[idx].name ; ++idx) {
-            if (strncasecmp(protocols[idx].name, ssl_disabled_protocols, blank_ptr-ssl_disabled_protocols) == 0) {
-              debug("SSL: disable %s protocol", protocols[idx].name);
-              SSL_CTX_ctrl(CTX, SSL_CTRL_OPTIONS, protocols[idx].opt, NULL);                      
-              break;
-            }
-          }
-          if (*blank_ptr == 0)
-            break;
-          ssl_disabled_protocols = blank_ptr + 1;          
-        }
-
-	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},
-	{'x', "acl", "Allow/deny IP addresses/subnets", set_acl,
-		OFS(acl), "acl_list", NULL, 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 = 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);
-	LL_INIT(&ctx->acl);
-
-#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] = 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 = 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;
-
-	elog(E_LOG, NULL, "Initializing http server");
-	/* 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/src/server/shttpd/defs.h b/src/server/shttpd/defs.h
new file mode 100644
index 0000000..f2d4adf
--- /dev/null
+++ b/src/server/shttpd/defs.h
@@ -0,0 +1,410 @@
+/*
+ * 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 "wsman_config.h"
+
+#include "std_includes.h"
+#include "llist.h"
+#include "io.h"
+#include "md5.h"
+#include "u/libu.h"
+#include "config.h"
+#include "shttpd.h"
+
+#define	NELEMS(ar)	(sizeof(ar) / sizeof(ar[0]))
+
+#ifdef _DEBUG
+#define	DBG(x)	do { printf x ; putchar('\n'); fflush(stdout); } while (0)
+#else
+#define	DBG(x)
+#endif /* DEBUG */
+
+/*
+ * Darwin prior to 7.0 and Win32 do not have socklen_t
+ */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+
+/*
+ * For parsing. This guy represents a substring.
+ */
+struct vec {
+	const char	*ptr;
+	int		len;
+};
+
+#if !defined(FALSE)
+enum {FALSE, TRUE};
+#endif /* !FALSE */
+
+enum {BASIC_AUTH, DIGEST_AUTH};
+enum {METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD};
+enum {HDR_DATE, HDR_INT, HDR_STRING};	/* HTTP header types		*/
+enum {E_FATAL = 1, E_LOG = 2};		/* Flags for elog() function	*/
+typedef unsigned long big_int_t;	/* Type for Content-Length	*/
+
+/*
+ * Unified socket address
+ */
+struct usa {
+	socklen_t len;
+	union {
+		struct sockaddr	sa;
+		struct sockaddr_in sin;
+	} u;
+};
+
+/*
+ * This thing is aimed to hold values of any type.
+ * Used to store parsed headers' values.
+ */
+union variant {
+	char		*v_str;
+	int		v_int;
+	big_int_t	v_big_int;
+	time_t		v_time;
+	void		(*v_func)(void);
+	void		*v_void;
+	struct vec	v_vec;
+};
+
+/*
+ * This is used only in embedded configuration. This structure holds a
+ * registered URI, associated callback function with callback data.
+ * For non-embedded compilation shttpd_callback_t is not defined, so
+ * we use union variant to keep the compiler silent.
+ */
+struct registered_uri {
+	struct llhead	link;
+	const char	*uri;
+	union variant	callback;
+	void		*callback_data;
+};
+
+struct uri_auth {
+        struct llhead   link;
+        const char      *uri;
+        const char      *file_name;
+        int type;
+        size_t          uri_len;
+        union variant callback;
+        void *callback_data;
+};
+
+/*
+ * User may want to handle certain errors. This structure holds the
+ * handlers for corresponding error codes.
+ */
+struct error_handler {
+	struct llhead	link;
+	int		code;
+	union variant	callback;
+	void		*callback_data;
+};
+
+struct http_header {
+	int		len;		/* Header name length		*/
+	int		type;		/* Header type			*/
+	size_t		offset;		/* Value placeholder		*/
+	const char	*name;		/* Header name			*/
+};
+
+/*
+ * This guy holds parsed HTTP headers
+ */
+struct headers {
+	union variant	cl;		/* Content-Length:		*/
+	union variant	ct;		/* Content-Type:		*/
+	union variant	connection;	/* Connection:			*/
+	union variant	ims;		/* If-Modified-Since:		*/
+	union variant	user;		/* Remote user name		*/
+	union variant	auth;		/* Authorization		*/
+	union variant	useragent;	/* User-Agent:			*/
+	union variant	referer;	/* Referer:			*/
+	union variant	cookie;		/* Cookie:			*/
+	union variant	location;	/* Location:			*/
+	union variant	range;		/* Range:			*/
+	union variant	status;		/* Status:			*/
+	union variant	transenc;	/* Transfer-Encoding:		*/
+};
+
+/* Must go after union variant definition */
+#include "ssl.h"
+
+/*
+ * The communication channel
+ */
+union channel {
+	int		fd;		/* Regular static file		*/
+	int		sock;		/* Connected socket		*/
+	struct {
+		int		sock;	/* XXX important. must be first	*/
+		SSL		*ssl;	/* shttpd_poll() assumes that	*/
+	} ssl;				/* SSL-ed socket		*/
+	struct {
+		DIR	*dirp;
+		char	*path;
+	} dir;				/* Opened directory		*/
+	struct {
+		void		*state;	/* For keeping state		*/
+		union variant	func;	/* User callback function	*/
+		void		*data;	/* User defined parameters	*/
+	} emb;				/* Embedded, user callback	*/
+};
+
+struct stream;
+
+/*
+ * IO class descriptor (file, directory, socket, SSL, CGI, etc)
+ * These classes are defined in io_*.c files.
+ */
+struct io_class {
+	const char *name;
+	int (*read)(struct stream *, void *buf, size_t len);
+	int (*write)(struct stream *, const void *buf, size_t len);
+	void (*close)(struct stream *);
+};
+
+/*
+ * Data exchange stream. It is backed by some communication channel:
+ * opened file, socket, etc. The 'read' and 'write' methods are
+ * determined by a communication channel.
+ */
+struct stream {
+	struct conn		*conn;
+	union channel		chan;		/* Descriptor		*/
+	struct io		io;		/* IO buffer		*/
+	const struct io_class	*io_class;	/* IO class		*/
+	int			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	*/
+#define	FLAG_SUSPEND		128
+};
+
+struct worker {
+	struct llhead	link;
+	int		num_conns;	/* Num of active connections 	*/
+	int		exit_flag;	/* Ditto - exit flag		*/
+	int		ctl[2];		/* Control socket pair		*/
+	struct shttpd_ctx *ctx;		/* Context reference		*/
+	struct llhead	connections;	/* List of connections		*/
+};
+
+struct conn {
+	struct llhead	link;		/* Connections chain		*/
+	struct worker	*worker;	/* Worker this conn belongs to	*/
+	struct shttpd_ctx *ctx;		/* Context this conn belongs to */
+	struct usa	sa;		/* Remote socket address	*/
+	time_t		birth_time;	/* Creation time		*/
+	time_t		expire_time;	/* Expiration time		*/
+
+	int		loc_port;	/* Local port			*/
+	int		status;		/* Reply status code		*/
+	int		method;		/* Request method		*/
+	char		*uri;		/* Decoded URI			*/
+	unsigned long	major_version;	/* Major HTTP version number    */
+	unsigned long	minor_version;	/* Minor HTTP version number    */
+	char		*request;	/* Request line			*/
+	char		*headers;	/* Request headers		*/
+	char		*query;		/* QUERY_STRING part of the URI	*/
+	char		*path_info;	/* PATH_INFO thing		*/
+	struct vec	mime_type;	/* Mime type			*/
+
+	struct headers	ch;		/* Parsed client headers	*/
+
+	struct stream	loc;		/* Local stream			*/
+	struct stream	rem;		/* Remote stream		*/
+
+#if !defined(NO_SSI)
+	void			*ssi;	/* SSI descriptor		*/
+#endif /* NO_SSI */
+};
+
+enum {
+	OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST,
+	OPT_CGI_EXTENSIONS, OPT_CGI_INTERPRETER, OPT_CGI_ENVIRONMENT,
+	OPT_SSI_EXTENSIONS, OPT_AUTH_REALM, OPT_AUTH_GPASSWD,
+	OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, OPT_MIME_TYPES,
+	OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_INETD, OPT_UID,
+	OPT_CFG_URI, OPT_PROTECT, OPT_SERVICE, OPT_HIDE, OPT_THREADS,
+	NUM_OPTIONS
+};
+
+/*
+ * SHTTPD context
+ */
+struct shttpd_ctx {
+	SSL_CTX		*ssl_ctx;	/* SSL context			*/
+
+	struct llhead	registered_uris;/* User urls			*/
+	struct llhead   uri_auths;      /* User auth files              */
+	struct llhead	error_handlers;	/* Embedded error handlers	*/
+	struct llhead	acl;		/* Access control list		*/
+	struct llhead	ssi_funcs;	/* SSI callback functions	*/
+	struct llhead	listeners;	/* Listening sockets		*/
+	struct llhead	workers;	/* Worker workers		*/
+
+	FILE		*access_log;	/* Access log stream		*/
+	FILE		*error_log;	/* Error log stream		*/
+
+	char	*options[NUM_OPTIONS];	/* Configurable options		*/
+#if defined(__rtems__)
+	rtems_id         mutex;
+#endif /* _WIN32 */
+};
+
+struct listener {
+	struct llhead		link;
+	struct shttpd_ctx	*ctx;	/* Context that socket belongs	*/
+	int			sock;	/* Listening socket		*/
+	int			is_ssl;	/* Should be SSL-ed		*/
+};
+
+/* Types of messages that could be sent over the control socket */
+enum {CTL_PASS_SOCKET, CTL_WAKEUP};
+
+/*
+ * In SHTTPD, list of values are represented as comma or space separated
+ * string. For example, list of CGI extensions can be represented as
+ * ".cgi,.php,.pl", or ".cgi .php .pl". The macro that follows allows to
+ * loop through the individual values in that list.
+ *
+ * A "const char *" pointer and size_t variable must be passed to the macro.
+ * Spaces or commas can be used as delimiters (macro DELIM_CHARS).
+ *
+ * In every iteration of the loop, "s" points to the current value, and
+ * "len" specifies its length. The code inside loop must not change
+ * "s" and "len" parameters.
+ */
+#define	FOR_EACH_WORD_IN_LIST(s,len)					\
+	for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0;	\
+			s += len, s+= strspn(s, DELIM_CHARS))
+
+/*
+ * IPv4 ACL entry. Specifies subnet with deny/allow flag
+ */
+struct acl {
+	struct llhead	link;
+	uint32_t	ip;		/* IP, in network byte order	*/
+	uint32_t	mask;		/* Also in network byte order	*/
+	int		flag;		/* Either '+' or '-'		*/
+};
+
+/*
+ * shttpd.c
+ */
+extern time_t	_shttpd_current_time;	/* Current UTC time		*/
+extern int	_shttpd_tz_offset;	/* Offset from GMT time zone	*/
+extern const struct vec _shttpd_known_http_methods[];
+
+extern void	_shttpd_stop_stream(struct stream *stream);
+extern int	_shttpd_url_decode(const char *, int, char *dst, int);
+extern void	_shttpd_send_server_error(struct conn *, int, const char *);
+extern int	_shttpd_get_headers_len(const char *buf, size_t buflen);
+extern void	_shttpd_parse_headers(const char *s, int, struct headers *);
+extern int	_shttpd_is_true(const char *str);
+extern int	_shttpd_socketpair(int pair[2]);
+extern void	_shttpd_get_mime_type(struct shttpd_ctx *,
+			const char *, int, struct vec *);
+
+#define	IS_TRUE(ctx, opt)	_shttpd_is_true((ctx)->options[opt])
+
+/*
+ * config.c
+ */
+extern void	_shttpd_usage(const char *prog);
+
+/*
+ * log.c
+ */
+extern void	_shttpd_elog(int flags, struct conn *c, const char *fmt, ...);
+extern void	_shttpd_log_access(FILE *fp, const struct conn *c);
+
+/*
+ * string.c
+ */
+extern void	_shttpd_strlcpy(register char *, register const char *, size_t);
+extern int	_shttpd_strncasecmp(register const char *,
+			register const char *, size_t);
+extern char	*_shttpd_strndup(const char *ptr, size_t len);
+extern char	*_shttpd_strdup(const char *str);
+extern int	_shttpd_snprintf(char *buf, size_t len, const char *fmt, ...);
+extern int	_shttpd_match_extension(const char *path, const char *ext_list);
+
+/*
+ * compat_*.c
+ */
+extern void	_shttpd_set_close_on_exec(int fd);
+extern int	_shttpd_set_non_blocking_mode(int fd);
+extern int	_shttpd_stat(const char *, struct stat *stp);
+extern int	_shttpd_open(const char *, int flags, int mode);
+extern int	_shttpd_remove(const char *);
+extern int	_shttpd_rename(const char *, const char *);
+extern int	_shttpd_mkdir(const char *, int);
+extern char *	_shttpd_getcwd(char *, int);
+extern int	_shttpd_spawn_process(struct conn *c, const char *prog,
+			char *envblk, char *envp[], int sock, const char *dir);
+
+extern int	_shttpd_set_nt_service(struct shttpd_ctx *, const char *);
+extern int	_shttpd_set_systray(struct shttpd_ctx *, const char *);
+extern void	_shttpd_try_to_run_as_nt_service(void);
+
+/*
+ * io_*.c
+ */
+extern const struct io_class	_shttpd_io_file;
+extern const struct io_class	_shttpd_io_socket;
+extern const struct io_class	_shttpd_io_ssl;
+extern const struct io_class	_shttpd_io_cgi;
+extern const struct io_class	_shttpd_io_dir;
+extern const struct io_class	_shttpd_io_embedded;
+extern const struct io_class	_shttpd_io_ssi;
+
+extern int	_shttpd_put_dir(const char *path);
+extern void	_shttpd_get_dir(struct conn *c);
+extern void	_shttpd_get_file(struct conn *c, struct stat *stp);
+extern void	_shttpd_ssl_handshake(struct stream *stream);
+extern void	_shttpd_setup_embedded_stream(struct conn *,
+			union variant, void *);
+extern struct registered_uri *_shttpd_is_registered_uri(struct shttpd_ctx *,
+			const char *uri);
+extern void	_shttpd_do_ssi(struct conn *);
+extern void	_shttpd_ssi_func_destructor(struct llhead *lp);
+
+/*
+ * auth.c
+ */
+extern int	_shttpd_check_authorization(struct conn *c, const char *path);
+extern int	_shttpd_is_authorized_for_put(struct conn *c);
+extern void	_shttpd_send_authorization_request(struct conn *c);
+extern int	_shttpd_edit_passwords(const char *fname, const char *domain,
+			const char *user, const char *pass);
+
+/*
+ * cgi.c
+ */
+extern int	_shttpd_run_cgi(struct conn *c, const char *prog);
+extern void	_shttpd_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/src/server/shttpd/io.h b/src/server/shttpd/io.h
index 5439917..d774cc2 100644
--- a/src/server/shttpd/io.h
+++ b/src/server/shttpd/io.h
@@ -14,14 +14,6 @@
 #include <assert.h>
 #include <stddef.h>
 
-#ifdef __GNUC__
-#define __INLINE__ __inline__
-#elif _WIN32
-#define __INLINE__ __inline
-#elif defined (__SUNPRO_C) || defined (__SUNPRO_CC)
-#define __INLINE__ inline
-#endif
-
 /*
  * I/O buffer descriptor
  */
@@ -33,7 +25,7 @@
 	size_t		total;		/* Total bytes read		*/
 };
 
-static __INLINE__ void
+static __inline void
 io_clear(struct io *io)
 {
 	assert(io->buf != NULL);
@@ -41,7 +33,7 @@
 	io->total = io->tail = io->head = 0;
 }
 
-static __INLINE__ char *
+static __inline char *
 io_space(struct io *io)
 {
 	assert(io->buf != NULL);
@@ -50,7 +42,7 @@
 	return (io->buf + io->head);
 }
 
-static __INLINE__ char *
+static __inline char *
 io_data(struct io *io)
 {
 	assert(io->buf != NULL);
@@ -59,7 +51,7 @@
 	return (io->buf + io->tail);
 }
 
-static __INLINE__ size_t
+static __inline size_t
 io_space_len(const struct io *io)
 {
 	assert(io->buf != NULL);
@@ -68,7 +60,7 @@
 	return (io->size - io->head);
 }
 
-static __INLINE__ size_t
+static __inline size_t
 io_data_len(const struct io *io)
 {
 	assert(io->buf != NULL);
@@ -78,7 +70,7 @@
 	return (io->head - io->tail);
 }
 
-static __INLINE__ void
+static __inline void
 io_inc_tail(struct io *io, size_t n)
 {
 	assert(io->buf != NULL);
@@ -91,7 +83,7 @@
 		io->head = io->tail = 0;
 }
 
-static __INLINE__ void
+static __inline void
 io_inc_head(struct io *io, size_t n)
 {
 	assert(io->buf != NULL);
diff --git a/src/server/shttpd/io_cgi.c b/src/server/shttpd/io_cgi.c
new file mode 100644
index 0000000..b41c600
--- /dev/null
+++ b/src/server/shttpd/io_cgi.c
@@ -0,0 +1,127 @@
+/*
+ * 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) {
+		_shttpd_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 = _shttpd_get_headers_len(stream->io.buf,
+	    stream->io.head);
+	if (stream->headers_len < 0) {
+		stream->flags &= ~FLAG_DONT_CLOSE;
+		_shttpd_send_server_error(stream->conn, 500,
+		    "Bad headers sent");
+		_shttpd_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));
+	_shttpd_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) _shttpd_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	_shttpd_io_cgi =  {
+	"cgi",
+	read_cgi,
+	write_cgi,
+	close_cgi
+};
diff --git a/src/server/shttpd/io_dir.c b/src/server/shttpd/io_dir.c
index d8ed661..7366398 100644
--- a/src/server/shttpd/io_dir.c
+++ b/src/server/shttpd/io_dir.c
@@ -8,7 +8,7 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 /*
  * For a given PUT path, create all intermediate subdirectories
@@ -16,7 +16,7 @@
  * or -1 on error, 1 if OK.
  */
 int
-put_dir(const char *path)
+_shttpd_put_dir(const char *path)
 {
 	char		buf[FILENAME_MAX];
 	const char	*s, *p;
@@ -30,7 +30,8 @@
 		buf[len] = '\0';
 
 		/* Try to create intermediate directory */
-		if (my_stat(buf, &st) == -1 && my_mkdir(buf, 0755) != 0)
+		if (_shttpd_stat(buf, &st) == -1 &&
+		    _shttpd_mkdir(buf, 0755) != 0)
 			return (-1);
 
 		/* Is path itself a directory ? */
@@ -44,6 +45,8 @@
 static int
 read_dir(struct stream *stream, void *buf, size_t len)
 {
+	static const char footer[] = "</table></body></html>\n";
+
 	struct dirent	*dp = NULL;
 	char		file[FILENAME_MAX], line[FILENAME_MAX + 512],
 				size[64], mod[64];
@@ -59,10 +62,8 @@
 		if (len < sizeof(line))
 			break;
 
-		if ((dp = readdir(stream->chan.dir.dirp)) == NULL) {
-			stream->flags |= FLAG_CLOSED;
+		if ((dp = readdir(stream->chan.dir.dirp)) == NULL)
 			break;
-		}
 		DBG(("read_dir: %s", dp->d_name));
 
 		/* Do not show current dir and passwords file */
@@ -70,26 +71,27 @@
 		   strcmp(dp->d_name, HTPASSWD) == 0)
 			continue;
 
-		(void) snprintf(file, sizeof(file),
+		(void) _shttpd_snprintf(file, sizeof(file),
 		    "%s%s%s", stream->chan.dir.path, slash, dp->d_name);
-		(void) my_stat(file, &st);
+		(void) _shttpd_stat(file, &st);
 		if (S_ISDIR(st.st_mode)) {
-			snprintf(size,sizeof(size),"%s","&lt;DIR&gt;");
+			_shttpd_snprintf(size,sizeof(size),"%s","&lt;DIR&gt;");
 		} else {
 			if (st.st_size < 1024)
-				(void) snprintf(size, sizeof(size),
+				(void) _shttpd_snprintf(size, sizeof(size),
 				    "%lu", (unsigned long) st.st_size);
 			else if (st.st_size < 1024 * 1024)
-				(void) snprintf(size, sizeof(size), "%luk",
+				(void) _shttpd_snprintf(size,
+				    sizeof(size), "%luk",
 				    (unsigned long) (st.st_size >> 10)  + 1);
 			else
-				(void) snprintf(size, sizeof(size),
+				(void) _shttpd_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 = snprintf(line, sizeof(line),
+		n = _shttpd_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,
@@ -100,6 +102,13 @@
 		len -= n;
 	} while (dp != NULL);
 
+	/* Append proper HTML footer for the page */
+	if (dp == NULL && len >= sizeof(footer)) {
+		(void) memcpy(buf, footer, sizeof(footer));
+		nwritten += sizeof(footer);
+		stream->flags |= FLAG_CLOSED;
+	}
+
 	return (nwritten);
 }
 
@@ -113,14 +122,15 @@
 }
 
 void
-get_dir(struct conn *c)
+_shttpd_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");
+		_shttpd_send_server_error(c, 500, "Cannot open directory");
 	} else {
-		c->loc.io.head = snprintf(c->loc.io.buf, c->loc.io.size,
+		c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
 		    "HTTP/1.1 200 OK\r\n"
+		    "Connection: close\r\n"
 		    "Content-Type: text/html; charset=utf-8\r\n\r\n"
 		    "<html><head><title>Index of %s</title>"
 		    "<style>th {text-align: left;}</style></head>"
@@ -130,12 +140,12 @@
 		    c->uri, c->uri);
 		io_clear(&c->rem.io);
 		c->status = 200;
-		c->loc.io_class = &io_dir;
+		c->loc.io_class = &_shttpd_io_dir;
 		c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
 	}
 }
 
-const struct io_class	io_dir =  {
+const struct io_class	_shttpd_io_dir =  {
 	"dir",
 	read_dir,
 	NULL,
diff --git a/src/server/shttpd/io_emb.c b/src/server/shttpd/io_emb.c
index ee40a8b..e2d7f69 100644
--- a/src/server/shttpd/io_emb.c
+++ b/src/server/shttpd/io_emb.c
@@ -8,17 +8,12 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
-#ifdef ENABLE_IPV6
-extern int wsmand_options_get_use_ipv6(void);
-#endif
-
-#if defined(EMBEDDED)
 const char *
 shttpd_version(void)
 {
-	return (VERSION);
+	return (SHTTPD_VERSION);
 }
 
 static void
@@ -33,13 +28,11 @@
 	arg->in.len		= io_data_len(&c->rem.io);
 	arg->in.num_bytes	= 0;
 
-	if (io_data_len(&c->rem.io) >= c->rem.io.size) {
+	if (io_data_len(&c->rem.io) >= c->rem.io.size)
 		arg->flags |= SHTTPD_POST_BUFFER_FULL;
-	}
 
-	if (c->rem.content_len > 0 && c->rem.io.total < c->rem.content_len) {
-			arg->flags |= SHTTPD_MORE_POST_DATA;
-	}
+	if (c->rem.content_len > 0 && c->rem.io.total < c->rem.content_len)
+		arg->flags |= SHTTPD_MORE_POST_DATA;
 
 	func(arg);
 
@@ -56,15 +49,12 @@
 	 */
 
 	if (arg->flags & SHTTPD_END_OF_OUTPUT)
-	{
-		c->loc.flags |= FLAG_RESPONSE_COMPLETE;
 		c->loc.flags &= ~FLAG_DONT_CLOSE;
-}
 	else
-{
-c->loc.flags &= ~FLAG_RESPONSE_COMPLETE;
 		c->loc.flags |= FLAG_DONT_CLOSE;
-}
+
+	if (arg->flags & SHTTPD_SUSPEND)
+		c->loc.flags |= FLAG_SUSPEND;
 }
 
 static int
@@ -103,14 +93,10 @@
 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);
@@ -138,7 +124,7 @@
 	while (p < e) {
 		if ((s = strchr(p, '\n')) != NULL)
 			s[s[-1] == '\r' ? -1 : 0] = '\0';
-		if (strncasecmp(header_name, p, len) == 0)
+		if (_shttpd_strncasecmp(header_name, p, len) == 0)
 			return (p + len + 2);
 
 		p += strlen(p) + 1;
@@ -154,7 +140,7 @@
 	struct vec	*vec;
 
 	if (strcmp(env_name, "REQUEST_METHOD") == 0) {
-		return (known_http_methods[c->method].ptr);
+		return (_shttpd_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) {
@@ -166,25 +152,15 @@
 			return (vec->ptr);
 		}
 	} else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
-#ifdef ENABLE_IPV6
-		if (wsmand_options_get_use_ipv6()) {
-			static char str[INET6_ADDRSTRLEN];
-			inet_ntop( AF_INET6,&c->sa.u.sin6.sin6_addr, str, sizeof(str));
-			return (const char*)str;
-		}
-		else {
-#endif
-			return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
-#ifdef ENABLE_IPV6
-		}
-#endif
+		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)
+shttpd_get_http_version(struct shttpd_arg *arg,
+		unsigned long *major, unsigned long *minor)
 {
 	struct conn *c = arg->priv;
 
@@ -199,47 +175,39 @@
 	struct registered_uri	*e;
 
 	if ((e = malloc(sizeof(*e))) != NULL) {
-		e->uri			= strdup(uri);
+		e->uri			= _shttpd_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,
-				basic_auth_callback cb, int type)
+                                basic_auth_callback cb, int type)
 {
-	struct uri_auth	*auth;
+        struct uri_auth *auth;
 
-	if ((auth = malloc(sizeof(*auth))) != NULL) {
-		auth->uri	= strdup(uri);
-		if (file)
-			auth->file_name	= strdup(file);
-		else
-			auth->file_name	= NULL;
-		if (cb)
-			auth->callback.v_func = (void (*)(void)) cb;
-		auth->uri_len	= strlen(uri);
-		if (type == BASIC_AUTH || type == DIGEST_AUTH)
-			auth->type = type;
-		else
-			auth->type = DIGEST_AUTH;
-		LL_ADD(&ctx->uri_auths, &auth->link);
-	}
+        if ((auth = malloc(sizeof(*auth))) != NULL) {
+                auth->uri       = strdup(uri);
+                if (file) {
+                        auth->file_name = strdup(file);
+		}
+                else {
+                        auth->file_name = NULL;
+		}
+                if (cb) {
+                        auth->callback.v_func = (void (*)(void)) cb;
+		}
+                auth->uri_len   = strlen(uri);
+                if (type == BASIC_AUTH || type == DIGEST_AUTH) {
+                        auth->type = type;
+		}
+                else {
+                        auth->type = DIGEST_AUTH;
+		}
+                LL_ADD(&ctx->uri_auths, &auth->link);
+        }
 }
 
 int
@@ -256,7 +224,7 @@
 	for (p = buf; p + var_len < e; p++)
 		if ((p == buf || p[-1] == '&') &&
 		    p[var_len] == '=' &&
-		    !strncasecmp(var, p, var_len)) {
+		    !_shttpd_strncasecmp(var, p, var_len)) {
 
 			/* Point 'p' to var value, 's' to the end of value */
 			p += var_len + 1;
@@ -264,7 +232,7 @@
 				s = e;
 
 			/* URL-decode value. Return result length */
-			return (url_decode(p, s - p, value, value_len));
+			return (_shttpd_url_decode(p, s - p, value, value_len));
 		}
 
 	return (-1);
@@ -289,7 +257,7 @@
 }
 
 struct registered_uri *
-is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
+_shttpd_is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
 {
 	struct llhead		*lp;
 	struct registered_uri	*reg_uri;
@@ -304,12 +272,12 @@
 }
 
 void
-setup_embedded_stream(struct conn *c, union variant func, void *data)
+_shttpd_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.io_class = &_shttpd_io_embedded;
 	c->loc.flags |= FLAG_R | FLAG_W |FLAG_ALWAYS_READY;
 }
 
@@ -327,11 +295,25 @@
 	}
 }
 
-const struct io_class	io_embedded =  {
+void
+shttpd_wakeup(const void *priv)
+{
+	const struct conn	*conn = priv;
+	char			buf[sizeof(int) + sizeof(void *)];
+	int			cmd = CTL_WAKEUP;
+
+#if 0
+	conn->flags &= ~SHTTPD_SUSPEND;
+#endif
+	(void) memcpy(buf, &cmd, sizeof(cmd));
+	(void) memcpy(buf + sizeof(cmd), conn, sizeof(conn));
+
+	(void) send(conn->worker->ctl[1], buf, sizeof(buf), 0);
+}
+
+const struct io_class	_shttpd_io_embedded =  {
 	"embedded",
 	do_embedded,
 	(int (*)(struct stream *, const void *, size_t)) do_embedded,
 	close_embedded
 };
-
-#endif /* EMBEDDED */
diff --git a/src/server/shttpd/io_file.c b/src/server/shttpd/io_file.c
index fefb2b6..75e879b 100644
--- a/src/server/shttpd/io_file.c
+++ b/src/server/shttpd/io_file.c
@@ -8,7 +8,7 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 static int
 write_file(struct stream *stream, const void *buf, size_t len)
@@ -20,16 +20,16 @@
 	assert(fd != -1);
 	n = write(fd, buf, len);
 
-	DBG(("put_file(%p, %d): %d bytes", (void *) stream, len, n));
+	DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
 
-	if (n <= 0 || (rem->io.total >= (big_int_t) rem->headers_len)) {
+	if (n <= 0 || (rem->io.total >= (big_int_t) rem->content_len)) {
 		(void) fstat(fd, &st);
 		stream->io.head = stream->headers_len =
-		    snprintf(stream->io.buf,
+		    _shttpd_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);
+		_shttpd_stop_stream(stream);
 	}
 
 	return (n);
@@ -90,16 +90,17 @@
 }
 
 void
-get_file(struct conn *c, struct stat *stp)
+_shttpd_get_file(struct conn *c, struct stat *stp)
 {
 	char		date[64], lm[64], etag[64], range[64] = "";
-	int		n, status = 200;
+	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));
+	if (c->mime_type.len == 0)
+		_shttpd_get_mime_type(c->ctx, c->uri,
+		    strlen(c->uri), &c->mime_type);
 	cl = (big_int_t) stp->st_size;
 
 	/* If Range: header specified, act accordingly */
@@ -108,16 +109,17 @@
 		status = 206;
 		(void) lseek(c->loc.chan.fd, r1, SEEK_SET);
 		cl = n == 2 ? r2 - r1 + 1: cl - r1;
-		(void) snprintf(range, sizeof(range),
+		(void) _shttpd_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(date, sizeof(date),
+	    fmt, localtime(&_shttpd_current_time));
 	(void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
-	(void) snprintf(etag, sizeof(etag), "%lx.%lx",
+	(void) _shttpd_snprintf(etag, sizeof(etag), "%lx.%lx",
 	    (unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
 
 	/*
@@ -125,28 +127,29 @@
 	 * 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 = snprintf(c->loc.io.buf,
+	c->loc.io.head = c->loc.headers_len = _shttpd_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-Type: %.*s\r\n"
 	    "Content-Length: %lu\r\n"
-	    "Connection: close\r\n"
+	    "Accept-Ranges: bytes\r\n"
 	    "%s\r\n",
-	    status, msg, date, lm, etag, c->mime_type, cl, range);
+	    status, msg, date, lm, etag,
+	    c->mime_type.len, c->mime_type.ptr, cl, range);
 
 	c->status = status;
 	c->loc.content_len = cl;
-	c->loc.io_class = &io_file;
+	c->loc.io_class = &_shttpd_io_file;
 	c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
 
 	if (c->method == METHOD_HEAD)
-		stop_stream(&c->loc);
+		_shttpd_stop_stream(&c->loc);
 }
 
-const struct io_class	io_file =  {
+const struct io_class	_shttpd_io_file =  {
 	"file",
 	read_file,
 	write_file,
diff --git a/src/server/shttpd/io_socket.c b/src/server/shttpd/io_socket.c
index 3e183e6..20bf207 100644
--- a/src/server/shttpd/io_socket.c
+++ b/src/server/shttpd/io_socket.c
@@ -8,7 +8,7 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 static int
 read_socket(struct stream *stream, void *buf, size_t len)
@@ -28,11 +28,10 @@
 close_socket(struct stream *stream)
 {
 	assert(stream->chan.sock != -1);
-	shutdown(stream->chan.sock,SHUT_RDWR);
 	(void) closesocket(stream->chan.sock);
 }
 
-const struct io_class	io_socket =  {
+const struct io_class	_shttpd_io_socket =  {
 	"socket",
 	read_socket,
 	write_socket,
diff --git a/src/server/shttpd/io_ssi.c b/src/server/shttpd/io_ssi.c
new file mode 100644
index 0000000..750b059
--- /dev/null
+++ b/src/server/shttpd/io_ssi.c
@@ -0,0 +1,488 @@
+/*
+ * 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		= _shttpd_strdup(name);
+		e->func		= func;
+		e->user_data	= user_data;
+		LL_TAIL(&ctx->ssi_funcs, &e->link);
+	}
+}
+
+void
+_shttpd_ssi_func_destructor(struct llhead *lp)
+{
+	struct ssi_func	*e = LL_ENTRY(lp, struct ssi_func, link);
+
+	free(e->name);
+	free(e);
+}
+
+static const struct ssi_func *
+find_ssi_func(struct ssi *ssi, const char *name)
+{
+	struct ssi_func	*e;
+	struct llhead	*lp;
+
+	LL_FOREACH(&ssi->conn->ctx->ssi_funcs, lp) {
+		e = LL_ENTRY(lp, struct ssi_func, link);
+		if (!strcmp(name, e->name))
+			return (e);
+	}
+
+	return (NULL);
+}
+
+static void
+call(struct ssi *ssi, const char *name,
+		struct shttpd_arg *arg, char *buf, int len)
+{
+	const struct ssi_func	*ssi_func;
+
+	(void) memset(arg, 0, sizeof(*arg));
+
+	/*
+	 * SSI function may be called with parameters. These parameters
+	 * are passed as arg->in.buf, arg->in.len vector.
+	 */
+	arg->in.buf = strchr(name, ' ');
+	if (arg->in.buf != NULL) {
+		*arg->in.buf++ = '\0';
+		arg->in.len = strlen(arg->in.buf);
+	}
+
+	if ((ssi_func = find_ssi_func(ssi, name)) != NULL) {
+		arg->priv = ssi->conn;
+		arg->user_data = ssi_func->user_data;
+		arg->out.buf = buf;
+		arg->out.len = len;
+		ssi_func->func(arg);
+	}
+}
+
+static int
+evaluate(struct ssi *ssi, const char *name)
+{
+	struct shttpd_arg	arg;
+
+	call(ssi, name, &arg, NULL, 0);
+
+	return (arg.flags & SHTTPD_SSI_EVAL_TRUE);
+}
+
+static void
+pass(struct ssi_inc *inc, void *buf, int *n)
+{
+	if (inc->cond == SSI_GO) {
+		(void) memcpy(buf, inc->buf, inc->nbuf);
+		(*n) += inc->nbuf;
+	}
+	inc->nbuf = 0;
+	inc->state = SSI_PASS;
+}
+
+static int
+get_path(struct conn *conn, const char *src,
+		int src_len, char *dst, int dst_len)
+{
+	static struct vec	accepted[] = {
+		{"\"",		1},	/* Relative to webserver CWD	*/
+		{"file=\"", 	6},	/* Relative to current URI	*/
+		{"virtual=\"", 	9},	/* Relative to document root	*/
+		{NULL,		0},
+	};
+	struct vec	*vec;
+	const char	*p, *root = conn->ctx->options[OPT_ROOT];
+	int		len;
+
+	for (vec = accepted; vec->len > 0; vec++)
+		if (src_len > vec->len && !memcmp(src, vec->ptr, vec->len)) {
+			src += vec->len;
+			src_len -= vec->len;
+			if ((p = memchr(src, '"', src_len)) == NULL)
+				break;
+			if (vec->len == 6) {
+				len = _shttpd_snprintf(dst, dst_len, "%s%c%s",
+				    root, DIRSEP, conn->uri);
+				while (len > 0 && dst[len] != '/')
+					len--;
+				dst += len;
+				dst_len -= len;
+			} else if (vec->len == 9) {
+				len = _shttpd_snprintf(dst, dst_len, "%s%c",
+				    root, DIRSEP);
+				dst += len;
+				dst_len -= len;
+			}
+			_shttpd_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) {
+		_shttpd_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))) {
+		_shttpd_elog(E_LOG, ssi->conn, "ssi: bad #include: [%.*s]",
+		    inc->nbuf, inc->buf);
+	} else if ((fp = fopen(buf, "r")) == NULL) {
+		_shttpd_elog(E_LOG, ssi->conn,
+		    "ssi: fopen(%s): %s", buf, strerror(errno));
+	} else {
+		ssi->nest++;
+		ssi->incs[ssi->nest].fp = fp;
+		ssi->incs[ssi->nest].nbuf = 0;
+		ssi->incs[ssi->nest].cond = SSI_GO;
+	}
+}
+
+static char *
+trim_spaces(struct ssi_inc *inc)
+{
+	char	*p = inc->buf + inc->nbuf - 2;
+
+	/* Trim spaces from the right */
+	*p-- = '\0';
+	while (isspace(* (unsigned char *) p))
+		*p-- = '\0';
+
+	/* Shift pointer to the start of attributes */
+	for (p = inc->buf; !isspace(* (unsigned char *) p); p++);
+	while (*p && isspace(* (unsigned char *) p)) p++;
+
+	return (p);
+}
+
+static void
+do_if(struct ssi *ssi)
+{
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+	char		*name = trim_spaces(inc);
+
+	inc->cond = evaluate(ssi, name) ? SSI_GO : SSI_STOP;
+}
+
+static void
+do_elif(struct ssi *ssi)
+{
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+	char		*name = trim_spaces(inc);
+
+	if (inc->cond == SSI_STOP && evaluate(ssi, name))
+		inc->cond = SSI_GO;
+	else
+		inc->cond = SSI_STOP;
+}
+static void
+do_endif(struct ssi *ssi)
+{
+	ssi->incs[ssi->nest].cond = SSI_GO;
+}
+
+static void
+do_else(struct ssi *ssi)
+{
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+
+	inc->cond = inc->cond == SSI_GO ? SSI_STOP : SSI_GO;
+}
+
+static void
+do_call2(struct ssi *ssi, char *buf, int len, int *n)
+{
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+	struct shttpd_arg	arg;
+
+	call(ssi, inc->buf, &arg, buf, len);
+	(*n) += arg.out.num_bytes;
+	if (arg.flags & SHTTPD_END_OF_OUTPUT)
+		inc->state = SSI_PASS;
+}
+
+static void
+do_call(struct ssi *ssi, char *buf, int len, int *n)
+{
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+	char		*name = trim_spaces(inc);
+
+	if (inc->cond == SSI_GO) {
+		(void) memmove(inc->buf, name, strlen(name) + 1);
+		inc->state = SSI_CALL;
+		do_call2(ssi, buf, len, n);
+	}
+}
+
+static void
+do_exec2(struct ssi *ssi, char *buf, int len, int *n)
+{
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+	int		i, ch;
+
+	for (i = 0; i < len; i++) {
+		if ((ch = fgetc(inc->pipe)) == EOF) {
+			inc->state = SSI_PASS;
+			(void) pclose(inc->pipe);
+			inc->pipe = NULL;
+			break;
+		}
+		*buf++ = ch;
+		(*n)++;
+	}
+}
+
+static void
+do_exec(struct ssi *ssi, char *buf, int len, int *n)
+{
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+	char		cmd[sizeof(inc->buf)], *e, *p;
+
+	p = trim_spaces(inc);
+
+	if (inc->cond == SSI_STOP) {
+		/* Do nothing - conditional FALSE */
+	} else if (*p != '"' || (e = strchr(p + 1, '"')) == NULL) {
+		_shttpd_elog(E_LOG, ssi->conn, "ssi: bad exec(%s)", p);
+	} else if (!_shttpd_url_decode(p + 1, e - p - 1, cmd, sizeof(cmd))) {
+		_shttpd_elog(E_LOG, ssi->conn,
+		    "ssi: cannot url_decode: exec(%s)", p);
+	} else if ((inc->pipe = popen(cmd, "r")) == NULL) {
+		_shttpd_elog(E_LOG, ssi->conn, "ssi: popen(%s)", cmd);
+	} else {
+		inc->state = SSI_EXEC;
+		do_exec2(ssi, buf, len, n);
+	}
+}
+
+static const struct ssi_cmd {
+	struct vec	vec;
+	void (*func)();
+} known_ssi_commands [] = {
+	{{"include ",	8}, do_include	},
+	{{"if ",	3}, do_if	},
+	{{"elif ",	5}, do_elif	},
+	{{"else",	4}, do_else	},
+	{{"endif",	5}, do_endif	},
+	{{"call ",	5}, do_call	},
+	{{"exec ",	5}, do_exec	},
+	{{NULL,		0}, NULL	}
+};
+
+static void
+do_command(struct ssi *ssi, char *buf, size_t len, int *n)
+{
+	struct ssi_inc		*inc = ssi->incs + ssi->nest;
+	const struct ssi_cmd	*cmd;
+
+	assert(len > 0);
+	assert(inc->nbuf <= len);
+	inc->state = SSI_PASS;
+
+	for (cmd = known_ssi_commands; cmd->func != NULL; cmd++)
+		if (inc->nbuf > (size_t) st.len + cmd->vec.len &&
+		    !memcmp(inc->buf + st.len, cmd->vec.ptr, cmd->vec.len)) {
+			cmd->func(ssi, buf, len, n);
+			break;
+		}
+
+	if (cmd->func == NULL)
+		pass(inc, buf, n);
+
+	inc->nbuf = 0;
+}
+
+static int
+read_ssi(struct stream *stream, void *vbuf, size_t len)
+{
+	struct ssi	*ssi = stream->conn->ssi;
+	struct ssi_inc	*inc = ssi->incs + ssi->nest;
+	char		*buf = vbuf;
+	int		ch = EOF, n = 0;
+
+again:
+
+	if (inc->state == SSI_CALL)
+		do_call2(ssi, buf, len, &n);
+	else if (inc->state == SSI_EXEC)
+		do_exec2(ssi, buf, len, &n);
+
+	while (n + inc->nbuf < len && (ch = fgetc(inc->fp)) != EOF)
+
+		switch (inc->state) {
+
+		case SSI_PASS:
+			if (ch == '<') {
+				inc->nbuf = 0;
+				inc->buf[inc->nbuf++] = ch;
+				inc->state = SSI_BUF;
+			} else if (inc->cond == SSI_GO) {
+				buf[n++] = ch;
+			}
+			break;
+
+		/*
+		 * We are buffering whole SSI command, until closing "-->".
+		 * That means that when do_command() is called, we can rely
+		 * on that full command with arguments is buffered in and
+		 * there is no need for streaming.
+		 * Restrictions:
+		 *  1. The command must fit in CMDBUFSIZ
+		 *  2. HTML comments inside the command ? Not sure about this.
+		 */
+		case SSI_BUF:
+			if (inc->nbuf >= sizeof(inc->buf) - 1) {
+				pass(inc, buf + n, &n);
+			} else if (ch == '>' &&
+			    !memcmp(inc->buf + inc->nbuf - 2, "--", 2)) {
+				do_command(ssi, buf + n, len - n, &n);
+				inc = ssi->incs + ssi->nest;
+			} else {
+				inc->buf[inc->nbuf++] = ch;
+
+				/* If not SSI tag, pass it */
+				if (inc->nbuf <= (size_t) st.len &&
+				    memcmp(inc->buf, st.ptr, inc->nbuf) != 0)
+					pass(inc, buf + n, &n);
+			}
+			break;
+
+		case SSI_EXEC:
+		case SSI_CALL:
+			break;
+
+		default:
+			/* Never happens */
+			abort();
+			break;
+		}
+
+	if (ssi->nest > 0 && n + inc->nbuf < len && ch == EOF) {
+		(void) fclose(inc->fp);
+		inc->fp = NULL;
+		ssi->nest--;
+		inc--;
+		goto again;
+	}
+
+	return (n);
+}
+
+static void
+close_ssi(struct stream *stream)
+{
+	struct ssi	*ssi = stream->conn->ssi;
+	size_t		i;
+
+	for (i = 0; i < NELEMS(ssi->incs); i++) {
+		if (ssi->incs[i].fp != NULL)
+			(void) fclose(ssi->incs[i].fp);
+		if (ssi->incs[i].pipe != NULL)
+			(void) pclose(ssi->incs[i].pipe);
+	}
+
+	free(ssi);
+}
+
+void
+_shttpd_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(&_shttpd_current_time));
+
+	c->loc.io.head = c->loc.headers_len = _shttpd_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 = &_shttpd_io_ssi;
+	c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
+
+	if (c->method == METHOD_HEAD) {
+		_shttpd_stop_stream(&c->loc);
+	} else if ((ssi = calloc(1, sizeof(struct ssi))) == NULL) {
+		_shttpd_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	_shttpd_io_ssi =  {
+	"ssi",
+	read_ssi,
+	NULL,
+	close_ssi
+};
+
+#endif /* !NO_SSI */
diff --git a/src/server/shttpd/io_ssl.c b/src/server/shttpd/io_ssl.c
index 293d0b5..6de0db2 100644
--- a/src/server/shttpd/io_ssl.c
+++ b/src/server/shttpd/io_ssl.c
@@ -8,7 +8,7 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 #if !defined(NO_SSL)
 struct ssl_func	ssl_sw[] = {
@@ -25,42 +25,37 @@
 	{"SSL_library_init",		{0}},
 	{"SSL_CTX_use_PrivateKey_file",	{0}},
 	{"SSL_CTX_use_certificate_file",{0}},
-	{"SSL_CTX_free", {0}},
-	{"SSL_pending", {0}},
-	{"SSL_CTX_use_certificate_chain_file",{0}},
-	{"SSL_CTX_ctrl", {0}},
 	{NULL,				{0}}
 };
 
 void
-ssl_handshake(struct stream *stream)
+_shttpd_ssl_handshake(struct stream *stream)
 {
 	int	n;
 
-	if ((n = SSL_accept(stream->chan.ssl.ssl)) == 0) {
+	if ((n = SSL_accept(stream->chan.ssl.ssl)) == 1) {
+		DBG(("handshake: SSL accepted"));
+		stream->flags |= FLAG_SSL_ACCEPTED;
+	} else {
 		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;
+		DBG(("SSL_accept error %d", n));
 	}
 }
 
 static int
 read_ssl(struct stream *stream, void *buf, size_t len)
 {
-	int	nread = 0;
+	int	nread = -1;
 
 	assert(stream->chan.ssl.ssl != NULL);
 
 	if (!(stream->flags & FLAG_SSL_ACCEPTED))
-		ssl_handshake(stream);
+		_shttpd_ssl_handshake(stream);
 
 	if (stream->flags & FLAG_SSL_ACCEPTED)
 		nread = SSL_read(stream->chan.ssl.ssl, buf, len);
-	ERR_print_errors_fp(stderr);
 
 	return (nread);
 }
@@ -68,11 +63,8 @@
 static int
 write_ssl(struct stream *stream, const void *buf, size_t len)
 {
-	int nwrite = 0;
 	assert(stream->chan.ssl.ssl != NULL);
-	nwrite = SSL_write(stream->chan.ssl.ssl, buf, len);
-	ERR_print_errors_fp(stderr);
-	return nwrite;
+	return (SSL_write(stream->chan.ssl.ssl, buf, len));
 }
 
 static void
@@ -80,12 +72,11 @@
 {
 	assert(stream->chan.ssl.sock != -1);
 	assert(stream->chan.ssl.ssl != NULL);
-	shutdown(stream->chan.ssl.sock,SHUT_RDWR);
 	(void) closesocket(stream->chan.ssl.sock);
 	SSL_free(stream->chan.ssl.ssl);
 }
 
-const struct io_class	io_ssl =  {
+const struct io_class	_shttpd_io_ssl =  {
 	"ssl",
 	read_ssl,
 	write_ssl,
diff --git a/src/server/shttpd/log.c b/src/server/shttpd/log.c
index 422eb6d..8c506f4 100644
--- a/src/server/shttpd/log.c
+++ b/src/server/shttpd/log.c
@@ -8,13 +8,13 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 /*
  * Log function
  */
 void
-elog(int flags, struct conn *c, const char *fmt, ...)
+_shttpd_elog(int flags, struct conn *c, const char *fmt, ...)
 {
 	char	date[64], buf[URI_MAX];
 	int	len;
@@ -22,7 +22,7 @@
 	va_list	ap;
 
 	/* Print to stderr */
-	if (c == NULL || c->ctx->inetd_mode == 0) {
+	if (c == NULL || !IS_TRUE(c->ctx, OPT_INETD)) {
 		va_start(ap, fmt);
 		(void) vfprintf(stderr, fmt, ap);
 		(void) fputc('\n', stderr);
@@ -30,9 +30,9 @@
 	}
 
 	strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y",
-	    localtime(&current_time));
+	    localtime(&_shttpd_current_time));
 
-	len = snprintf(buf, sizeof(buf),
+	len = _shttpd_snprintf(buf, sizeof(buf),
 	    "[%s] [error] [client %s] \"%s\" ",
 	    date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
 	    c && c->request ? c->request : "-");
@@ -48,21 +48,12 @@
 		(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);
 }
-#if 0
+
 void
-log_access(FILE *fp, const struct conn *c)
+_shttpd_log_access(FILE *fp, const struct conn *c)
 {
 	static const struct vec	dash = {"-", 1};
 
@@ -85,12 +76,12 @@
 	}
 
 	(void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
-			localtime(&current_time));
+			localtime(&c->birth_time));
 
-	(void) snprintf(buf, sizeof(buf),
+	(void) _shttpd_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 : "-",
+	    date, _shttpd_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);
@@ -99,14 +90,4 @@
 		(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 */
 }
-#endif
diff --git a/src/server/shttpd/md5.c b/src/server/shttpd/md5.c
index 9a3df6c..4e9c470 100644
--- a/src/server/shttpd/md5.c
+++ b/src/server/shttpd/md5.c
@@ -15,7 +15,7 @@
  * will fill a supplied 16-byte array with the digest.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 #ifndef HAVE_MD5
 #if __BYTE_ORDER == 1234
@@ -201,7 +201,7 @@
 }
 
 /*
- * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
  * 1 0* (64-bit count of bits processed, MSB-first)
  */
 void
diff --git a/src/server/shttpd/mime_type.c b/src/server/shttpd/mime_type.c
deleted file mode 100644
index 31ce380..0000000
--- a/src/server/shttpd/mime_type.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file.  As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "shttpd_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] == '.' &&
-		    !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] == '.' &&
-		    !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/src/server/shttpd/shttpd.c b/src/server/shttpd/shttpd.c
index 4aedf62..6d8e518 100644
--- a/src/server/shttpd/shttpd.c
+++ b/src/server/shttpd/shttpd.c
@@ -10,39 +10,25 @@
 
 /*
  * Small and portable HTTP server, http://shttpd.sourceforge.net
- * $Id: shttpd.c,v 1.18 2008/01/10 11:01:21 drozd Exp $
+ * $Id: shttpd.c,v 1.57 2008/08/23 21:00:38 drozd Exp $
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
+#include "wsmand-daemon.h"
 
-/* from src/server/wsmand-daemon.h */
-extern int wsmand_options_get_use_ipv4(void);
-#ifdef ENABLE_IPV6
-extern int wsmand_options_get_use_ipv6(void);
-extern void wsmand_options_disable_use_ipv6(void);
-#endif
+time_t	_shttpd_current_time;	/* Current UTC time		*/
+int	_shttpd_tz_offset;	/* Time zone offset from UTC	*/
+int	_shttpd_exit_flag;	/* Program exit flag		*/
 
-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}, */
+const struct vec _shttpd_known_http_methods[] = {
+	{"GET",		3},
 	{"POST",	4},
-/*	{"PUT",		3},
+	{"PUT",		3},
 	{"DELETE",	6},
-	{"HEAD",	4}, */
+	{"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.
@@ -68,7 +54,56 @@
 static void process_connection(struct conn *, int, int);
 
 int
-url_decode(const char *src, int src_len, char *dst, int dst_len)
+_shttpd_is_true(const char *str)
+{
+	static const char	*trues[] = {"1", "yes", "true", "jawohl", NULL};
+	const char		**p;
+
+	for (p = trues; *p != NULL; p++)
+		if (str && !strcmp(str, *p))
+			return (TRUE);
+
+	return (FALSE);
+}
+
+static void
+free_list(struct llhead *head, void (*dtor)(struct llhead *))
+{
+	struct llhead	*lp, *tmp;
+
+	LL_FOREACH_SAFE(head, lp, tmp) {
+		LL_DEL(lp);
+		dtor(lp);
+	}
+}
+
+static void
+listener_destructor(struct llhead *lp)
+{
+	struct listener	*listener = LL_ENTRY(lp, struct listener, link);
+
+	(void) closesocket(listener->sock);
+	free(listener);
+}
+
+static void
+registered_uri_destructor(struct llhead *lp)
+{
+	struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
+
+	free((void *) ruri->uri);
+	free(ruri);
+}
+
+static void
+acl_destructor(struct llhead *lp)
+{
+	struct acl	*acl = LL_ENTRY(lp, struct acl, link);
+	free(acl);
+}
+
+int
+_shttpd_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')
@@ -96,37 +131,21 @@
 	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= strdup(ext)) == NULL) {
-		elog(E_FATAL, 0, error_msg);
-	} else if ((e->mime = 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;
+	const char	*p, *s = ctx->options[OPT_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) {
+
+		if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
+			continue;
+
+		if (memcmp(uri, s, p - s) == 0) {
 			a_uri->ptr = s;
 			a_uri->len = p - s;
 			a_path->ptr = ++p;
@@ -139,7 +158,7 @@
 }
 
 void
-stop_stream(struct stream *stream)
+_shttpd_stop_stream(struct stream *stream)
 {
 	if (stream->io_class != NULL && stream->io_class->close != NULL)
 		stream->io_class->close(stream);
@@ -151,54 +170,30 @@
 	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)));
+	    (unsigned long) stream->io.total, (int) io_data_len(&stream->io)));
 }
 
 /*
  * Setup listening socket on given port, return socket
  */
 static int
-open_listening_port(int port)
+shttpd_open_listening_port(int port)
 {
-	int		sock = -1, on = 1;
+	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);
 
-
-#ifdef ENABLE_IPV6
-	sa.len                          = sizeof(sa.u.sin6);
-        memset(&sa.u.sin6, 0, sa.len);
-	sa.u.sin6.sin6_family		= AF_INET6;
-	sa.u.sin6.sin6_addr		= in6addr_any;
-	sa.u.sin6.sin6_port             = htons((uint16_t) port);
-	sa.u.sin6.sin6_flowinfo         = 0;
-	sa.u.sin6.sin6_scope_id         = 0;
-
-	if (!wsmand_options_get_use_ipv6()
-	    || (sock = socket(AF_INET6, SOCK_STREAM, 6)) == -1) {
-		wsmand_options_disable_use_ipv6();
-		if (wsmand_options_get_use_ipv4()) {
-#endif	
-			sa.len                         = sizeof(sa.u.sin);
-			memset(&sa.u.sin, 0, sa.len);
-			sa.u.sin.sin_family            = AF_INET;
-			sa.u.sin.sin_addr.s_addr       = htonl(INADDR_ANY);	
-			sa.u.sin.sin_port              = htons((uint16_t) port);
-
-			if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
-				goto fail;
-#ifdef ENABLE_IPV6
-		}
-		else
-			goto fail;
-	}
-#endif	
-
-	if (set_non_blocking_mode(sock) != 0)
+	if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
+		goto fail;
+	if (_shttpd_set_non_blocking_mode(sock) != 0)
 		goto fail;
 	if (setsockopt(sock, SOL_SOCKET,
 	    SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
@@ -216,7 +211,7 @@
 fail:
 	if (sock != -1)
 		(void) closesocket(sock);
-	elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
+	_shttpd_elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
 	return (-1);
 }
 
@@ -224,7 +219,7 @@
  * Check whether full request is buffered Return headers length, or 0
  */
 int
-get_headers_len(const char *buf, size_t buflen)
+_shttpd_get_headers_len(const char *buf, size_t buflen)
 {
 	const char	*s, *e;
 	int		len = 0;
@@ -247,9 +242,8 @@
  * Send error message back to a client.
  */
 void
-send_server_error(struct conn *c, int status, const char *reason)
+_shttpd_send_server_error(struct conn *c, int status, const char *reason)
 {
-#ifdef EMBEDDED
 	struct llhead		*lp;
 	struct error_handler	*e;
 
@@ -261,18 +255,23 @@
 			    c->loc.io_class->close != NULL)
 				c->loc.io_class->close(&c->loc);
 			io_clear(&c->loc.io);
-			setup_embedded_stream(c, e->callback, e->callback_data);
+			_shttpd_setup_embedded_stream(c,
+			    e->callback, e->callback_data);
 			return;
 		}
 	}
-#endif /* EMBEDDED */
 
 	io_clear(&c->loc.io);
-	c->loc.headers_len = c->loc.io.head = snprintf(c->loc.io.buf,
-	    c->loc.io.size, "HTTP/1.1 %d %s\r\nConnection: Close\r\n\r\n%d %s",
-	    status, reason, status, reason);
+	c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
+	    "HTTP/1.1 %d %s\r\n"
+	    "Content-Type: text/plain\r\n"
+	    "Content-Length: 12\r\n"
+	    "\r\n"
+	    "Error: %03d\r\n",
+	    status, reason, status);
+	c->loc.content_len = 10;
 	c->status = status;
-	stop_stream(&c->loc);
+	_shttpd_stop_stream(&c->loc);
 }
 
 /*
@@ -330,7 +329,7 @@
 		tm.tm_year += 100;
 
 	/* Set Daylight Saving Time field */
-	tmp = localtime(&current_time);
+	tmp = localtime(&_shttpd_current_time);
 	tm.tm_isdst = tmp->tm_isdst;
 
 	return (mktime(&tm));
@@ -351,7 +350,7 @@
 }
 
 void
-parse_headers(const char *s, int len, struct headers *parsed)
+_shttpd_parse_headers(const char *s, int len, struct headers *parsed)
 {
 	const struct http_header	*h;
 	union variant			*v;
@@ -368,7 +367,7 @@
 		/* Is this header known to us ? */
 		for (h = http_headers; h->len != 0; h++)
 			if (e - s > h->len &&
-			    !strncasecmp(s, h->name, h->len))
+			    !_shttpd_strncasecmp(s, h->name, h->len))
 				break;
 
 		/* If the header is known to us, store its value */
@@ -397,6 +396,86 @@
 	}
 }
 
+static const struct {
+	const char	*extension;
+	int		ext_len;
+	const char	*mime_type;
+} builtin_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"		},
+	{"swf",		3,	"application/x-shockwave-flash"	},
+	{"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				}
+};
+
+void
+_shttpd_get_mime_type(struct shttpd_ctx *ctx,
+		const char *uri, int len, struct vec *vec)
+{
+	const char	*eq, *p = ctx->options[OPT_MIME_TYPES];
+	int		i, n, ext_len;
+
+	/* Firt, loop through the custom mime types if any */
+	FOR_EACH_WORD_IN_LIST(p, n) {
+		if ((eq = memchr(p, '=', n)) == NULL || eq >= p + n || eq == p)
+			continue;
+		ext_len = eq - p;
+		if (len > ext_len && uri[len - ext_len - 1] == '.' &&
+		    !_shttpd_strncasecmp(p, &uri[len - ext_len], ext_len)) {
+			vec->ptr = eq + 1;
+			vec->len = p + n - vec->ptr;
+			return;
+		}
+	}
+
+	/* If no luck, try built-in mime types */
+	for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
+		ext_len = builtin_mime_types[i].ext_len;
+		if (len > ext_len && uri[len - ext_len - 1] == '.' &&
+		    !_shttpd_strncasecmp(builtin_mime_types[i].extension,
+			    &uri[len - ext_len], ext_len)) {
+			vec->ptr = builtin_mime_types[i].mime_type;
+			vec->len = strlen(vec->ptr);
+			return;
+		}
+	}
+
+	/* Oops. This extension is unknown to us. Fallback to text/plain */
+	vec->ptr = "text/plain";
+	vec->len = strlen(vec->ptr);
+}
+
 /*
  * For given directory path, substitute it to valid index file.
  * Return 0 if index file has been found, -1 if not found
@@ -405,14 +484,15 @@
 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;
-	int		len;
+	const char	*s = c->ctx->options[OPT_INDEX_FILES];
+	size_t		len;
 
 	FOR_EACH_WORD_IN_LIST(s, len) {
-		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);
+		/* path must end with '/' character */
+		_shttpd_snprintf(buf, sizeof(buf), "%s%.*s", path, len, s);
+		if (_shttpd_stat(buf, stp) == 0) {
+			_shttpd_strlcpy(path, buf, maxpath);
+			_shttpd_get_mime_type(c->ctx, s, len, &c->mime_type);
 			return (0);
 		}
 	}
@@ -430,17 +510,17 @@
 {
 	char	*p, *e;
 
-	if (my_stat(path, stp) == 0)
+	if (_shttpd_stat(path, stp) == 0)
 		return (0);
 
 	p = path + strlen(path);
-	e = path + strlen(c->ctx->document_root) + 2;
+	e = path + strlen(c->ctx->options[OPT_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)) {
+			if (!_shttpd_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
 				c->path_info = p + 1;
 				return (0);
 			} else {
@@ -451,139 +531,130 @@
 	return (-1);
 }
 
-
 static void
 decide_what_to_do(struct conn *c)
 {
-	char		path[URI_MAX], buf[1024];
+	char		path[URI_MAX], buf[1024], *root;
 	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);
+	_shttpd_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");
+	root = c->ctx->options[OPT_ROOT];
+	if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
+		_shttpd_send_server_error(c, 400, "URI is too long");
 		return;
 	}
 
-	(void) snprintf(path, sizeof(path), "%s%s",
-	    c->ctx->document_root, c->uri);
+	(void) _shttpd_snprintf(path, sizeof(path), "%s%s", 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) snprintf(path, sizeof(path), "%.*s%s",
+		(void) _shttpd_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)
-	rc = check_authorization(c, path);
-        if (rc != 1) {
-                if  (rc != 2) { /* 2 = multipass auth (GSS)*/
-		        if (send_authorization_request(c)) {
-                                fprintf(stderr, "Digest realm overflows buffer\n");
-                                return;
-                        }
-                }
+	if (_shttpd_check_authorization(c, path) != 1) {
+		_shttpd_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);
+	if ((ruri = _shttpd_is_registered_uri(c->ctx, c->uri)) != NULL) {
+		_shttpd_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");
+		_shttpd_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))) {
-		if (send_authorization_request(c)) {
-                        fprintf(stderr, "Digest realm overflows buffer\n");
-                        return;
-                }
+	    (c->ctx->options[OPT_AUTH_PUT] == NULL ||
+	     !_shttpd_is_authorized_for_put(c))) {
+		_shttpd_send_authorization_request(c);
 	} else
 #endif /* NO_AUTH */
 	if (c->method == METHOD_PUT) {
-		c->status = my_stat(path, &st) == 0 ? 200 : 201;
+		c->status = _shttpd_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");
+			_shttpd_send_server_error(c, 501,
+			    "PUT Range Not Implemented");
+		} else if ((rc = _shttpd_put_dir(path)) == 0) {
+			_shttpd_send_server_error(c, 200, "OK");
 		} else if (rc == -1) {
-			send_server_error(c, 500, "PUT Directory Error");
+			_shttpd_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 |
+			_shttpd_send_server_error(c, 411, "Length Required");
+		} else if ((c->loc.chan.fd = _shttpd_open(path, O_WRONLY | O_BINARY |
 		    O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
-			send_server_error(c, 500, "PUT Error");
+			_shttpd_send_server_error(c, 500, "PUT Error");
 		} else {
 			DBG(("PUT file [%s]", c->uri));
-			c->loc.io_class = &io_file;
+			c->loc.io_class = &_shttpd_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");
+		if (_shttpd_remove(path) == 0)
+			_shttpd_send_server_error(c, 200, "OK");
 		else
-			send_server_error(c, 500, "DELETE Error");
+			_shttpd_send_server_error(c, 500, "DELETE Error");
 	} else if (get_path_info(c, path, &st) != 0) {
-		send_server_error(c, 404, "Not Found");
+		_shttpd_send_server_error(c, 404, "Not Found");
 	} else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
-		(void) snprintf(buf, sizeof(buf),
+		(void) _shttpd_snprintf(buf, sizeof(buf),
 			"Moved Permanently\r\nLocation: %s/", c->uri);
-		send_server_error(c, 301, buf);
+		_shttpd_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 = strdup(path)) != NULL)
-			get_dir(c);
+	    !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
+		_shttpd_send_server_error(c, 403, "Directory Listing Denied");
+	} else if (S_ISDIR(st.st_mode) && IS_TRUE(c->ctx, OPT_DIR_LIST)) {
+		if ((c->loc.chan.dir.path = _shttpd_strdup(path)) != NULL)
+			_shttpd_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");
+			_shttpd_send_server_error(c, 500, "GET Directory Error");
+	} else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
+		_shttpd_send_server_error(c, 403, "Directory listing denied");
 #if !defined(NO_CGI)
-	} else if (match_extension(path, c->ctx->cgi_extensions)) {
+	} else if (_shttpd_match_extension(path,
+	    c->ctx->options[OPT_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");
+			_shttpd_send_server_error(c, 501, "Bad method ");
+		} else if ((_shttpd_run_cgi(c, path)) == -1) {
+			_shttpd_send_server_error(c, 500, "Cannot exec CGI");
 		} else {
-			do_cgi(c);
+			_shttpd_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,
+	} else if (_shttpd_match_extension(path,
+	    c->ctx->options[OPT_SSI_EXTENSIONS])) {
+		if ((c->loc.chan.fd = _shttpd_open(path,
 		    O_RDONLY | O_BINARY, 0644)) == -1) {
-			send_server_error(c, 500, "SSI open error");
+			_shttpd_send_server_error(c, 500, "SSI open error");
 		} else {
-			do_ssi(c);
+			_shttpd_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,
+		_shttpd_send_server_error(c, 304, "Not Modified");
+	} else if ((c->loc.chan.fd = _shttpd_open(path,
 	    O_RDONLY | O_BINARY, 0644)) != -1) {
-		get_file(c, &st);
+		_shttpd_get_file(c, &st);
 	} else {
-		send_server_error(c, 500, "Internal Error");
+		_shttpd_send_server_error(c, 500, "Internal Error");
 	}
 }
 
@@ -592,12 +663,10 @@
 {
 	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++)
+	for (v = _shttpd_known_http_methods; v->ptr != NULL; v++)
 		if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
-			c->method = v - known_http_methods;
+			c->method = v - _shttpd_known_http_methods;
 			break;
 		}
 
@@ -608,28 +677,32 @@
 parse_http_request(struct conn *c)
 {
 	char	*s, *e, *p, *start;
-	char	*end_number;
-	int	uri_len, req_len;
+	int	uri_len, req_len, n;
 
 	s = io_data(&c->rem.io);;
 	req_len = c->rem.headers_len =
-	    get_headers_len(s, io_data_len(&c->rem.io));
+	    _shttpd_get_headers_len(s, io_data_len(&c->rem.io));
 
-	if (req_len == 0 && io_space_len(&c->rem.io) == 0)
-		send_server_error(c, 400, "Request is too big");
+	if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
+		io_clear(&c->rem.io);
+		_shttpd_send_server_error(c, 400, "Request is too big");
+	}
 
-	if (req_len == 0)
+	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 = u_strndup(s, req_len)) == NULL)
-		send_server_error(c, 500, "Cannot allocate request");
+	} else if (req_len < 16) {	/* Minimal: "GET / HTTP/1.0\n\n" */
+		_shttpd_send_server_error(c, 400, "Bad request");
+	} else if (set_request_method(c)) {
+		_shttpd_send_server_error(c, 501, "Method Not Implemented");
+	} else if ((c->request = _shttpd_strndup(s, req_len)) == NULL) {
+		_shttpd_send_server_error(c, 500, "Cannot allocate request");
+	}
 
 	if (c->loc.flags & FLAG_CLOSED)
 		return;
 
+	io_inc_tail(&c->rem.io, req_len);
+
 	DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
 	c->rem.flags |= FLAG_HEADERS_PARSED;
 
@@ -647,338 +720,269 @@
 	 * 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++;
+	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++;
+	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 ||
+	if (sscanf(p, "HTTP/%lu.%lu%n",
+	    &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
+		_shttpd_send_server_error(c, 400, "Bad HTTP version");
+	} else 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");
+		_shttpd_send_server_error(c, 505, "HTTP version not supported");
+	} else if (uri_len <= 0) {
+		_shttpd_send_server_error(c, 400, "Bad URI");
 	} else if ((c->uri = malloc(uri_len + 1)) == NULL) {
-		send_server_error(c, 500, "Cannot allocate URI");
+		_shttpd_send_server_error(c, 500, "Cannot allocate URI");
 	} else {
-		my_strlcpy(c->uri, (char *) start, uri_len + 1);
-		parse_headers(c->headers,
+		_shttpd_strlcpy(c->uri, (char *) start, uri_len + 1);
+		_shttpd_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, int is_ssl)
+static void
+add_socket(struct worker *worker, int sock, int is_ssl)
 {
-	struct conn	*c;
-	struct usa	sa;
-	int		l = ctx->inetd_mode ? E_FATAL : E_LOG;
+	struct shttpd_ctx	*ctx = worker->ctx;
+	struct conn		*c;
+	struct usa		sa;
+	int			l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
 #if !defined(NO_SSL)
 	SSL		*ssl = NULL;
+#else
+	is_ssl = is_ssl;	/* supress warnings */
 #endif /* NO_SSL */
 
 	sa.len = sizeof(sa.u.sin);
-	(void) set_non_blocking_mode(sock);
+	(void) _shttpd_set_non_blocking_mode(sock);
 
 	if (getpeername(sock, &sa.u.sa, &sa.len)) {
-		elog(l, NULL, "add_socket: %s", strerror(errno));
+		_shttpd_elog(l, NULL, "add_socket: %s", strerror(errno));
 #if !defined(NO_SSL)
 	} else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
-		elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
+		_shttpd_elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
 		(void) closesocket(sock);
 	} else if (is_ssl && SSL_set_fd(ssl, sock) == 0) {
-		elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
+		_shttpd_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) {
+	} else if ((c = calloc(1, sizeof(*c) + 2 * URI_MAX)) == 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));
+		_shttpd_elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
 	} else {
-		ctx->nrequests++;
-		c->rem.conn = c->loc.conn = c;
+		c->rem.conn	= c->loc.conn = c;
 		c->ctx		= ctx;
+		c->worker	= worker;
 		c->sa		= sa;
-		c->birth_time	= current_time;
-		c->expire_time	= current_time + EXPIRE_TIME;
+		c->birth_time	= _shttpd_current_time;
+		c->expire_time	= _shttpd_current_time + EXPIRE_TIME;
 
 		(void) getsockname(sock, &sa.u.sa, &sa.len);
-#ifdef ENABLE_IPV6	
-		if (wsmand_options_get_use_ipv6()) {
-			c->loc_port = sa.u.sin6.sin6_port;
-		}
-		else {
-#endif
-			c->loc_port = sa.u.sin.sin_port;
-#ifdef ENABLE_IPV6
-		}
-#endif
+		c->loc_port = sa.u.sin.sin_port;
 
-		set_close_on_exec(sock);
+		_shttpd_set_close_on_exec(sock);
 
 		c->loc.io_class	= NULL;
 
-		c->rem.io_class	= &io_socket;
+		c->rem.io_class	= &_shttpd_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;
-#ifdef SHTTPD_GSS
-                c->gss_ctx = GSS_C_NO_CONTEXT;
-#endif
+		c->rem.io.buf	= c->loc.io.buf + URI_MAX;
+		c->loc.io.size	= c->rem.io.size = URI_MAX;
+
 #if !defined(NO_SSL)
 		if (is_ssl) {
-			c->rem.io_class	= &io_ssl;
+			c->rem.io_class	= &_shttpd_io_ssl;
 			c->rem.chan.ssl.sock = sock;
 			c->rem.chan.ssl.ssl = ssl;
-			ssl_handshake(&c->rem);
+			_shttpd_ssl_handshake(&c->rem);
 		}
 #endif /* NO_SSL */
 
-		EnterCriticalSection(&ctx->mutex);
-		LL_TAIL(&ctx->connections, &c->link);
-		ctx->nactive++;
-		LeaveCriticalSection(&ctx->mutex);
-#ifdef ENABLE_IPV6
-		if (wsmand_options_get_use_ipv6()) {
-			char str[INET6_ADDRSTRLEN];
-			inet_ntop( AF_INET6,&sa.u.sin6.sin6_addr, str, sizeof(str));
-			DBG(("%s:%hu connected IPv6 (socket %d)",  str , ntohs(sa.u.sin6.sin6_port), sock));
-		}
-		else {
-#endif
-			DBG(("%s:%hu connected IPv4 (socket %d)",
-			     inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
-			     ntohs(sa.u.sin.sin_port), sock));
-#ifdef ENABLE_IPV6
-		}
-#endif
+		LL_TAIL(&worker->connections, &c->link);
+		worker->num_conns++;
+
+		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)
+static struct worker *
+first_worker(struct shttpd_ctx *ctx)
 {
-	return (ctx->nactive);
+	return (LL_ENTRY(ctx->workers.next, struct worker, link));
 }
 
-/*
- * Setup a listening socket on given port. Return opened socket or -1
- */
-int
-shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl)
+static void
+pass_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
 {
+	struct llhead	*lp;
+	struct worker	*worker, *lazy;
+	int		buf[3];
+
+	lazy = first_worker(ctx);
+
+	/* Find least busy worker */
+	LL_FOREACH(&ctx->workers, lp) {
+		worker = LL_ENTRY(lp, struct worker, link);
+		if (worker->num_conns < lazy->num_conns)
+			lazy = worker;
+	}
+
+	buf[0] = CTL_PASS_SOCKET;
+	buf[1] = sock;
+	buf[2] = is_ssl;
+
+	(void) send(lazy->ctl[1], (void *) buf, sizeof(buf), 0);
+}
+
+static int
+set_ports(struct shttpd_ctx *ctx, const char *p)
+{
+	int		sock, len, is_ssl, port;
 	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));
+
+	free_list(&ctx->listeners, &listener_destructor);
+
+	FOR_EACH_WORD_IN_LIST(p, len) {
+
+		is_ssl	= p[len - 1] == 's' ? 1 : 0;
+		port	= atoi(p);
+
+		if ((sock = shttpd_open_listening_port(port)) == -1) {
+			_shttpd_elog(E_LOG, NULL, "cannot open port %d", port);
+			goto fail;
+		} else if (is_ssl && ctx->ssl_ctx == NULL) {
+			(void) closesocket(sock);
+			_shttpd_elog(E_LOG, NULL, "cannot add SSL socket, "
+			    "please specify certificate file");
+			goto fail;
+		} else if ((l = calloc(1, sizeof(*l))) == NULL) {
+			(void) closesocket(sock);
+			_shttpd_elog(E_LOG, NULL, "cannot allocate listener");
+			goto fail;
+		} else {
+			l->is_ssl = is_ssl;
+			l->sock	= sock;
+			l->ctx	= ctx;
+			LL_TAIL(&ctx->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);
+	return (TRUE);
+fail:
+	free_list(&ctx->listeners, &listener_destructor);
+	return (FALSE);
 }
 
 static void
 read_stream(struct stream *stream)
 {
-        int     n, len;
-        int sslerr = 0;
-        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;
+	int	n, len;
 
-        /* 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);
-                stream->flags &= ~FLAG_SSL_SHOULD_SELECT_ON_WRITE;
-        }
-        else if (n == -1) {
-		  if(stream->chan.ssl.ssl) {
-                	sslerr = SSL_get_error(stream->chan.ssl.ssl, n);
-		  }
-                /* Ignore SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE*/
-                if((stream->chan.ssl.ssl && sslerr == SSL_ERROR_SYSCALL &&
-					(ERRNO == EINTR || ERRNO == EWOULDBLOCK)) ||
-					(ERRNO == EINTR || ERRNO == EWOULDBLOCK)) {
-                                n = n; /* Ignore EINTR and EAGAIN */
-                }
-                else if(sslerr == SSL_ERROR_WANT_READ) {
-                        n = n;
-                }
-                else if(sslerr == SSL_ERROR_WANT_WRITE) {
-                        stream->flags |= FLAG_SSL_SHOULD_SELECT_ON_WRITE;
-                }
-                else if (!(stream->flags & FLAG_DONT_CLOSE))
-                        stop_stream(stream);
+	len = io_space_len(&stream->io);
+	assert(len > 0);
 
-        }
-        else if (!(stream->flags & FLAG_DONT_CLOSE))
-                stop_stream(stream);
-#if 0
-        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));
-#endif
+	/* 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;
 
-        /*
-         * 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);
-        }
+	/* Read from underlying channel */
+	assert(stream->io_class != NULL);
+	n = 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))
+		_shttpd_stop_stream(stream);
 
-	stream->conn->expire_time = current_time + EXPIRE_TIME;
+	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)
+			_shttpd_stop_stream(stream);
+	}
+
+	stream->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
 }
 
 static void
 write_stream(struct stream *from, struct stream *to)
 {
-        int     n, len;
-        int sslerr = 0;
-        len = io_data_len(&from->io);
-        assert(len > 0);
+	int	n, len;
 
-        /* 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));
-                */
+	len = io_data_len(&from->io);
+	assert(len > 0);
 
-        if (n > 0) {
-                io_inc_tail(&from->io, n);
-                to->flags &= ~FLAG_SSL_SHOULD_SELECT_ON_READ;
-        }
-        else if (n == -1) {
-		  if(to->chan.ssl.ssl) {
-                	sslerr = SSL_get_error(to->chan.ssl.ssl, n);
-		  }
-                /* Ignore SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE*/
-                if((to->chan.ssl.ssl && sslerr == SSL_ERROR_SYSCALL &&
-					(ERRNO == EINTR || ERRNO == EWOULDBLOCK)) ||
-					(ERRNO == EINTR || ERRNO == EWOULDBLOCK)) {
-                                n = n; /* Ignore EINTR and EAGAIN */
-                }
-                else if(sslerr == SSL_ERROR_WANT_WRITE) {
-                        n = n;
-                }
-                else if(sslerr == SSL_ERROR_WANT_READ) {
-                        to->flags |= FLAG_SSL_SHOULD_SELECT_ON_READ;
-                }
+	/* TODO: should be assert on CAN_WRITE flag */
+	n = to->io_class->write(to, io_data(&from->io), len);
+	to->conn->expire_time = _shttpd_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));
 
-                else if (!(to->flags & FLAG_DONT_CLOSE))
-                        stop_stream(to);
-        }
-        else if (!(to->flags & FLAG_DONT_CLOSE))
-                stop_stream(to);
+	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))
+		_shttpd_stop_stream(to);
 }
 
 
-
 static void
-disconnect(struct llhead *lp)
+connection_desctructor(struct llhead *lp)
 {
 	struct conn		*c = LL_ENTRY(lp, struct conn, link);
-	static const struct vec	ka = {"keep-alive", 10};
-	int			dont_close;
+	static const struct vec	vec = {"close", 5};
+	int			do_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);
+	if (c->request != NULL && c->ctx->access_log != NULL)
+		_shttpd_log_access(c->ctx->access_log, c);
 
 	/* In inetd mode, exit if request is finished. */
-	if (c->ctx->inetd_mode)
+	if (IS_TRUE(c->ctx, OPT_INETD))
 		exit(0);
 
 	if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
@@ -988,45 +992,50 @@
 	 * 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 &&
-	    !strncasecmp(ka.ptr, c->ch.connection.v_vec.ptr, ka.len);
-	dont_close = 0;
+	do_close = (c->ch.connection.v_vec.len >= vec.len &&
+	    !_shttpd_strncasecmp(vec.ptr,c->ch.connection.v_vec.ptr,vec.len)) ||
+	    (c->major_version < 1 ||
+	    (c->major_version >= 1 && c->minor_version < 1));
+
 	if (c->request)
 		free(c->request);
 	if (c->uri)
 		free(c->uri);
 
-	/* Handle Keep-Alive */
-	if (dont_close) {
+	/* Keep the connection open only if we have Content-Length set */
+	if (!do_close && c->loc.content_len > 0) {
 		c->loc.io_class = NULL;
 		c->loc.flags = 0;
-		c->rem.flags = FLAG_W | FLAG_R;
+		c->loc.content_len = 0;
+		c->rem.flags = FLAG_W | FLAG_R | FLAG_SSL_ACCEPTED;
 		c->query = c->request = c->uri = c->path_info = NULL;
-		c->mime_type = NULL;
+		c->mime_type.len = 0;
 		(void) memset(&c->ch, 0, sizeof(c->ch));
 		io_clear(&c->loc.io);
+		c->birth_time = _shttpd_current_time;
 		if (io_data_len(&c->rem.io) > 0)
 			process_connection(c, 0, 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);
- #ifdef SHTTPD_GSS
-                 {
-                     OM_uint32 majStat, minStat;
-                     majStat = gss_delete_sec_context(&minStat, c->gss_ctx, 0);
-                 }
- #endif
+		c->worker->num_conns--;
+		assert(c->worker->num_conns >= 0);
 
 		free(c);
 	}
 }
 
+static void
+worker_destructor(struct llhead *lp)
+{
+	struct worker	*worker = LL_ENTRY(lp, struct worker, link);
+
+	free_list(&worker->connections, connection_desctructor);
+	free(worker);
+}
+
 static int
 is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
 {
@@ -1037,10 +1046,6 @@
 
 	LL_FOREACH(&ctx->acl, lp) {
 		acl = LL_ENTRY(lp, struct acl, link);
-#ifdef ENABLE_IPV6
-		if (wsmand_options_get_use_ipv6())
-			return allowed;
-#endif
 		(void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
 		if (acl->ip == (ntohl(ip) & acl->mask))
 			allowed = acl->flag;
@@ -1060,23 +1065,19 @@
 static void
 process_connection(struct conn *c, int remote_ready, int local_ready)
 {
-#if 0
-		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)));
-		DBG(("locf=%x,remf=%x", c->loc.flags,c->rem.flags));
-#endif
-
 	/* Read from remote end if it is ready */
-		if(c->loc.flags & FLAG_RESPONSE_COMPLETE)
-				c->rem.flags &= ~ FLAG_HEADERS_PARSED;
 	if (remote_ready && 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: %d [%.*s]", (int) io_data_len(&c->loc.io),
+	    (int) io_data_len(&c->loc.io), io_data(&c->loc.io)));
+	DBG(("rem: %d [%.*s]", (int) io_data_len(&c->rem.io),
+	    (int) io_data_len(&c->rem.io), io_data(&c->rem.io)));
+
 	/* Read from the local end if it is ready */
 	if (local_ready && io_space_len(&c->loc.io))
 		read_stream(&c->loc);
@@ -1088,100 +1089,53 @@
 	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) ||
+	if ((_shttpd_current_time > c->expire_time) ||
 	    (c->rem.flags & FLAG_CLOSED) ||
 	    ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
-		disconnect(&c->link);
+		connection_desctructor(&c->link);
 }
 
-/*
- * One iteration of server loop. This is the core of the data exchange.
- */
-void
-shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
+static int
+num_workers(const struct shttpd_ctx *ctx)
 {
-	struct llhead	*lp, *tmp;
-	struct listener	*l;
-	struct conn	*c = NULL;
-	struct timeval	tv;			/* Timeout for select() */
-	fd_set		read_set, write_set;
-	int		sock, max_fd = -1, msec = milliseconds;
-	struct usa	sa;
+	char	*p = ctx->options[OPT_THREADS];
+	return (p ? atoi(p) : 1);
+}
 
-	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));
+static void
+handle_connected_socket(struct shttpd_ctx *ctx,
+		struct usa *sap, int sock, int is_ssl)
+{
+#if !defined(_WIN32)
+	if (sock >= (int) FD_SETSIZE) {
+		_shttpd_elog(E_LOG, NULL, "ctx %p: discarding "
+		    "socket %d, too busy", ctx, sock);
+		(void) closesocket(sock);
+	} else
+#endif /* !_WIN32 */
+		if (!is_allowed(ctx, sap)) {
+		_shttpd_elog(E_LOG, NULL, "%s is not allowed to connect",
+		    inet_ntoa(sap->u.sin.sin_addr));
+		(void) closesocket(sock);
+	} else if (num_workers(ctx) > 1) {
+		pass_socket(ctx, sock, is_ssl);
+	} else {
+		add_socket(first_worker(ctx), sock, is_ssl);
 	}
+}
 
-	/* Multiplex streams */
-	LL_FOREACH(&ctx->connections, lp) {
-		c = LL_ENTRY(lp, struct conn, link);
+static int
+do_select(int max_fd, fd_set *read_set, fd_set *write_set, int milliseconds)
+{
+	struct timeval	tv;
+	int		n;
 
-		/* If there is a space in remote IO, check remote socket */
-		if (c->rem.flags & 	FLAG_SSL_SHOULD_SELECT_ON_READ)
-			add_to_set(c->rem.chan.fd, &read_set, &max_fd);
-		else if (io_space_len(&c->rem.io) && c->rem.flags &
-			FLAG_SSL_SHOULD_SELECT_ON_WRITE)
-			add_to_set(c->rem.chan.fd, &write_set, &max_fd);
-		else 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) && (c->rem.flags &
-			FLAG_SSL_SHOULD_SELECT_ON_READ))
-			add_to_set(c->rem.chan.fd, &read_set, &max_fd);
-		else 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;
+	tv.tv_sec = milliseconds / 1000;
+	tv.tv_usec = (milliseconds % 1000) * 1000;
 
 	/* Check IO readiness */
-	if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
+	if ((n = select(max_fd + 1, read_set, write_set, NULL, &tv)) < 0) {
 #ifdef _WIN32
 		/*
 		 * On windows, if read_set and write_set are empty,
@@ -1192,117 +1146,168 @@
 		Sleep(milliseconds);
 #endif /* _WIN32 */
 		DBG(("select: %d", ERRNO));
-		if(c->rem.chan.ssl.ssl == NULL)
-			return;
-		else if(!SSL_pending(c->rem.chan.ssl.ssl))
-			return;
 	}
 
+	return (n);
+}
+
+static int
+multiplex_worker_sockets(const struct worker *worker, int *max_fd,
+		fd_set *read_set, fd_set *write_set)
+{
+	struct llhead	*lp;
+	struct conn	*c;
+	int		nowait = FALSE;
+
+	/* Add control socket */
+	add_to_set(worker->ctl[0], read_set, max_fd);
+
+	/* Multiplex streams */
+	LL_FOREACH(&worker->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 == &_shttpd_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 == &_shttpd_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) && !(c->loc.flags & FLAG_SUSPEND))
+			add_to_set(c->rem.chan.fd, write_set, max_fd);
+
+		/*
+		 * Set select wait interval to zero if FLAG_ALWAYS_READY set
+		 */
+		if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
+		    (c->loc.flags & FLAG_ALWAYS_READY))
+			nowait = TRUE;
+
+		if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
+		    (c->loc.flags & FLAG_ALWAYS_READY))
+			nowait = TRUE;
+	}
+
+	return (nowait);
+}
+
+int
+shttpd_join(struct shttpd_ctx *ctx,
+		fd_set *read_set, fd_set *write_set, int *max_fd)
+{
+	struct llhead	*lp;
+	struct listener	*l;
+	int		nowait = FALSE;
+
+	/* Add listening sockets to the read set */
+	LL_FOREACH(&ctx->listeners, lp) {
+		l = LL_ENTRY(lp, struct listener, link);
+		add_to_set(l->sock, read_set, max_fd);
+		DBG(("FD_SET(%d) (listening)", l->sock));
+	}
+
+	if (num_workers(ctx) == 1)
+		nowait = multiplex_worker_sockets(first_worker(ctx), max_fd,
+		    read_set, write_set);
+
+	return (nowait);
+}
+
+
+static void
+process_worker_sockets(struct worker *worker, fd_set *read_set)
+{
+	struct llhead	*lp, *tmp;
+	int		cmd, skt[2], sock = worker->ctl[0];
+	struct conn	*c;
+
+	/* Check if new socket is passed to us over the control socket */
+	if (FD_ISSET(worker->ctl[0], read_set))
+		while (recv(sock, (void *) &cmd, sizeof(cmd), 0) == sizeof(cmd))
+			switch (cmd) {
+			case CTL_PASS_SOCKET:
+				(void)recv(sock, (void *) &skt, sizeof(skt), 0);
+				add_socket(worker, skt[0], skt[1]);
+				break;
+			case CTL_WAKEUP:
+				(void)recv(sock, (void *) &c, sizeof(c), 0);
+				c->loc.flags &= FLAG_SUSPEND;
+				break;
+			default:
+				_shttpd_elog(E_FATAL, NULL, "ctx %p: ctl cmd %d",
+				    worker->ctx, cmd);
+				break;
+			}
+
+	/* Process all connections */
+	LL_FOREACH_SAFE(&worker->connections, lp, tmp) {
+		c = LL_ENTRY(lp, struct conn, link);
+		process_connection(c, FD_ISSET(c->rem.chan.sock, read_set),
+		    c->loc.io_class != NULL &&
+		    ((c->loc.flags & FLAG_ALWAYS_READY)
+#if !defined(NO_CGI)
+		    || (c->loc.io_class == &_shttpd_io_cgi &&
+		     FD_ISSET(c->loc.chan.fd, read_set))
+#endif /* NO_CGI */
+		    ));
+	}
+}
+
+/*
+ * 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;
+	struct listener	*l;
+	fd_set		read_set, write_set;
+	int		sock, max_fd = -1;
+	struct usa	sa;
+
+	_shttpd_current_time = time(0);
+	FD_ZERO(&read_set);
+	FD_ZERO(&write_set);
+
+	if (shttpd_join(ctx, &read_set, &write_set, &max_fd))
+		milliseconds = 0;
+
+	if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
+		return;;
+
 	/* Check for incoming connections on listener sockets */
-	LL_FOREACH(&listeners, lp) {
+	LL_FOREACH(&ctx->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, l->is_ssl);
-#else
-				if (sock >= (int) FD_SETSIZE) {
-					elog(E_LOG, NULL,
-					   "shttpd_poll: ctx %p: disarding "
-					   "socket %d, too busy", ctx, sock);
-					(void) closesocket(sock);
-				} else if (!is_allowed(ctx, &sa)) {
-					//elog(E_LOG, NULL, "shttpd_poll: %s is not allowed to connect",   inet_ntoa(sa.u.sin.sin_addr));
-					(void) closesocket(sock);
-				} else {
-					shttpd_add_socket(ctx, sock, l->is_ssl);
-				}
-#endif /* _WIN32 */
-			}
+			if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1)
+				handle_connected_socket(ctx,&sa,sock,l->is_ssl);
 		} while (sock != -1);
 	}
 
-	/* Process all connections */
-	LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
-		c = LL_ENTRY(lp, struct conn, link);
-#ifndef NO_SSL
-		process_connection(c, ((FD_ISSET(c->rem.chan.fd, &read_set) &&
-				!(c->rem.flags & FLAG_SSL_SHOULD_SELECT_ON_READ)) ||
-				(c->rem.chan.ssl.ssl && SSL_pending(c->rem.chan.ssl.ssl)) ||
-				(FD_ISSET(c->rem.chan.fd, &write_set) &&
-				 (c->rem.flags & FLAG_SSL_SHOULD_SELECT_ON_WRITE))),
-			((c->loc.flags & FLAG_ALWAYS_READY)
-
-#else
-
-		process_connection(c, FD_ISSET(c->rem.chan.fd, &read_set),
-				((c->loc.flags & FLAG_ALWAYS_READY)
-#endif
-#if !defined(NO_CGI)
-			|| (c->loc.io_class == &io_cgi &&
-		     FD_ISSET(c->loc.chan.fd, &read_set))
-#endif /* NO_CGI */
-			));
-	}
-}
-
-static void
-free_list(struct llhead *head, void (*dtor)(struct llhead *))
-{
-	struct llhead	*lp, *tmp;
-
-	LL_FOREACH_SAFE(head, lp, tmp) {
-		LL_DEL(lp);
-		dtor(lp);
-	}
-}
-
-static void
-listener_desctructor(struct llhead *lp)
-{
-	struct listener	*listener = LL_ENTRY(lp, struct listener, link);
-
-	(void) closesocket(listener->sock);
-	free(listener);
-}
-
-static void
-mime_type_destructor(struct llhead *lp)
-{
-	struct mime_type_link *mtl = LL_ENTRY(lp, struct mime_type_link, link);
-
-	free(mtl->mime);
-	free(mtl->ext);
-	free(mtl);
-}
-
-static void
-registered_uri_destructor(struct llhead *lp)
-{
-	struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
-
-	free((void *) ruri->uri);
-	free(ruri);
-}
-
-static void
-acl_destructor(struct llhead *lp)
-{
-	struct acl	*acl = LL_ENTRY(lp, struct acl, link);
-	free(acl);
-}
-
-static void
-protected_uri_destructor(struct llhead *lp)
-{
-	struct uri_auth *auth = LL_ENTRY(lp, struct uri_auth, link);
-
-	free((void *) auth->file_name);
-	free((void *) auth->uri);
-	free(auth);
+	if (num_workers(ctx) == 1)
+		process_worker_sockets(first_worker(ctx), &read_set);
 }
 
 /*
@@ -1311,51 +1316,625 @@
 void
 shttpd_fini(struct shttpd_ctx *ctx)
 {
-	free_list(&ctx->mime_types, mime_type_destructor);
-	free_list(&ctx->connections, disconnect);
-	free_list(&ctx->registered_uris, registered_uri_destructor);
- 	free_list(&ctx->uri_auths, protected_uri_destructor);
-	free_list(&ctx->acl, acl_destructor);
-	free_list(&listeners, listener_desctructor);
+	size_t	i;
 
+	free_list(&ctx->workers, worker_destructor);
+	free_list(&ctx->registered_uris, registered_uri_destructor);
+	free_list(&ctx->acl, acl_destructor);
+	free_list(&ctx->listeners, listener_destructor);
 #if !defined(NO_SSI)
-	if (ctx->ssi_extensions)	free(ctx->ssi_extensions);
-	free_list(&ctx->ssi_funcs, ssi_func_destructor);
-#endif /* NO_SSI */
+	free_list(&ctx->ssi_funcs, _shttpd_ssi_func_destructor);
+#endif /* !NO_SSI */
+
+	for (i = 0; i < NELEMS(ctx->options); i++)
+		if (ctx->options[i] != NULL)
+			free(ctx->options[i]);
 
 	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);
-	if (ctx->mime_file)		free(ctx->mime_file);
-	if (ctx->ports)			free(ctx->ports);
 
 	/* TODO: free SSL context */
-	if(ctx->ssl_ctx)
-		SSL_CTX_free(ctx->ssl_ctx);
+
 	free(ctx);
 }
 
-void
-open_listening_ports(struct shttpd_ctx *ctx)
+/*
+ * UNIX socketpair() implementation. Why? Because Windows does not have it.
+ * Return 0 on success, -1 on error.
+ */
+int
+shttpd_socketpair(int sp[2])
 {
-	const char	*p = ctx->ports;
-	int		len, is_ssl;
+	struct sockaddr_in	sa;
+	int			sock, ret = -1;
+	socklen_t		len = sizeof(sa);
 
-	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));
+	sp[0] = sp[1] = -1;
+
+	(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 &&
+	    !bind(sock, (struct sockaddr *) &sa, len) &&
+	    !listen(sock, 1) &&
+	    !getsockname(sock, (struct sockaddr *) &sa, &len) &&
+	    (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
+	    !connect(sp[0], (struct sockaddr *) &sa, len) &&
+	    (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != -1) {
+
+		/* Success */
+		ret = 0;
+	} else {
+
+		/* Failure, close descriptors */
+		if (sp[0] != -1)
+			(void) closesocket(sp[0]);
+		if (sp[1] != -1)
+			(void) closesocket(sp[1]);
 	}
+
+	(void) closesocket(sock);
+	(void) _shttpd_set_non_blocking_mode(sp[0]);
+	(void) _shttpd_set_non_blocking_mode(sp[1]);
+
+#ifndef _WIN32
+	(void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
+	(void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
+#endif /* _WIN32*/
+
+	return (ret);
+}
+
+static int isbyte(int n) { return (n >= 0 && n <= 255); }
+
+static int
+set_inetd(struct shttpd_ctx *ctx, const char *flag)
+{
+	ctx = NULL; /* Unused */
+
+	if (_shttpd_is_true(flag)) {
+		shttpd_set_option(ctx, "ports", NULL);
+		(void) freopen("/dev/null", "a", stderr);
+		add_socket(first_worker(ctx), 0, 0);
+	}
+
+	return (TRUE);
+}
+
+static int
+set_uid(struct shttpd_ctx *ctx, const char *uid)
+{
+	struct passwd	*pw;
+
+	ctx = NULL; /* Unused */
+
+#if !defined(_WIN32)
+	if ((pw = getpwnam(uid)) == NULL)
+		_shttpd_elog(E_FATAL, 0, "%s: unknown user [%s]", __func__, uid);
+	else if (setgid(pw->pw_gid) == -1)
+		_shttpd_elog(E_FATAL, NULL, "%s: setgid(%s): %s",
+		    __func__, uid, strerror(errno));
+	else if (setuid(pw->pw_uid) == -1)
+		_shttpd_elog(E_FATAL, NULL, "%s: setuid(%s): %s",
+		    __func__, uid, strerror(errno));
+#endif /* !_WIN32 */
+	return (TRUE);
+}
+
+static int
+set_acl(struct shttpd_ctx *ctx, const char *s)
+{
+	struct acl	*acl = NULL;
+	char		flag;
+	int		len, a, b, c, d, n, mask;
+
+	/* Delete the old ACLs if any */
+	free_list(&ctx->acl, acl_destructor);
+
+	FOR_EACH_WORD_IN_LIST(s, len) {
+
+		mask = 32;
+
+		if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
+			_shttpd_elog(E_FATAL, NULL, "[%s]: subnet must be "
+			    "[+|-]x.x.x.x[/x]", s);
+		} else if (flag != '+' && flag != '-') {
+			_shttpd_elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
+		} else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
+			_shttpd_elog(E_FATAL, NULL, "bad ip address: [%s]", s);
+		} else	if ((acl = malloc(sizeof(*acl))) == NULL) {
+			_shttpd_elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
+		} else if (sscanf(s + n, "/%d", &mask) == 0) {
+			/* Do nothing, no mask specified */
+		} else if (mask < 0 || mask > 32) {
+			_shttpd_elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
+		}
+
+		acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
+		acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
+		acl->flag = flag;
+		LL_TAIL(&ctx->acl, &acl->link);
+	}
+
+	return (TRUE);
+}
+
+#ifndef NO_SSL
+/*
+ * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
+ */
+static int
+set_ssl(struct shttpd_ctx *ctx, const char *pem)
+{
+	SSL_CTX		*CTX;
+	void		*lib;
+	struct ssl_func	*fp;
+	char *ssl_disabled_protocols = wsmand_options_get_ssl_disabled_protocols();
+	int		retval = FALSE;
+
+	/* Load SSL library dynamically */
+	if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) {
+		_shttpd_elog(E_LOG, NULL, "set_ssl: cannot load %s", SSL_LIB);
+		return (FALSE);
+	}
+
+	for (fp = ssl_sw; fp->name != NULL; fp++)
+		if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL) {
+			_shttpd_elog(E_LOG, NULL,"set_ssl: cannot find %s", fp->name);
+			return (FALSE);
+		}
+
+	/* Initialize SSL crap */
+	SSL_library_init();
+
+	if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
+		_shttpd_elog(E_LOG, NULL, "SSL_CTX_new error");
+	else if (SSL_CTX_use_certificate_file(CTX, wsmand_options_get_ssl_cert_file(), SSL_FILETYPE_PEM) != 1)
+		_shttpd_elog(E_LOG, NULL, "cannot open certificate file %s", pem);
+	else if (SSL_CTX_use_PrivateKey_file(CTX, wsmand_options_get_ssl_key_file(), SSL_FILETYPE_PEM) != 1)
+		_shttpd_elog(E_LOG, NULL, "cannot open PrivateKey %s", pem);
+	else
+		retval = TRUE;
+
+	while (ssl_disabled_protocols) {
+		struct ctx_opts_t {
+			char *name;
+			long opt;
+		} protocols[] = {
+			{ "SSLv2", SSL_OP_NO_SSLv2 },
+			{ "SSLv3", SSL_OP_NO_SSLv3 },
+			{ "TLSv1", SSL_OP_NO_TLSv1 },
+# if OPENSSL_VERSION_NUMBER >= 0x10001000L
+			{ "TLSv1_1", SSL_OP_NO_TLSv1_1 },
+			{ "TLSv1_2", SSL_OP_NO_TLSv1_2 },
+# endif
+			{ NULL, 0 }
+		};
+		char *blank_ptr;
+		int idx;
+		if (*ssl_disabled_protocols == 0)
+			break;
+		blank_ptr = strchr(ssl_disabled_protocols, ' ');
+		if (blank_ptr == NULL)
+			blank_ptr = ssl_disabled_protocols + strlen(ssl_disabled_protocols);
+		for (idx = 0; protocols[idx].name ; ++idx) {
+			if (strncasecmp(protocols[idx].name, ssl_disabled_protocols, blank_ptr-ssl_disabled_protocols) == 0) {
+				//_shttpd_elog(E_LOG, NULL, "SSL: disable %s protocol", protocols[idx].name);
+				debug("SSL: disable %s protocol", protocols[idx].name);
+				SSL_CTX_ctrl(CTX, SSL_CTRL_OPTIONS, protocols[idx].opt, NULL);
+				break;
+			}
+		}
+		if (*blank_ptr == 0)
+			break;
+		ssl_disabled_protocols = blank_ptr + 1;
+	}
+
+	ctx->ssl_ctx = CTX;
+
+	return (retval);
+}
+#endif /* NO_SSL */
+
+static int
+open_log_file(FILE **fpp, const char *path)
+{
+	int	retval = TRUE;
+
+	if (*fpp != NULL)
+		(void) fclose(*fpp);
+
+	if (path == NULL) {
+		*fpp = NULL;
+	} else if ((*fpp = fopen(path, "a")) == NULL) {
+		_shttpd_elog(E_LOG, NULL, "cannot open log file %s: %s",
+		    path, strerror(errno));
+		retval = FALSE;
+	}
+
+	return (retval);
+}
+
+static int set_alog(struct shttpd_ctx *ctx, const char *path) {
+	return (open_log_file(&ctx->access_log, path));
+}
+
+static int set_elog(struct shttpd_ctx *ctx, const char *path) {
+	return (open_log_file(&ctx->error_log, path));
+}
+
+static void show_cfg_page(struct shttpd_arg *arg);
+
+static int
+set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
+{
+	free_list(&ctx->registered_uris, &registered_uri_destructor);
+
+	if (uri != NULL)
+		shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
+
+	return (TRUE);
+}
+
+static struct worker *
+add_worker(struct shttpd_ctx *ctx)
+{
+	struct worker	*worker;
+
+	if ((worker = calloc(1, sizeof(*worker))) == NULL)
+		_shttpd_elog(E_FATAL, NULL, "Cannot allocate worker");
+	LL_INIT(&worker->connections);
+	worker->ctx = ctx;
+	(void) shttpd_socketpair(worker->ctl);
+	LL_TAIL(&ctx->workers, &worker->link);
+
+	return (worker);
+}
+
+#if !defined(NO_THREADS)
+static void
+poll_worker(struct worker *worker, int milliseconds)
+{
+	fd_set		read_set, write_set;
+	int		max_fd = -1;
+
+	FD_ZERO(&read_set);
+	FD_ZERO(&write_set);
+
+	if (multiplex_worker_sockets(worker, &max_fd, &read_set, &write_set))
+		milliseconds = 0;
+
+	if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
+		return;;
+
+	process_worker_sockets(worker, &read_set);
+}
+
+static void
+worker_function(void *param)
+{
+	struct worker *worker = param;
+
+	while (worker->exit_flag == 0)
+		poll_worker(worker, 1000 * 10);
+
+	free_list(&worker->connections, connection_desctructor);
+	free(worker);
+}
+
+static int
+set_workers(struct shttpd_ctx *ctx, const char *value)
+{
+	int		new_num, old_num;
+	struct llhead	*lp, *tmp;
+	struct worker	*worker;
+
+       	new_num = atoi(value);
+	old_num = 0;
+	LL_FOREACH(&ctx->workers, lp)
+		old_num++;
+
+	if (new_num == 1) {
+		if (old_num > 1)
+			/* Stop old threads */
+			LL_FOREACH_SAFE(&ctx->workers, lp, tmp) {
+				worker = LL_ENTRY(lp, struct worker, link);
+				LL_DEL(&worker->link);
+				worker = LL_ENTRY(lp, struct worker, link);
+				worker->exit_flag = 1;
+			}
+		(void) add_worker(ctx);
+	} else {
+		/* FIXME: we cannot here reduce the number of threads */
+		while (new_num > 1 && new_num > old_num) {
+			worker = add_worker(ctx);
+			_beginthread(worker_function, 0, worker);
+			old_num++;
+		}
+	}
+
+	return (TRUE);
+}
+#endif /* NO_THREADS */
+
+static const struct opt {
+	int		index;		/* Index in shttpd_ctx		*/
+	const char	*name;		/* Option name in config file	*/
+	const char	*description;	/* Description			*/
+	const char	*default_value;	/* Default option value		*/
+	int (*setter)(struct shttpd_ctx *, const char *);
+} known_options[] = {
+	{OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
+	{OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
+#ifndef NO_SSL
+	{OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
+#endif /* NO_SSL */
+	{OPT_PORTS, "ports", "Listening ports", NULL, set_ports},
+	{OPT_DIR_LIST, "dir_list", "Directory listing", "yes", NULL},
+	{OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
+	{OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
+#ifndef NO_CGI
+	{OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
+	{OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
+	{OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
+#endif /* NO_CGI */
+	{OPT_SSI_EXTENSIONS, "ssi_ext",	"SSI extensions", SSI_EXT, NULL},
+#ifndef NO_AUTH
+	{OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
+	{OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
+	{OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
+#endif /* !NO_AUTH */
+#ifdef _WIN32
+	{OPT_SERVICE, "service", "Manage WinNNT service (install"
+	    "|uninstall)", NULL, _shttpd_set_nt_service},
+	{OPT_HIDE, "systray", "Hide console, show icon on systray",
+		"no", _shttpd_set_systray},
+#else
+	{OPT_INETD, "inetd", "Inetd mode", "no", set_inetd},
+	{OPT_UID, "uid", "\tRun as user", NULL, set_uid},
+#endif /* _WIN32 */
+	{OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
+	{OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
+	{OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
+	{OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
+	{OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
+#if !defined(NO_THREADS)
+	{OPT_THREADS, "threads", "Number of worker threads", "1", set_workers},
+#endif /* !NO_THREADS */
+	{-1, NULL, NULL, NULL, NULL}
+};
+
+static const struct opt *
+find_opt(const char *opt_name)
+{
+	int	i;
+
+	for (i = 0; known_options[i].name != NULL; i++)
+		if (!strcmp(opt_name, known_options[i].name))
+			return (known_options + i);
+
+	_shttpd_elog(E_FATAL, NULL, "no such option: [%s]", opt_name);
+
+	/* UNREACHABLE */
+	return (NULL);
+}
+
+int
+shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
+{
+	const struct opt	*o = find_opt(opt);
+	int			retval = TRUE;
+
+	/* Call option setter first, so it can use both new and old values */
+	if (o->setter != NULL)
+		retval = o->setter(ctx, val);
+
+	/* Free old value if any */
+	if (ctx->options[o->index] != NULL)
+		free(ctx->options[o->index]);
+
+	/* Set new option value */
+	ctx->options[o->index] = val ? _shttpd_strdup(val) : NULL;
+
+	return (retval);
+}
+
+static void
+show_cfg_page(struct shttpd_arg *arg)
+{
+	struct shttpd_ctx	*ctx = arg->user_data;
+	char			opt_name[20], value[BUFSIZ];
+	const struct opt	*o;
+
+	opt_name[0] = value[0] = '\0';
+
+	if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
+		if (arg->flags & SHTTPD_MORE_POST_DATA)
+			return;
+		(void) shttpd_get_var("o", arg->in.buf, arg->in.len,
+		    opt_name, sizeof(opt_name));
+		(void) shttpd_get_var("v", arg->in.buf, arg->in.len,
+		    value, sizeof(value));
+		shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
+	}
+
+	shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
+	    "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
+
+	shttpd_printf(arg, "%s", "<table border=1"
+	    "<tr><th>Option</th><th>Description</th>"
+	    "<th colspan=2>Value</th></tr>");
+
+	if (opt_name[0] != '\0' && value[0] != '\0')
+		shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
+		    opt_name, value[0] ? value : "NULL");
+
+
+	for (o = known_options; o->name != NULL; o++) {
+		shttpd_printf(arg,
+		    "<form method=post><tr><td>%s</td><td>%s</td>"
+		    "<input type=hidden name=o value='%s'>"
+		    "<td><input type=text name=v value='%s'></td>"
+		    "<td><input type=submit value=save></td></form></tr>",
+		    o->name, o->description, o->name,
+		    ctx->options[o->index] ? ctx->options[o->index] : "");
+	}
+
+	shttpd_printf(arg, "%s", "</table></body></html>");
+	arg->flags |= SHTTPD_END_OF_OUTPUT;
+}
+
+/*
+ * Show usage string and exit.
+ */
+void
+_shttpd_usage(const char *prog)
+{
+	const struct opt	*o;
+
+	(void) fprintf(stderr,
+	    "SHTTPD version %s (c) Sergey Lyubka\n"
+	    "usage: %s [options] [config_file]\n", SHTTPD_VERSION, prog);
+
+#if !defined(NO_AUTH)
+	fprintf(stderr, "  -A <htpasswd_file> <realm> <user> <passwd>\n");
+#endif /* NO_AUTH */
+
+	for (o = known_options; o->name != NULL; o++) {
+		(void) fprintf(stderr, "  -%s\t%s", o->name, o->description);
+		if (o->default_value != NULL)
+			fprintf(stderr, " (default: %s)", o->default_value);
+		fputc('\n', stderr);
+	}
+
+	exit(EXIT_FAILURE);
+}
+
+static void
+set_opt(struct shttpd_ctx *ctx, const char *opt, const char *value)
+{
+	const struct opt	*o;
+
+	o = find_opt(opt);
+	if (ctx->options[o->index] != NULL)
+		free(ctx->options[o->index]);
+	ctx->options[o->index] = _shttpd_strdup(value);
+}
+
+static void
+process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
+{
+	const char		*config_file = CONFIG_FILE;
+	char			line[BUFSIZ], opt[BUFSIZ],
+				val[BUFSIZ], path[FILENAME_MAX], *p;
+	FILE			*fp;
+	size_t			i, line_no = 0;
+
+	/* First find out, which config file to open */
+	for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
+		if (argv[i + 1] == NULL)
+			_shttpd_usage(argv[0]);
+
+	if (argv[i] != NULL && argv[i + 1] != NULL) {
+		/* More than one non-option arguments are given w*/
+		_shttpd_usage(argv[0]);
+	} else if (argv[i] != NULL) {
+		/* Just one non-option argument is given, this is config file */
+		config_file = argv[i];
+	} else {
+		/* No config file specified. Look for one where shttpd lives */
+		if ((p = strrchr(argv[0], DIRSEP)) != 0) {
+			_shttpd_snprintf(path, sizeof(path), "%.*s%s",
+			    p - argv[0] + 1, argv[0], config_file);
+			config_file = path;
+		}
+	}
+
+	fp = fopen(config_file, "r");
+
+	/* If config file was set in command line and open failed, exit */
+	if (fp == NULL && argv[i] != NULL)
+		_shttpd_elog(E_FATAL, NULL, "cannot open config file %s: %s",
+		    config_file, strerror(errno));
+
+	if (fp != NULL) {
+
+		_shttpd_elog(E_LOG, NULL, "Loading config file %s", config_file);
+
+		/* Loop over the lines in config file */
+		while (fgets(line, sizeof(line), fp) != NULL) {
+
+			line_no++;
+
+			/* Ignore empty lines and comments */
+			if (line[0] == '#' || line[0] == '\n')
+				continue;
+
+			if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
+				_shttpd_elog(E_FATAL, NULL, "line %d in %s is invalid",
+				    line_no, config_file);
+
+			set_opt(ctx, opt, val);
+		}
+
+		(void) fclose(fp);
+	}
+
+	/* Now pass through the command line options */
+	for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
+		set_opt(ctx, &argv[i][1], argv[i + 1]);
+}
+
+struct shttpd_ctx *
+shttpd_init(int argc, char *argv[])
+{
+	struct shttpd_ctx	*ctx;
+	struct tm		*tm;
+	const struct opt	*o;
+
+	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+		_shttpd_elog(E_FATAL, NULL, "cannot allocate shttpd context");
+
+	LL_INIT(&ctx->registered_uris);
+	LL_INIT(&ctx->uri_auths);
+	LL_INIT(&ctx->error_handlers);
+	LL_INIT(&ctx->acl);
+	LL_INIT(&ctx->ssi_funcs);
+	LL_INIT(&ctx->listeners);
+	LL_INIT(&ctx->workers);
+
+	/* Initialize options. First pass: set default option values */
+	for (o = known_options; o->name != NULL; o++)
+		ctx->options[o->index] = o->default_value ?
+			_shttpd_strdup(o->default_value) : NULL;
+
+	/* Second and third passes: config file and argv */
+	if (argc > 0 && argv != NULL)
+		process_command_line_arguments(ctx, argv);
+
+	/* Call setter functions */
+	for (o = known_options; o->name != NULL; o++)
+		if (o->setter && ctx->options[o->index] != NULL)
+			if (o->setter(ctx, ctx->options[o->index]) == FALSE) {
+				shttpd_fini(ctx);
+				return (NULL);
+			}
+
+	_shttpd_current_time = time(NULL);
+	tm = localtime(&_shttpd_current_time);
+	_shttpd_tz_offset = 0;
+
+	if (num_workers(ctx) == 1)
+		(void) add_worker(ctx);
+#if 0
+	tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
+#endif
+
+#ifdef _WIN32
+	{WSADATA data;	WSAStartup(MAKEWORD(2,2), &data);}
+#endif /* _WIN32 */
+
+	return (ctx);
 }
diff --git a/src/server/shttpd/shttpd.h b/src/server/shttpd/shttpd.h
index f134ead..a7d33aa 100644
--- a/src/server/shttpd/shttpd.h
+++ b/src/server/shttpd/shttpd.h
@@ -1,25 +1,13 @@
 /*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * Copyright (c) 2004-2008 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
  *
- * 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 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.
  *
- * 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.6 2008/01/11 11:20:31 drozd Exp $
+ * $Id: shttpd.h,v 1.18 2008/08/23 08:34:50 drozd Exp $
  */
 
 #ifndef SHTTPD_HEADER_INCLUDED
@@ -41,67 +29,67 @@
 struct shttpd_arg {
 	void		*priv;		/* Private! Do not touch!	*/
 	void		*state;		/* User state			*/
-	void		*user_data;	/* User-defined data		*/
+	void		*user_data;	/* Data from register_uri()	*/
 	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
+#define	SHTTPD_END_OF_OUTPUT	1	/* No more data do send		*/
+#define	SHTTPD_CONNECTION_ERROR	2	/* Server closed the connection	*/
+#define	SHTTPD_MORE_POST_DATA	4	/* arg->in has incomplete data	*/
+#define	SHTTPD_POST_BUFFER_FULL	8	/* arg->in has max data		*/
+#define	SHTTPD_SSI_EVAL_TRUE	16	/* SSI eval callback must set it*/
+#define	SHTTPD_SUSPEND		32	/* User wants to suspend output	*/
 };
 
 /*
  * 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,
+ * 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
+ * 2. It must not call any blocking functions
+ * 3. It must set SHTTPD_END_OF_OUTPUT flag when there is no more data to send
+ * 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.
+ *	data was processed and can be discarded by SHTTPD.
  * 5. If callback allocates arg->state, to keep state, it must deallocate it
  *    at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
+ * 6. If callback function wants to suspend until some event, it must store
+ *	arg->priv pointer elsewhere, set SHTTPD_SUSPEND flag and return. When
+ *	the event happens, user code should call shttpd_wakeup(priv).
+ *	It is safe to call shttpd_wakeup() from any thread. User code must
+ *	not call shttpd_wakeup once the connection is closed.
  */
 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_init		Initialize shttpd context
+ * shttpd_fini		Dealocate the context, close all connections
+ * shttpd_set_option	Set new value for option
+ * shttpd_register_uri	Setup the callback function for specified URL
  * shttpd_poll		Do connections processing
  * shttpd_version	return string with SHTTPD version
  * shttpd_get_var	Fetch POST/GET variable value by name. Return value len
  * shttpd_get_header	return value of the specified HTTP header
- * shttpd_get_env	return string values for the following
- *			pseudo-variables: "REQUEST_METHOD", "REQUEST_URI",
- *			"REMOTE_USER" and "REMOTE_ADDR".
+ * shttpd_get_env	return values for the following	pseudo-variables:
+ 			"REQUEST_METHOD", "REQUEST_URI",
+ *			"REMOTE_USER" and "REMOTE_ADDR"
+ * shttpd_printf	helper function to output data
+ * shttpd_handle_error	register custom HTTP error handler
+ * shttpd_wakeup	clear SHTTPD_SUSPEND state for the connection
  */
 
 typedef int (*basic_auth_callback)(char *user, char *passwd);
 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);
+struct shttpd_ctx *shttpd_init(int argc, char *argv[]);
+int shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
 void shttpd_fini(struct shttpd_ctx *);
-void shttpd_add_mime_type(struct shttpd_ctx *,
-		const char *extension, const char *mime_type);
-int shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl);
 void shttpd_register_uri(struct shttpd_ctx *ctx, const char *uri,
 		shttpd_callback_t callback, void *const user_data);
 void shttpd_protect_uri(struct shttpd_ctx *ctx,
-		const char *uri, const char *password_file,  basic_auth_callback cb, int type);
+                const char *uri, const char *password_file,  basic_auth_callback cb, int type);
 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,
@@ -115,19 +103,9 @@
 		shttpd_callback_t func, void *const data);
 void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
 		shttpd_callback_t func, void *const user_data);
-
-/*
- * The following three functions are for applications that need to
- * load-balance the connections on their own. Many threads may be spawned
- * with one SHTTPD context per thread. Boss thread may only wait for
- * new connections by means of shttpd_accept(). Then it may scan thread
- * pool for the idle thread by means of shttpd_active(), and add new
- * connection to the context by means of shttpd_add().
- */
-void shttpd_add_socket(struct shttpd_ctx *, int sock, int is_ssl);
-int shttpd_accept(int lsn_sock, int milliseconds);
-int shttpd_active(struct shttpd_ctx *);
-
+void shttpd_wakeup(const void *priv);
+int shttpd_join(struct shttpd_ctx *, fd_set *, fd_set *, int *max_fd);
+int  shttpd_socketpair(int sp[2]);
 
 #ifdef __cplusplus
 }
diff --git a/src/server/shttpd/shttpd_defs.h b/src/server/shttpd/shttpd_defs.h
deleted file mode 100644
index edcc5fd..0000000
--- a/src/server/shttpd/shttpd_defs.h
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file.  As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#ifndef DEFS_HEADER_DEFINED
-#define	DEFS_HEADER_DEFINED
-
-#include "wsman_config.h"
-
-#ifdef SHTTPD_GSS
-#include <gssapi/gssapi_generic.h>
-#endif
-
-#include "std_includes.h"
-#include "llist.h"
-#include "io.h"
-#include "shttpd.h"
-#include "adapter.h"
-#include "md5.h"
-#include "u/libu.h"
-#include <dlfcn.h>
-
-#undef VERSION
-#define	VERSION		"1.39"		/* 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]))
-
-#define GLOBAL_DEBUG
-#ifdef GLOBAL_DEBUG
-#ifdef _DEBUG
-#define	DBG(x)	do { printf x ; putchar('\n'); fflush(stdout); } while (0)
-#else
-#define	DBG(x) do { debug x; } while(0)
-#endif /* DEBUG */
-#else
-#define DBG(x)
-#endif
-
-#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 {BASIC_AUTH, DIGEST_AUTH};
-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;
-#ifdef ENABLE_IPV6
-		struct sockaddr_in6 sin6;
-#endif
-		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;
-	int type;
-	size_t		uri_len;
-	union variant callback;
-	void *callback_data;
-};
-
-/*
- * User may want to handle certain errors. This structure holds the
- * handlers for corresponding error codes.
- */
-struct error_handler {
-	struct llhead	link;
-	int		code;
-	union variant	callback;
-	void		*callback_data;
-};
-
-struct http_header {
-	int		len;		/* Header name length		*/
-	int		type;		/* Header type			*/
-	size_t		offset;		/* Value placeholder		*/
-	const char	*name;		/* Header name			*/
-};
-
-/*
- * This guy holds parsed HTTP headers
- */
-struct headers {
-	union variant	cl;		/* Content-Length:		*/
-	union variant	ct;		/* Content-Type:		*/
-	union variant	connection;	/* Connection:			*/
-	union variant	ims;		/* If-Modified-Since:		*/
-	union variant	user;		/* Remote user name		*/
-	union variant	auth;		/* Authorization		*/
-	union variant	useragent;	/* User-Agent:			*/
-	union variant	referer;	/* Referer:			*/
-	union variant	cookie;		/* Cookie:			*/
-	union variant	location;	/* Location:			*/
-	union variant	range;		/* Range:			*/
-	union variant	status;		/* Status:			*/
-	union variant	transenc;	/* Transfer-Encoding:		*/
-};
-
-/* Must go after union variant definition */
-#include "ssl.h"
-#include <openssl/err.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	*/
-#define	FLAG_SSL_SHOULD_SELECT_ON_WRITE	128	/* ssl should select on write next time  */
-#define	FLAG_SSL_SHOULD_SELECT_ON_READ	256	/*  ssl should select on read next time */
-#define FLAG_RESPONSE_COMPLETE 512
-};
-
-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		*/
-#ifdef SHTTPD_GSS
-	gss_ctx_id_t gss_ctx;    /* GSS context */
-#endif
-
-#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	*/
-	struct llhead	acl;		/* Access control list		*/
-
-	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).
- *
- * In every iteration of the loop, "s" points to the current value, and
- * "len" specifies its length. The code inside loop must not change
- * "s" and "len" parameters.
- */
-#define	FOR_EACH_WORD_IN_LIST(s,len)					\
-	for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0;	\
-			s += len, s+= strspn(s, DELIM_CHARS))
-
-/*
- * IPv4 ACL entry. Specifies subnet with deny/allow flag
- */
-struct acl {
-	struct llhead	link;
-	uint32_t	ip;		/* IP, in network byte order	*/
-	uint32_t	mask;		/* Also in network byte order	*/
-	int		flag;		/* Either '+' or '-'		*/
-};
-
-/*
- * shttpd.c
- */
-extern time_t		current_time;	/* Current UTC time		*/
-extern int		tz_offset;	/* Offset from GMT time zone	*/
-extern const struct vec known_http_methods[];
-
-extern void	stop_stream(struct stream *stream);
-extern int	url_decode(const char *, int, char *dst, int);
-extern void	send_server_error(struct conn *, int code, const char *reason);
-extern int	get_headers_len(const char *buf, size_t buflen);
-extern void	parse_headers(const char *s, int len, struct headers *parsed);
-extern void	open_listening_ports(struct shttpd_ctx *ctx);
-
-/*
- * 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, ...);
-#define elog(level, conn, ...) \
-        do { \
-                debug( __VA_ARGS__ ); \
-                if (level == E_FATAL) exit(EXIT_FAILURE); \
-        } while (0)
-
-// extern void	log_access(FILE *fp, const struct conn *c);
-
-/*
- * string.c
- */
-extern void	my_strlcpy(register char *, register const char *, size_t);
-/*
-extern int	my_strncasecmp(register const char *, register const char *, size_t);
-extern char	*my_strndup(const char *ptr, size_t len);
-extern char	*my_strdup(const char *str);
-extern int	my_snprintf(char *buf, size_t buflen, const char *fmt, ...);
-*/
-extern int	match_extension(const char *path, const char *ext_list);
-
-/*
- * compat_*.c
- */
-extern void	set_close_on_exec(int fd);
-extern int	set_non_blocking_mode(int fd);
-extern int	my_stat(const char *, struct stat *stp);
-extern int	my_open(const char *, int flags, int mode);
-extern int	my_remove(const char *);
-extern int	my_rename(const char *, const char *);
-extern int	my_mkdir(const char *, int);
-extern char *	my_getcwd(char *, int);
-extern int	spawn_process(struct conn *c, const char *prog,
-		char *envblk, char *envp[], int sock, const char *dir);
-
-/*
- * io_*.c
- */
-extern const struct io_class	io_file;
-extern const struct io_class	io_socket;
-extern const struct io_class	io_ssl;
-extern const struct io_class	io_cgi;
-extern const struct io_class	io_dir;
-extern const struct io_class	io_embedded;
-extern const struct io_class	io_ssi;
-
-extern int	put_dir(const char *path);
-extern void	get_dir(struct conn *c);
-extern void	get_file(struct conn *c, struct stat *stp);
-extern void	ssl_handshake(struct stream *stream);
-extern void	setup_embedded_stream(struct conn *, union variant, void *);
-extern struct registered_uri *is_registered_uri(struct shttpd_ctx *,
-		const char *uri);
-extern void	do_ssi(struct conn *);
-extern void	ssi_func_destructor(struct llhead *lp);
-
-/*
- * auth.c
- */
-extern int	check_authorization(struct conn *c, const char *path);
-extern int	is_authorized_for_put(struct conn *c);
-extern int	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/src/server/shttpd/ssl.h b/src/server/shttpd/ssl.h
index d045b6e..a863f2c 100644
--- a/src/server/shttpd/ssl.h
+++ b/src/server/shttpd/ssl.h
@@ -10,7 +10,7 @@
 
 #ifdef HAVE_OPENSSL
 
-# include <openssl/ssl.h>
+#include <openssl/ssl.h>
 
 #else
 
@@ -26,8 +26,8 @@
 
 #define	SSL_ERROR_WANT_READ	2
 #define	SSL_ERROR_WANT_WRITE	3
-#define SSL_ERROR_SYSCALL               5
-#define SSL_FILETYPE_PEM	1
+#define	SSL_ERROR_SYSCALL	5
+#define	SSL_FILETYPE_PEM	1
 
 #endif
 
@@ -59,9 +59,3 @@
 		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))
-#define SSL_CTX_use_certificate_chain_file(x,y)	(* (int (*)(SSL_CTX *, \
-		const char *)) FUNC(15))((x), (y))
-#define	SSL_CTX_free(x)	(*(void (*)(SSL_CTX *)) FUNC(13))(x)
-#define	SSL_pending(x) (*(int (*)(SSL *)) FUNC(14))(x)
-#define SSL_CTX_ctrl(w,x,y,z) (*(long (*)(SSL_CTX *,int,long,void *)) FUNC(16))((w),(x),(y),(z))
-                                    
diff --git a/src/server/shttpd/standalone.c b/src/server/shttpd/standalone.c
new file mode 100644
index 0000000..db920b2
--- /dev/null
+++ b/src/server/shttpd/standalone.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int	exit_flag;	/* Program termination 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;
+
+#if !defined(NO_AUTH)
+	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
+		if (argc != 6)
+			_shttpd_usage(argv[0]);
+		exit(_shttpd_edit_passwords(argv[2],argv[3],argv[4],argv[5]));
+	}
+#endif /* NO_AUTH */
+
+	if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
+		_shttpd_usage(argv[0]);
+
+#if defined(_WIN32)
+	try_to_run_as_nt_service();
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+	(void) signal(SIGCHLD, signal_handler);
+	(void) signal(SIGPIPE, SIG_IGN);
+#endif /* _WIN32 */
+
+	(void) signal(SIGTERM, signal_handler);
+	(void) signal(SIGINT, signal_handler);
+
+	if ((ctx = shttpd_init(argc, argv)) == NULL)
+		_shttpd_elog(E_FATAL, NULL, "%s",
+		    "Cannot initialize SHTTPD context");
+
+	_shttpd_elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
+	    SHTTPD_VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
+
+	while (exit_flag == 0)
+		shttpd_poll(ctx, 10 * 1000);
+
+	_shttpd_elog(E_LOG, NULL, "Exit on signal %d", exit_flag);
+	shttpd_fini(ctx);
+
+	return (EXIT_SUCCESS);
+}
diff --git a/src/server/shttpd/std_includes.h b/src/server/shttpd/std_includes.h
index f61503c..4bf1ea7 100644
--- a/src/server/shttpd/std_includes.h
+++ b/src/server/shttpd/std_includes.h
@@ -28,7 +28,6 @@
 #include <limits.h>
 #include <stddef.h>
 #include <stdio.h>
-#include <wchar.h>
 
 #if defined(_WIN32)		/* Windows specific	*/
 #include "compat_win32.h"
diff --git a/src/server/shttpd/string.c b/src/server/shttpd/string.c
index 0f84ce3..7e09e42 100644
--- a/src/server/shttpd/string.c
+++ b/src/server/shttpd/string.c
@@ -8,30 +8,88 @@
  * this stuff is worth it, you can buy me a beer in return.
  */
 
-#include "shttpd_defs.h"
+#include "defs.h"
 
 void
-my_strlcpy(register char *dst, register const char *src, size_t n)
+_shttpd_strlcpy(register char *dst, register const char *src, size_t n)
 {
-       for (; *src != '\0' && n > 1; n--)
-               *dst++ = *src++;
-       *dst = '\0';
+	for (; *src != '\0' && n > 1; n--)
+		*dst++ = *src++;
+	*dst = '\0';
+}
+
+int
+_shttpd_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 *
+_shttpd_strndup(const char *ptr, size_t len)
+{
+	char	*p;
+
+	if ((p = malloc(len + 1)) != NULL)
+		_shttpd_strlcpy(p, ptr, len + 1);
+
+	return (p);
+
+}
+
+char *
+_shttpd_strdup(const char *str)
+{
+	return (_shttpd_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
+_shttpd_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)
+_shttpd_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 &&
-		    !strncasecmp(path + path_len - len, ext_list, len))
-			return (1);
+		if (len < path_len && path[path_len - len - 1] == '.' &&
+		    !_shttpd_strncasecmp(path + path_len - len, ext_list, len))
+			return (TRUE);
 
-	return (0);
+	return (FALSE);
 }
diff --git a/src/server/wsmand-listener.c b/src/server/wsmand-listener.c
index a1cfe1f..2d20550 100644
--- a/src/server/wsmand-listener.c
+++ b/src/server/wsmand-listener.c
@@ -65,7 +65,6 @@
 
 
 #include "shttpd.h"
-#include "adapter.h"
 
 #include "wsman-plugins.h"
 #include "wsmand-listener.h"
@@ -343,7 +342,8 @@
 DONE:
 
 	if (fault_reason == NULL) {
-		fault_reason = shttpd_reason_phrase(status);
+		// this is a way to segfault, investigate
+		//fault_reason = shttpd_reason_phrase(status);
 	}
 	debug("Response status=%d (%s)", status, fault_reason);
 
@@ -433,28 +433,29 @@
 	}
 }
 
-static struct shttpd_ctx *create_shttpd_context(SoapH soap)
+static struct shttpd_ctx *create_shttpd_context(SoapH soap, int port)
 {
 	struct shttpd_ctx *ctx;
-	if (wsmand_options_get_use_ssl()) {
-		message("ssl certificate: %s", wsmand_options_get_ssl_cert_file());
-		message("Using SSL");
-		ctx = shttpd_init(NULL,
-				  "ssl_certificate",
-				  wsmand_options_get_ssl_cert_file(),
-				  "auth_realm",
-				  AUTHENTICATION_REALM,
-				  NULL);
-	} else {
-		ctx = shttpd_init(NULL,
-				  "auth_realm", AUTHENTICATION_REALM,
-				   NULL);
-	}
+	char *tmps;
+	int len;
+
+	ctx = shttpd_init(0, NULL);
 	if (ctx == NULL) {
 		return NULL;
 	}
+	if (wsmand_options_get_use_ssl()) {
+		message("ssl certificate: %s", wsmand_options_get_ssl_cert_file());
+		shttpd_set_option(ctx, "ssl_cert", wsmand_options_get_ssl_cert_file());
+	}
+	len = snprintf(NULL, 0, "%d%s", port, wsmand_options_get_use_ssl() ? "s" : "");
+	tmps = malloc((len+1) * sizeof(char));
+	snprintf(tmps, len+1, "%d%s", port, wsmand_options_get_use_ssl() ? "s" : "");
+	shttpd_set_option(ctx, "ports", tmps);
+	free(tmps);
+	shttpd_set_option(ctx, "auth_realm", AUTHENTICATION_REALM);
 	shttpd_register_uri(ctx, wsmand_options_get_service_path(),
 			    server_callback, (void *) soap);
+	protect_uri(ctx, wsmand_options_get_service_path());
 	shttpd_register_uri(ctx, ANON_IDENTIFY_PATH,
 			    server_callback, (void *) soap);
 
@@ -464,7 +465,6 @@
 	protect_uri( ctx, DEFAULT_CIMINDICATION_PATH );
 #endif
 
-	protect_uri( ctx, wsmand_options_get_service_path());
 
 	return ctx;
 }
@@ -608,56 +608,6 @@
         }
 }
 
-static void *thread_function(void *param)
-{
-    struct thread *thread = param;
-
-    for (;;)
-        shttpd_poll(thread->ctx, 1000);
-
-    return NULL;
-}
-
-
-static struct thread *
-spawn_new_thread(pthread_attr_t pattrs, SoapH soap)
-{
-    struct shttpd_ctx   *ctx;
-    struct thread       *thread;
-    pthread_t           tid;
-	debug("spawning new thread");
-
-    thread  = malloc(sizeof(*thread));
-    ctx = create_shttpd_context(soap);
-
-    assert(ctx != NULL);
-    assert(thread != NULL);
-
-    thread->ctx = ctx;
-    thread->next    = threads;
-    threads     = thread;
-
-	pthread_create(&tid, &pattrs, thread_function, thread);
-
-    return (thread);
-}
-
-
-static struct thread *
-find_not_busy_thread(int *num_threads, int max_connections_per_thread)
-{
-    struct thread   *thread;
-
-    for (thread = threads, *num_threads=0; thread != NULL; thread = thread->next) {
-	debug("Active sockets: %d, Thread Number: %d", shttpd_active(thread->ctx), *num_threads );
-        (*num_threads)++;
-        if (shttpd_active(thread->ctx) < max_connections_per_thread)
-            return (thread);
-	}
-
-    return (NULL);
-}
-
 
 WsManListenerH *wsmand_start_server(dictionary * ini)
 {
@@ -717,9 +667,7 @@
 	wsmand_shutdown_add_handler(listener_shutdown_handler,
 				    &continue_working);
 
-	httpd_ctx = create_shttpd_context(soap);
-
-	lsn = shttpd_listen(httpd_ctx, port, use_ssl);
+	httpd_ctx = create_shttpd_context(soap, port);
 
 	if (wsman_setup_thread(&pattrs) == 0 )
 		return listener;
@@ -730,24 +678,7 @@
 #endif
 
 	while (continue_working) {
-		if ((sock = shttpd_accept(lsn, 1000)) == -1) {
-			continue;
-		}
-		debug("Sock %d accepted", sock);
-                if ((thread = find_not_busy_thread(&num_threads, max_connections_per_thread)) == NULL){
-                        if(max_threads){
-                                if(num_threads < max_threads){
-                                        thread = spawn_new_thread(pattrs, soap);
-                                }
-                                else{
-                                        continue;
-                                }
-                        }
-                        else{
-                                thread = spawn_new_thread(pattrs, soap);
-                        }
-		}
-                shttpd_add_socket(thread->ctx, sock, use_ssl);
-        }
-        return listener;
+		shttpd_poll(httpd_ctx, 1000);
+	}
+	return listener;
 }