[dss-commits] r8729 - in dss/trunk: . core external external/mongoose external/shttpd

dss-commits at forum.digitalstrom.org dss-commits at forum.digitalstrom.org
Thu Sep 3 11:48:59 CEST 2009


Author: jwinkelmann
Date: 2009-09-03 11:48:59 +0200 (Thu, 03 Sep 2009)
New Revision: 8729

Added:
   dss/trunk/external/mongoose/
   dss/trunk/external/mongoose/CMakeLists.txt
   dss/trunk/external/mongoose/mongoose.c
   dss/trunk/external/mongoose/mongoose.h
Removed:
   dss/trunk/external/shttpd/CMakeLists.txt
   dss/trunk/external/shttpd/auth.c
   dss/trunk/external/shttpd/cgi.c
   dss/trunk/external/shttpd/compat_rtems.c
   dss/trunk/external/shttpd/compat_rtems.h
   dss/trunk/external/shttpd/compat_unix.c
   dss/trunk/external/shttpd/compat_unix.h
   dss/trunk/external/shttpd/compat_win32.c
   dss/trunk/external/shttpd/compat_win32.h
   dss/trunk/external/shttpd/compat_wince.c
   dss/trunk/external/shttpd/compat_wince.h
   dss/trunk/external/shttpd/config.c
   dss/trunk/external/shttpd/config.h
   dss/trunk/external/shttpd/defs.h
   dss/trunk/external/shttpd/io.h
   dss/trunk/external/shttpd/io_cgi.c
   dss/trunk/external/shttpd/io_dir.c
   dss/trunk/external/shttpd/io_emb.c
   dss/trunk/external/shttpd/io_file.c
   dss/trunk/external/shttpd/io_socket.c
   dss/trunk/external/shttpd/io_ssi.c
   dss/trunk/external/shttpd/io_ssl.c
   dss/trunk/external/shttpd/llist.h
   dss/trunk/external/shttpd/log.c
   dss/trunk/external/shttpd/md5.c
   dss/trunk/external/shttpd/md5.h
   dss/trunk/external/shttpd/shttpd.1
   dss/trunk/external/shttpd/shttpd.c
   dss/trunk/external/shttpd/shttpd.h
   dss/trunk/external/shttpd/ssl.h
   dss/trunk/external/shttpd/standalone.c
   dss/trunk/external/shttpd/std_includes.h
   dss/trunk/external/shttpd/string.c
Modified:
   dss/trunk/CMakeLists.txt
   dss/trunk/core/webserver.cpp
   dss/trunk/core/webserver.h
   dss/trunk/external/CMakeLists.txt
Log:
webserver: replace shttpd with mongoose

Modified: dss/trunk/CMakeLists.txt
===================================================================
--- dss/trunk/CMakeLists.txt	2009-09-03 09:34:09 UTC (rev 8728)
+++ dss/trunk/CMakeLists.txt	2009-09-03 09:48:59 UTC (rev 8729)
@@ -111,4 +111,4 @@
 ADD_EXECUTABLE(dss main.cpp namespaces.cpp)
 
 TARGET_LINK_LIBRARIES(dss ${TEST_LIB} ${BOOST_TEST_LIB} core unix slaves webservices pthread
-	shttpd ${REQUIRED_LIBS})
+	mongoose ${REQUIRED_LIBS})

Modified: dss/trunk/core/webserver.cpp
===================================================================
--- dss/trunk/core/webserver.cpp	2009-09-03 09:34:09 UTC (rev 8728)
+++ dss/trunk/core/webserver.cpp	2009-09-03 09:48:59 UTC (rev 8729)
@@ -46,15 +46,14 @@
   //============================================= WebServer
 
   WebServer::WebServer(DSS* _pDSS)
-  : Subsystem(_pDSS, "WebServer"),
-    Thread("WebServer")
+  : Subsystem(_pDSS, "WebServer")
   {
     Logger::getInstance()->log("Starting Webserver...");
-    m_SHttpdContext = shttpd_init();
+    m_mgContext = mg_start();
   } // ctor
 
   WebServer::~WebServer() {
-    shttpd_fini(m_SHttpdContext);
+    mg_stop(m_mgContext);
   } // dtor
 
   void WebServer::initialize() {
@@ -97,7 +96,7 @@
     m_Plugins.push_back(plugin);
 
     log("Registering " + pFileNode->getStringValue() + " for URI '" + pURINode->getStringValue() + "'");
-    shttpd_register_uri(m_SHttpdContext, pURINode->getStringValue().c_str(), &httpPluginCallback, plugin);
+    mg_set_uri_callback(m_mgContext, pURINode->getStringValue().c_str(), &httpPluginCallback, plugin);
   } // loadPlugin
 
   void WebServer::setupAPI() {
@@ -409,28 +408,21 @@
   } // setupAPI
 
   void WebServer::doStart() {
-    run();
-  } // start
-
-  void WebServer::execute() {
     string ports = DSS::getInstance()->getPropertySystem().getStringValue(getConfigPropertyBasePath() + "ports");
     log("Webserver: Listening on port(s) " + ports);
-    shttpd_set_option(m_SHttpdContext, "ports", ports.c_str());
+    mg_set_option(m_mgContext, "ports", ports.c_str());
 
     string aliases = string("/=") + DSS::getInstance()->getPropertySystem().getStringValue(getConfigPropertyBasePath() + "webroot");
     log("Webserver: Configured aliases: " + aliases);
-    shttpd_set_option(m_SHttpdContext, "aliases", aliases.c_str());
+    mg_set_option(m_mgContext, "aliases", aliases.c_str());
 
-    shttpd_register_uri(m_SHttpdContext, "/browse/*", &httpBrowseProperties, NULL);
-    shttpd_register_uri(m_SHttpdContext, "/json/*", &jsonHandler, NULL);
+    mg_set_uri_callback(m_mgContext, "/browse/*", &httpBrowseProperties, NULL);
+    mg_set_uri_callback(m_mgContext, "/json/*", &jsonHandler, NULL);
 
     loadPlugins();
 
     log("Webserver started", lsInfo);
-    while(!m_Terminated) {
-      shttpd_poll(m_SHttpdContext, 1000);
-    }
-  } // execute
+  } // start
 
   const char* httpCodeToMessage(const int _code) {
     if(_code == 400) {
@@ -446,11 +438,12 @@
     }
   }
 
-  void WebServer::emitHTTPHeader(int _code, struct shttpd_arg* _arg, const std::string& _contentType) {
+  void WebServer::emitHTTPHeader(int _code, struct mg_connection* _connection, const std::string& _contentType) {
     std::stringstream sstream;
     sstream << "HTTP/1.1 " << _code << ' ' << httpCodeToMessage(_code) << "\r\n";
     sstream << "Content-Type: " << _contentType << "; charset=utf-8\r\n\r\n";
-    shttpd_printf(_arg, sstream.str().c_str());
+    string tmp = sstream.str();
+    mg_write(_connection, tmp.c_str(), tmp.length());
   } // emitHTTPHeader
 
   HashMapConstStringString parseParameter(const char* _params) {
@@ -656,7 +649,7 @@
     return arr.str();
   } // toJSONArray<int>
 
-  string WebServer::callDeviceInterface(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, IDeviceInterface* _interface, Session* _session) {
+  string WebServer::callDeviceInterface(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, IDeviceInterface* _interface, Session* _session) {
     bool ok = true;
     string errorString;
     assert(_interface != NULL);
@@ -738,7 +731,7 @@
         || endsWith(_method, "/getConsumption");
   } // isDeviceInterfaceCall
 
-  string WebServer::handleApartmentCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleApartmentCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     bool ok = true;
     string errorMessage;
     _handled = true;
@@ -779,7 +772,7 @@
         if(interface == NULL) {
           interface = &getDSS().getApartment().getGroup(GroupIDBroadcast);
         }
-        return callDeviceInterface(_method, _parameter, _arg, interface, _session);
+        return callDeviceInterface(_method, _parameter, _connection, interface, _session);
       } else {
         stringstream sstream;
         sstream << "{ ok: " << ToJSONValue(ok) << ", message: " << ToJSONValue(errorMessage) << " }";
@@ -837,7 +830,7 @@
     }
   } // handleApartmentCall
 
-  string WebServer::handleZoneCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleZoneCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     bool ok = true;
     string errorMessage;
     _handled = true;
@@ -913,7 +906,7 @@
             if(interface == NULL) {
               interface = pZone;
             }
-            return callDeviceInterface(_method, _parameter, _arg, interface, _session);
+            return callDeviceInterface(_method, _parameter, _connection, interface, _session);
           }
         } else if(endsWith(_method, "/getLastCalledScene")) {
           int lastScene = 0;
@@ -948,7 +941,7 @@
     }
   } // handleZoneCall
 
-  string WebServer::handleDeviceCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleDeviceCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     bool ok = true;
     string errorMessage;
     _handled = true;
@@ -983,7 +976,7 @@
     }
     if(ok) {
       if(isDeviceInterfaceCall(_method)) {
-        return callDeviceInterface(_method, _parameter, _arg, pDevice, _session);
+        return callDeviceInterface(_method, _parameter, _connection, pDevice, _session);
       } else if(beginsWith(_method, "device/getGroups")) {
         int numGroups = pDevice->getGroupsCount();
         stringstream sstream;
@@ -1067,7 +1060,7 @@
     }
   } // handleDeviceCall
 
-  string WebServer::handleCircuitCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleCircuitCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     _handled = true;
     string idString = _parameter["id"];
     if(idString.empty()) {
@@ -1104,7 +1097,7 @@
   } // handleCircuitCall
 
 
-  string WebServer::handleSetCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleSetCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     _handled = true;
     if(endsWith(_method, "/fromApartment")) {
       _handled = true;
@@ -1196,7 +1189,7 @@
       } else if(isDeviceInterfaceCall(_method)) {
         SetBuilder builder;
         Set set = builder.buildSet(self, NULL);
-        return callDeviceInterface(_method, _parameter, _arg, &set, _session);
+        return callDeviceInterface(_method, _parameter, _connection, &set, _session);
       } else {
         _handled = false;
       }
@@ -1205,7 +1198,7 @@
     return "";
   } // handleSetCall
 
-  string WebServer::handlePropertyCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handlePropertyCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     _handled = true;
     string propName = _parameter["path"];
     if(propName.empty()) {
@@ -1325,7 +1318,7 @@
     return "";
   } // handlePropertyCall
 
-  string WebServer::handleEventCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleEventCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     _handled = true;
     string result;
     if(endsWith(_method, "/raise")) {
@@ -1348,7 +1341,11 @@
     return result;
   } // handleEventCall
 
-  string WebServer::handleStructureCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleStructureCall(const std::string& _method, 
+                                        HashMapConstStringString& _parameter, 
+                                        struct mg_connection* _connection, 
+                                        bool& _handled, 
+                                        Session* _session) {
     _handled = true;
     StructureManipulator manipulator(getDSS().getDS485Interface(), getDSS().getApartment());
     if(endsWith(_method, "structure/zoneAddDevice")) {
@@ -1403,7 +1400,7 @@
     }
   } // handleStructureCall
 
-  string WebServer::handleSimCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleSimCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     _handled = true;
     if(beginsWith(_method, "sim/switch")) {
       if(endsWith(_method, "/switch/pressed")) {
@@ -1484,7 +1481,7 @@
     return "";
   } // handleSimCall
 
-  string WebServer::handleDebugCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+  string WebServer::handleDebugCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     _handled = true;
     if(endsWith(_method, "/sendFrame")) {
       int destination = strToIntDef(_parameter["destination"],0) & 0x3F;
@@ -1525,8 +1522,8 @@
       return "";
     }
   } // handleDebugCall
-  
-  string WebServer::handleMeteringCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session) {
+
+  string WebServer::handleMeteringCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session) {
     if(endsWith(_method, "/getResolutions")) {
       _handled = true;
       std::vector<boost::shared_ptr<MeteringConfigChain> > meteringConfig = getDSS().getMetering().getConfig();
@@ -1594,7 +1591,7 @@
       if(resolution == -1) {
         return ResultToJSON(false, "Need could not parse resolution '" + resolutionString + "'");
       }
-      if(typeString.empty()) {        
+      if(typeString.empty()) {
         return ResultToJSON(false, "Need a type, 'energy' or 'consumption'");
       } else {
         if(typeString == "consumption") {
@@ -1649,7 +1646,7 @@
         }
       } else {
         return ResultToJSON(false, "Could not parse resolution '" + resolutionString + "'");
-      }    
+      }
     } else if(endsWith(_method, "/getAggregatedValues")) { //?set=;n=,resolution=;type=
       _handled = false;
       return "";
@@ -1658,35 +1655,43 @@
     return "";
   } // handleMeteringCall
 
-  void WebServer::httpPluginCallback(struct shttpd_arg* _arg) {
-    if(_arg->user_data != NULL) {
-      WebServerPlugin* plugin = static_cast<WebServerPlugin*>(_arg->user_data);
+  void WebServer::httpPluginCallback(struct mg_connection* _connection,
+                                     const struct mg_request_info* _info, 
+                                     void* _userData) {
+    if(_userData != NULL) {
+      WebServerPlugin* plugin = static_cast<WebServerPlugin*>(_userData);
       WebServer& self = DSS::getInstance()->getWebServer();
-      string uri = shttpd_get_env(_arg, "REQUEST_URI");
+      
+      string uri = _info->uri;
       self.log("Plugin: Processing call to " + uri);
 
-      self.pluginCalled(_arg, *plugin, uri);
+      self.pluginCalled(_connection, _info, *plugin, uri);
     }
   } // httpPluginCallback
 
-  void WebServer::pluginCalled(struct shttpd_arg* _arg, WebServerPlugin& plugin, const std::string& _uri) {
-    HashMapConstStringString paramMap = parseParameter(shttpd_get_env(_arg, "QUERY_STRING"));
+  void WebServer::pluginCalled(struct mg_connection* _connection, 
+                               const struct mg_request_info* _info,
+                               WebServerPlugin& plugin, 
+                               const std::string& _uri) {
+    HashMapConstStringString paramMap = parseParameter(_info->query_string);
 
     string result;
     if(plugin.handleRequest(_uri, paramMap, getDSS(), result)) {
-      emitHTTPHeader(200, _arg, "text/plain");
-      shttpd_printf(_arg, result.c_str());
+      emitHTTPHeader(200, _connection, "text/plain");
+      mg_write(_connection, result.c_str(), result.length());
     } else {
-      emitHTTPHeader(500, _arg, "text/plain");
-      shttpd_printf(_arg, "error");
+      emitHTTPHeader(500, _connection, "text/plain");
+      mg_printf(_connection, "error");
     }
-    _arg->flags |= SHTTPD_END_OF_OUTPUT;
   } // pluginCalled
 
-  void WebServer::jsonHandler(struct shttpd_arg* _arg) {
-    const string urlid = "/json/";
-    string uri = shttpd_get_env(_arg, "REQUEST_URI");
-    HashMapConstStringString paramMap = parseParameter(shttpd_get_env(_arg, "QUERY_STRING"));
+  void WebServer::jsonHandler(struct mg_connection* _connection,
+                              const struct mg_request_info* _info, 
+                              void* _userData) {
+    const string urlid = "/json/";     
+    string uri = _info->uri;
+    
+    HashMapConstStringString paramMap = parseParameter(_info->query_string);
 
     string method = uri.substr(uri.find(urlid) + urlid.size());
 
@@ -1711,53 +1716,53 @@
     string result;
     bool handled = false;
     if(beginsWith(method, "apartment/")) {
-      result = self.handleApartmentCall(method, paramMap, _arg, handled, session);
+      result = self.handleApartmentCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "zone/")) {
-      result = self.handleZoneCall(method, paramMap, _arg, handled, session);
+      result = self.handleZoneCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "device/")) {
-      result = self.handleDeviceCall(method, paramMap, _arg, handled, session);
+      result = self.handleDeviceCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "circuit/")) {
-      result = self.handleCircuitCall(method, paramMap, _arg, handled, session);
+      result = self.handleCircuitCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "set/")) {
-      result = self.handleSetCall(method, paramMap, _arg, handled, session);
+      result = self.handleSetCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "property/")) {
-      result = self.handlePropertyCall(method, paramMap, _arg, handled, session);
+      result = self.handlePropertyCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "event/")) {
-      result = self.handleEventCall(method, paramMap, _arg, handled, session);
+      result = self.handleEventCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "structure/")) {
-      result = self.handleStructureCall(method, paramMap, _arg, handled, session);
+      result = self.handleStructureCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "sim/")) {
-      result = self.handleSimCall(method, paramMap, _arg, handled, session);
+      result = self.handleSimCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "debug/")) {
-      result = self.handleDebugCall(method, paramMap, _arg, handled, session);
+      result = self.handleDebugCall(method, paramMap, _connection, handled, session);
     } else if(beginsWith(method, "metering/")) {
-      result = self.handleMeteringCall(method, paramMap, _arg, handled, session);
+      result = self.handleMeteringCall(method, paramMap, _connection, handled, session);
     }
 
     if(!handled) {
-      emitHTTPHeader(404, _arg, "application/json");
+      emitHTTPHeader(404, _connection, "application/json");
       result = "{ ok: " + ToJSONValue(false) + ", message: " + ToJSONValue("Call to unknown function") + " }";
       self.log("Unknown function '" + method + "'", lsError);
     } else {
-      emitHTTPHeader(200, _arg, "application/json");
+      emitHTTPHeader(200, _connection, "application/json");
     }
-    shttpd_printf(_arg, result.c_str());
-    _arg->flags |= SHTTPD_END_OF_OUTPUT;
+    mg_write(_connection, result.c_str(), result.length());
   } // jsonHandler
 
-  void WebServer::httpBrowseProperties(struct shttpd_arg* _arg) {
-    emitHTTPHeader(200, _arg);
+  void WebServer::httpBrowseProperties(struct mg_connection* _connection,
+                                       const struct mg_request_info* _info, 
+                                       void* _userData) {
+    emitHTTPHeader(200, _connection);
 
     const string urlid = "/browse";
-    string uri = shttpd_get_env(_arg, "REQUEST_URI");
-    uri = urlDecode(uri);
-    HashMapConstStringString paramMap = parseParameter(shttpd_get_env(_arg, "QUERY_STRING"));
+    string uri = _info->uri;
+    HashMapConstStringString paramMap = parseParameter(_info->query_string);
 
     string path = uri.substr(uri.find(urlid) + urlid.size());
     if(path.empty()) {
       path = "/";
     }
-    shttpd_printf(_arg, "<html><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><body>");
+    mg_printf(_connection, "<html><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><body>");
 
     std::stringstream stream;
     stream << "<h1>" << path << "</h1>";
@@ -1794,9 +1799,8 @@
     }
 
     stream << "</ul></body></html>";
-    shttpd_printf(_arg, stream.str().c_str());
-
-    _arg->flags |= SHTTPD_END_OF_OUTPUT;
+    string tmp = stream.str();
+    mg_write(_connection, tmp.c_str(), tmp.length());
   } // httpBrowseProperties
 
 }

Modified: dss/trunk/core/webserver.h
===================================================================
--- dss/trunk/core/webserver.h	2009-09-03 09:34:09 UTC (rev 8728)
+++ dss/trunk/core/webserver.h	2009-09-03 09:48:59 UTC (rev 8729)
@@ -22,10 +22,9 @@
 #ifndef WEBSERVER_H_
 #define WEBSERVER_H_
 
-#include <shttpd/shttpd.h>
+#include <mongoose/mongoose.h>
 
 #include "base.h"
-#include "thread.h"
 #include "subsystem.h"
 #include "session.h"
 
@@ -42,40 +41,48 @@
 
   typedef boost::ptr_map<const int, Session> SessionByID;
 
-  class WebServer : public Subsystem,
-                    private Thread {
+  class WebServer : public Subsystem {
   private:
-    struct shttpd_ctx* m_SHttpdContext;
+    struct mg_context* m_mgContext;
     int m_LastSessionID;
     SessionByID m_Sessions;
     boost::ptr_vector<WebServerPlugin> m_Plugins;
   private:
-    virtual void execute();
     void setupAPI();
     void loadPlugin(PropertyNode& _node);
     void loadPlugins();
     std::string ResultToJSON(const bool _ok, const std::string& _message = "");
   protected:
     bool isDeviceInterfaceCall(const std::string& _method);
-    std::string callDeviceInterface(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, IDeviceInterface* _interface, Session* _session);
+    std::string callDeviceInterface(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, IDeviceInterface* _interface, Session* _session);
 
-    static void jsonHandler(struct shttpd_arg* _arg);
-    std::string handleApartmentCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleZoneCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleDeviceCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleSetCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handlePropertyCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleEventCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleCircuitCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleStructureCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleSimCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleDebugCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    std::string handleMeteringCall(const std::string& _method, HashMapConstStringString& _parameter, struct shttpd_arg* _arg, bool& _handled, Session* _session);
-    void pluginCalled(struct shttpd_arg* _arg, WebServerPlugin& plugin, const std::string& _uri);
+    std::string handleApartmentCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleZoneCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleDeviceCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleSetCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handlePropertyCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleEventCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleCircuitCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleStructureCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleSimCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleDebugCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    std::string handleMeteringCall(const std::string& _method, HashMapConstStringString& _parameter, struct mg_connection* _connection, bool& _handled, Session* _session);
+    void pluginCalled(struct mg_connection* _connection, 
+                      const struct mg_request_info* _info,
+                      WebServerPlugin& plugin, 
+                      const std::string& _uri);
 
-    static void httpPluginCallback(struct shttpd_arg* _arg);
-    static void httpBrowseProperties(struct shttpd_arg* _arg);
-    static void emitHTTPHeader(int _code, struct shttpd_arg* _arg, const std::string& _contentType = "text/html");
+    static void httpPluginCallback(struct mg_connection* _connection,
+                                   const struct mg_request_info* _info, 
+                                   void* _userData);
+    static void httpBrowseProperties(struct mg_connection* _connection,
+                                   const struct mg_request_info* _info, 
+                                   void* _userData);
+    static void jsonHandler(struct mg_connection* _connection,
+                            const struct mg_request_info* _info, 
+                            void* _userData);
+
+      static void emitHTTPHeader(int _code, struct mg_connection* _connection, const std::string& _contentType = "text/html");
   protected:
     virtual void doStart();
   public:

Modified: dss/trunk/external/CMakeLists.txt
===================================================================
--- dss/trunk/external/CMakeLists.txt	2009-09-03 09:34:09 UTC (rev 8728)
+++ dss/trunk/external/CMakeLists.txt	2009-09-03 09:48:59 UTC (rev 8729)
@@ -1,2 +1,2 @@
-add_subdirectory(shttpd)
+add_subdirectory(mongoose)
 add_subdirectory(slaves)

Added: dss/trunk/external/mongoose/CMakeLists.txt
===================================================================
--- dss/trunk/external/mongoose/CMakeLists.txt	                        (rev 0)
+++ dss/trunk/external/mongoose/CMakeLists.txt	2009-09-03 09:48:59 UTC (rev 8729)
@@ -0,0 +1 @@
+add_library(mongoose mongoose.c mongoose.h)

Added: dss/trunk/external/mongoose/mongoose.c
===================================================================
--- dss/trunk/external/mongoose/mongoose.c	                        (rev 0)
+++ dss/trunk/external/mongoose/mongoose.c	2009-09-03 09:48:59 UTC (rev 8729)
@@ -0,0 +1,4739 @@
+/*
+ * Copyright (c) 2004-2009 Sergey Lyubka
+ * Portions Copyright (c) 2009 Gilbert Wellisch
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * $Id$
+ */
+
+#if defined(_WIN32)
+#define _CRT_SECURE_NO_WARNINGS	/* Disable deprecation warning in VS2005 */
+#endif /* _WIN32 */
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* !_WIN32_WCE */
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#if defined(_WIN32)		/* Windows specific #includes and #defines */
+#define	_WIN32_WINNT	0x0400	/* To make it link in VS2005 */
+#include <windows.h>
+
+#ifndef _WIN32_WCE
+#include <process.h>
+#include <direct.h>
+#include <io.h>
+#else /* _WIN32_WCE */
+/* Windows CE-specific definitions */
+#include <winsock2.h>
+#define NO_CGI	/* WinCE has no pipes */
+#define NO_SSI	/* WinCE has no pipes */
+
+#define FILENAME_MAX	MAX_PATH
+#define BUFSIZ		4096
+typedef long off_t;
+
+#define errno			GetLastError()
+#define strerror(x)		_ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
+#endif /* _WIN32_WCE */
+
+#define EPOCH_DIFF	0x019DB1DED53E8000 /* 116444736000000000 nsecs */
+#define RATE_DIFF	10000000 /* 100 nsecs */
+#define MAKEUQUAD(lo, hi)	((uint64_t)(((uint32_t)(lo)) | \
+				((uint64_t)((uint32_t)(hi))) << 32))
+#define	SYS2UNIX_TIME(lo, hi) \
+	(time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
+
+/*
+ * Visual Studio 6 does not know __func__ or __FUNCTION__
+ * The rest of MS compilers use __FUNCTION__, not C99 __func__
+ * Also use _strtoui64 on modern M$ compilers
+ */
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#define	STRX(x)			#x
+#define	STR(x)			STRX(x)
+#define	__func__		"line " STR(__LINE__)
+#define	strtoull(x, y, z)	strtoul(x, y, z)
+#else
+#define	__func__		__FUNCTION__
+#define	strtoull(x, y, z)	_strtoui64(x, y, z)
+#endif /* _MSC_VER */
+
+#define	ERRNO			GetLastError()
+#define	NO_SOCKLEN_T
+#define	SSL_LIB			"ssleay32.dll"
+#define	CRYPTO_LIB		"libeay32.dll"
+#define	DIRSEP			'\\'
+#define	IS_DIRSEP_CHAR(c)	((c) == '/' || (c) == '\\')
+#define	O_NONBLOCK		0
+#define	EWOULDBLOCK		WSAEWOULDBLOCK
+#define	_POSIX_
+#define UINT64_FMT		"I64"
+
+#define	SHUT_WR			1
+#define	snprintf		_snprintf
+#define	vsnprintf		_vsnprintf
+#define	sleep(x)		Sleep((x) * 1000)
+
+#define	popen(x, y)		_popen(x, y)
+#define	pclose(x)		_pclose(x)
+#define	close(x)		_close(x)
+#define	dlsym(x,y)		GetProcAddress((HINSTANCE) (x), (y))
+#define	RTLD_LAZY		0
+#define	fseeko(x, y, z)		fseek((x), (y), (z))
+#define	fdopen(x, y)		_fdopen((x), (y))
+#define	write(x, y, z)		_write((x), (y), (unsigned) z)
+#define	read(x, y, z)		_read((x), (y), (unsigned) z)
+#define	flockfile(x)		(void) 0
+#define	funlockfile(x)		(void) 0
+
+#if !defined(fileno)
+#define	fileno(x)		_fileno(x)
+#endif /* !fileno MINGW #defines fileno */
+
+typedef HANDLE pthread_mutex_t;
+typedef HANDLE pthread_cond_t;
+typedef DWORD pthread_t;
+#define pid_t HANDLE	/* MINGW typedefs pid_t to int. Using #define here. */
+
+struct timespec {
+	long tv_nsec;
+	long tv_sec;
+};
+
+static int pthread_mutex_lock(pthread_mutex_t *);
+static int pthread_mutex_unlock(pthread_mutex_t *);
+
+#if defined(HAVE_STDINT)
+#include <stdint.h>
+#else
+typedef unsigned int		uint32_t;
+typedef unsigned short		uint16_t;
+#if _MSC_VER > 1200
+typedef unsigned __int64	uint64_t;
+#else
+/* VC6 cannot cast double to unsigned __int64, needed by print_dir_entry() */
+typedef __int64	uint64_t;
+#endif /* _MSC_VER */
+#endif /* HAVE_STDINT */
+
+/*
+ * POSIX dirent interface
+ */
+struct dirent {
+	char	d_name[FILENAME_MAX];
+};
+
+typedef struct DIR {
+	HANDLE			handle;
+	WIN32_FIND_DATAW	info;
+	struct dirent		result;
+} DIR;
+
+#else				/* UNIX  specific	*/
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/mman.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+
+#include <pwd.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <pthread.h>
+#define	SSL_LIB			"libssl.so"
+#define	CRYPTO_LIB		"libcrypto.so"
+#define	DIRSEP			'/'
+#define	IS_DIRSEP_CHAR(c)	((c) == '/')
+#define	O_BINARY		0
+#define	closesocket(a)		close(a)
+#define	mg_fopen(x, y)		fopen(x, y)
+#define	mg_mkdir(x, y)		mkdir(x, y)
+#define	mg_remove(x)		remove(x)
+#define	mg_rename(x, y)		rename(x, y)
+#define	ERRNO			errno
+#define	INVALID_SOCKET		(-1)
+#define UINT64_FMT		"ll"
+typedef int SOCKET;
+
+#endif /* End of Windows and UNIX specific includes */
+
+#include "mongoose.h"
+
+#define	MONGOOSE_VERSION	"2.9"
+#define	PASSWORDS_FILE_NAME	".htpasswd"
+#define	CGI_ENVIRONMENT_SIZE	4096
+#define	MAX_CGI_ENVIR_VARS	64
+#define	MAX_REQUEST_SIZE	8192
+#define	MAX_LISTENING_SOCKETS	10
+#define	MAX_CALLBACKS		20
+#define	ARRAY_SIZE(array)	(sizeof(array) / sizeof(array[0]))
+#define	UNKNOWN_CONTENT_LENGTH	((uint64_t) ~0)
+#define	DEBUG_MGS_PREFIX	"*** Mongoose debug *** "
+
+#if defined(DEBUG)
+#define	DEBUG_TRACE(x) do {printf x; putchar('\n'); fflush(stdout);} while (0)
+#else
+#define DEBUG_TRACE(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 */
+
+#if !defined(FALSE)
+enum {FALSE, TRUE};
+#endif /* !FALSE */
+
+typedef int bool_t;
+typedef void * (*mg_thread_func_t)(void *);
+
+static const char *http_500_error = "Internal Server Error";
+
+/*
+ * Snatched from OpenSSL includes. I put the prototypes here to be independent
+ * from the OpenSSL source installation. Having this, mongoose + SSL can be
+ * built on any system with binary SSL libraries installed.
+ */
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+
+#define	SSL_ERROR_WANT_READ	2
+#define	SSL_ERROR_WANT_WRITE	3
+#define SSL_FILETYPE_PEM	1
+#define	CRYPTO_LOCK		1
+
+/*
+ * Dynamically loaded SSL functionality
+ */
+struct ssl_func {
+	const char	*name;		/* SSL function name	*/
+	void		(*ptr)(void);	/* Function pointer	*/
+};
+
+#define	SSL_free(x)	(* (void (*)(SSL *)) ssl_sw[0].ptr)(x)
+#define	SSL_accept(x)	(* (int (*)(SSL *)) ssl_sw[1].ptr)(x)
+#define	SSL_connect(x)	(* (int (*)(SSL *)) ssl_sw[2].ptr)(x)
+#define	SSL_read(x,y,z)	(* (int (*)(SSL *, void *, int)) 		\
+				ssl_sw[3].ptr)((x),(y),(z))
+#define	SSL_write(x,y,z) (* (int (*)(SSL *, const void *,int))		\
+				ssl_sw[4].ptr)((x), (y), (z))
+#define	SSL_get_error(x,y)(* (int (*)(SSL *, int)) ssl_sw[5])((x), (y))
+#define	SSL_set_fd(x,y)	(* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)((x), (y))
+#define	SSL_new(x)	(* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)(x)
+#define	SSL_CTX_new(x)	(* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)(x)
+#define	SSLv23_server_method()	(* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)()
+#define	SSL_library_init() (* (int (*)(void)) ssl_sw[10].ptr)()
+#define	SSL_CTX_use_PrivateKey_file(x,y,z)	(* (int (*)(SSL_CTX *, \
+		const char *, int)) ssl_sw[11].ptr)((x), (y), (z))
+#define	SSL_CTX_use_certificate_file(x,y,z)	(* (int (*)(SSL_CTX *, \
+		const char *, int)) ssl_sw[12].ptr)((x), (y), (z))
+#define SSL_CTX_set_default_passwd_cb(x,y) \
+	(* (void (*)(SSL_CTX *, mg_spcb_t)) ssl_sw[13].ptr)((x),(y))
+#define SSL_CTX_free(x) (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)(x)
+
+#define CRYPTO_num_locks() (* (int (*)(void)) crypto_sw[0].ptr)()
+#define CRYPTO_set_locking_callback(x)					\
+		(* (void (*)(void (*)(int, int, const char *, int)))	\
+	 	crypto_sw[1].ptr)(x)
+#define CRYPTO_set_id_callback(x)					\
+	(* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)(x)
+
+/*
+ * set_ssl_option() function when called, updates this array.
+ * It loads SSL library dynamically and changes NULLs to the actual addresses
+ * of respective functions. The macros above (like SSL_connect()) are really
+ * just calling these functions indirectly via the pointer.
+ */
+static struct ssl_func	ssl_sw[] = {
+	{"SSL_free",			NULL},
+	{"SSL_accept",			NULL},
+	{"SSL_connect",			NULL},
+	{"SSL_read",			NULL},
+	{"SSL_write",			NULL},
+	{"SSL_get_error",		NULL},
+	{"SSL_set_fd",			NULL},
+	{"SSL_new",			NULL},
+	{"SSL_CTX_new",			NULL},
+	{"SSLv23_server_method",	NULL},
+	{"SSL_library_init",		NULL},
+	{"SSL_CTX_use_PrivateKey_file",	NULL},
+	{"SSL_CTX_use_certificate_file",NULL},
+	{"SSL_CTX_set_default_passwd_cb",NULL},
+	{"SSL_CTX_free",		NULL},
+	{NULL,				NULL}
+};
+
+/*
+ * Similar array as ssl_sw. These functions are located in different lib.
+ */
+static struct ssl_func	crypto_sw[] = {
+	{"CRYPTO_num_locks",		NULL},
+	{"CRYPTO_set_locking_callback",	NULL},
+	{"CRYPTO_set_id_callback",	NULL},
+	{NULL,				NULL}
+};
+
+/*
+ * Month names
+ */
+static const char *month_names[] = {
+	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/*
+ * Unified socket address. For IPv6 support, add IPv6 address structure
+ * in the union u.
+ */
+struct usa {
+	socklen_t len;
+	union {
+		struct sockaddr	sa;
+		struct sockaddr_in sin;
+	} u;
+};
+
+/*
+ * Specifies a string (chunk of memory).
+ * Used to traverse comma separated lists of options.
+ */
+struct vec {
+	const char	*ptr;
+	size_t		len;
+};
+
+/*
+ * Structure used by mg_stat() function. Uses 64 bit file length.
+ */
+struct mgstat {
+	bool_t		is_directory;	/* Directory marker		*/
+	uint64_t	size;		/* File size			*/
+	time_t		mtime;		/* Modification time		*/
+};
+
+struct mg_option {
+	const char	*name;
+	const char	*description;
+	const char	*default_value;
+	int		index;
+	bool_t (*setter)(struct mg_context *, const char *);
+};
+
+/*
+ * Numeric indexes for the option values in context, ctx->options
+ */
+enum mg_option_index {
+	OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST, OPT_CGI_EXTENSIONS,
+	OPT_CGI_INTERPRETER, OPT_CGI_ENV, OPT_SSI_EXTENSIONS, OPT_AUTH_DOMAIN,
+	OPT_AUTH_GPASSWD, OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG,
+	OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_UID, OPT_PROTECT,
+	OPT_SERVICE, OPT_HIDE, OPT_ADMIN_URI, OPT_MAX_THREADS, OPT_IDLE_TIME,
+	OPT_MIME_TYPES,
+	NUM_OPTIONS
+};
+
+/*
+ * Structure used to describe listening socket, or socket which was
+ * accept()-ed by the master thread and queued for future handling
+ * by the worker thread.
+ */
+struct socket {
+	SOCKET		sock;		/* Listening socket		*/
+	struct usa	lsa;		/* Local socket address		*/
+	struct usa	rsa;		/* Remote socket address	*/
+	bool_t		is_ssl;		/* Is socket SSL-ed		*/
+};
+
+/*
+ * Callback function, and where it is bound to
+ */
+struct callback {
+	char		*uri_regex;	/* URI regex to handle		*/
+	mg_callback_t	func;		/* user callback		*/
+	bool_t		is_auth;	/* func is auth checker		*/
+	int		status_code;	/* error code to handle		*/
+	void		*user_data;	/* opaque user data		*/
+};
+
+/*
+ * Mongoose context
+ */
+struct mg_context {
+	int		stop_flag;	/* Should we stop event loop	*/
+	SSL_CTX		*ssl_ctx;	/* SSL context			*/
+
+	FILE		*access_log;	/* Opened access log		*/
+	FILE		*error_log;	/* Opened error log		*/
+
+	struct socket	listeners[MAX_LISTENING_SOCKETS];
+	int		num_listeners;
+
+	struct callback	callbacks[MAX_CALLBACKS];
+	int		num_callbacks;
+
+	char		*options[NUM_OPTIONS];	/* Configured opions	*/
+	pthread_mutex_t	opt_mutex[NUM_OPTIONS];	/* Option protector	*/
+
+	int		max_threads;	/* Maximum number of threads	*/
+	int		num_threads;	/* Number of threads		*/
+	int		num_idle;	/* Number of idle threads	*/
+	pthread_mutex_t	thr_mutex;	/* Protects (max|num)_threads	*/
+	pthread_cond_t	thr_cond;
+	pthread_mutex_t	bind_mutex;	/* Protects bind operations	*/
+
+	struct socket	queue[20];	/* Accepted sockets		*/
+	int		sq_head;	/* Head of the socket queue	*/
+	int		sq_tail;	/* Tail of the socket queue	*/
+	pthread_cond_t	empty_cond;	/* Socket queue empty condvar	*/
+	pthread_cond_t	full_cond;	/* Socket queue full condvar	*/
+
+	mg_spcb_t	ssl_password_callback;
+	mg_callback_t	log_callback;
+};
+
+/*
+ * Client connection.
+ */
+struct mg_connection {
+	struct mg_request_info	request_info;
+	struct mg_context *ctx;		/* Mongoose context we belong to*/
+	SSL		*ssl;		/* SSL descriptor		*/
+	struct socket	client;		/* Connected client		*/
+	time_t		birth_time;	/* Time connection was accepted	*/
+	bool_t		free_post_data;	/* post_data was malloc-ed	*/
+	bool_t		embedded_auth;	/* Used for authorization	*/
+	uint64_t	num_bytes_sent;	/* Total bytes sent to client	*/
+};
+
+/*
+ * Print error message to the opened error log stream.
+ */
+static void
+cry(struct mg_connection *conn, const char *fmt, ...)
+{
+	char	buf[BUFSIZ];
+	va_list	ap;
+
+	va_start(ap, fmt);
+	(void) vsnprintf(buf, sizeof(buf), fmt, ap);
+	conn->ctx->log_callback(conn, &conn->request_info, buf);
+	va_end(ap);
+}
+
+/*
+ * Return fake connection structure. Used for logging, if connection
+ * is not applicable at the moment of logging.
+ */
+static struct mg_connection *
+fc(struct mg_context *ctx)
+{
+	static struct mg_connection fake_connection;
+	fake_connection.ctx = ctx;
+	return (&fake_connection);
+}
+
+/*
+ * If an embedded code does not intercept logging by calling
+ * mg_set_log_callback(), this function is used for logging. It prints
+ * stuff to the conn->error_log, which is stderr unless "error_log"
+ * option was set.
+ */
+static void
+builtin_error_log(struct mg_connection *conn,
+		const struct mg_request_info *request_info, void *message)
+{
+	FILE	*fp;
+	time_t	timestamp;
+
+	fp = conn->ctx->error_log;
+	flockfile(fp);
+
+	timestamp = time(NULL);
+
+	(void) fprintf(fp,
+	    "[%010lu] [error] [client %s] ",
+	    (unsigned long) timestamp,
+	    inet_ntoa(conn->client.rsa.u.sin.sin_addr));
+
+	if (request_info->request_method != NULL)
+		(void) fprintf(fp, "%s %s: ",
+		    request_info->request_method,
+		    request_info->uri);
+
+	(void) fprintf(fp, "%s", (char *) message);
+
+	fputc('\n', fp);
+
+	funlockfile(fp);
+}
+
+const char *
+mg_version(void)
+{
+	return (MONGOOSE_VERSION);
+}
+
+static void
+mg_strlcpy(register char *dst, register const char *src, size_t n)
+{
+	for (; *src != '\0' && n > 1; n--)
+		*dst++ = *src++;
+	*dst = '\0';
+}
+
+static int
+lowercase(const char *s)
+{
+	return (tolower(* (unsigned char *) s));
+}
+
+static int
+mg_strncasecmp(const char *s1, const char *s2, size_t len)
+{
+	int	diff = 0;
+
+	if (len > 0)
+		do {
+			diff = lowercase(s1++) - lowercase(s2++);
+		} while (diff == 0 && s1[-1] != '\0' && --len > 0);
+
+	return (diff);
+}
+
+static int
+mg_strcasecmp(const char *s1, const char *s2)
+{
+	int	diff;
+
+	do {
+		diff = lowercase(s1++) - lowercase(s2++);
+	} while (diff == 0 && s1[-1] != '\0');
+
+	return (diff);
+}
+
+static char *
+mg_strndup(const char *ptr, size_t len)
+{
+	char	*p;
+
+	if ((p = (char *) malloc(len + 1)) != NULL)
+		mg_strlcpy(p, ptr, len + 1);
+
+	return (p);
+
+}
+
+static char *
+mg_strdup(const char *str)
+{
+	return (mg_strndup(str, strlen(str)));
+}
+
+/*
+ * Like snprintf(), but never returns negative value, or the value
+ * that is larger than a supplied buffer.
+ * Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
+ * in his audit report.
+ */
+static int
+mg_vsnprintf(struct mg_connection *conn,
+		char *buf, size_t buflen, const char *fmt, va_list ap)
+{
+	int	n;
+
+	if (buflen == 0)
+		return (0);
+
+	n = vsnprintf(buf, buflen, fmt, ap);
+
+	if (n < 0) {
+		cry(conn, "vsnprintf error");
+		n = 0;
+	} else if (n >= (int) buflen) {
+		cry(conn, "truncating vsnprintf buffer: [%.*s]",
+		    n > 200 ? 200 : n, buf);
+		n = (int) buflen - 1;
+	}
+	buf[n] = '\0';
+
+	return (n);
+}
+
+static int
+mg_snprintf(struct mg_connection *conn,
+		char *buf, size_t buflen, const char *fmt, ...)
+{
+	va_list	ap;
+	int	n;
+
+	va_start(ap, fmt);
+	n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
+	va_end(ap);
+
+	return (n);
+}
+
+/*
+ * Convert string representing a boolean value to a boolean value
+ */
+static bool_t
+is_true(const char *str)
+{
+	static const char	*trues[] = {"1", "yes", "true", "ja", NULL};
+	int			i;
+
+	for (i = 0; trues[i] != NULL; i++)
+		if (str != NULL && mg_strcasecmp(str, trues[i]) == 0)
+			return (TRUE);
+
+	return (FALSE);
+}
+
+/*
+ * Skip the characters until one of the delimiters characters found.
+ * 0-terminate resulting word. Skip the rest of the delimiters if any.
+ * Advance pointer to buffer to the next word. Return found 0-terminated word.
+ */
+static char *
+skip(char **buf, const char *delimiters)
+{
+	char	*p, *begin_word, *end_word, *end_delimiters;
+
+	begin_word = *buf;
+	end_word = begin_word + strcspn(begin_word, delimiters);
+	end_delimiters = end_word + strspn(end_word, delimiters);
+
+	for (p = end_word; p < end_delimiters; p++)
+		*p = '\0';
+
+	*buf = end_delimiters;
+
+	return (begin_word);
+}
+
+/*
+ * Return HTTP header value, or NULL if not found.
+ */
+static const char *
+get_header(const struct mg_request_info *ri, const char *name)
+{
+	int	i;
+
+	for (i = 0; i < ri->num_headers; i++)
+		if (!mg_strcasecmp(name, ri->http_headers[i].name))
+			return (ri->http_headers[i].value);
+
+	return (NULL);
+}
+
+const char *
+mg_get_header(const struct mg_connection *conn, const char *name)
+{
+	return (get_header(&conn->request_info, name));
+}
+
+/*
+ * A helper function for traversing comma separated list of values.
+ * It returns a list pointer shifted to the next value, of NULL if the end
+ * of the list found.
+ * Value is stored in val vector. If value has form "x=y", then eq_val
+ * vector is initialized to point to the "y" part, and val vector length
+ * is adjusted to point only to "x".
+ */
+static const char *
+next_option(const char *list, struct vec *val, struct vec *eq_val)
+{
+	if (list == NULL || *list == '\0') {
+		/* End of the list */
+		list = NULL;
+	} else {
+		val->ptr = list;
+		if ((list = strchr(val->ptr, ',')) != NULL) {
+			/* Comma found. Store length and shift the list ptr */
+			val->len = list - val->ptr;
+			list++;
+		} else {
+			/* This value is the last one */
+			list = val->ptr + strlen(val->ptr);
+			val->len = list - val->ptr;
+		}
+
+		if (eq_val != NULL) {
+			/*
+			 * Value has form "x=y", adjust pointers and lengths
+			 * so that val points to "x", and eq_val points to "y".
+			 */
+			eq_val->len = 0;
+			eq_val->ptr = memchr(val->ptr, '=', val->len);
+			if (eq_val->ptr != NULL) {
+				eq_val->ptr++;  /* Skip over '=' character */
+				eq_val->len = val->ptr + val->len - eq_val->ptr;
+				val->len = (eq_val->ptr - val->ptr) - 1;
+			}
+		}
+	}
+
+	return (list);
+}
+
+#if !(defined(NO_CGI) && defined(NO_SSI))
+/*
+ * Verify that given file has certain extension
+ */
+static bool_t
+match_extension(const char *path, const char *ext_list)
+{
+	struct vec	ext_vec;
+	size_t		path_len;
+
+	path_len = strlen(path);
+
+	while ((ext_list = next_option(ext_list, &ext_vec, NULL)) != NULL)
+		if (ext_vec.len < path_len &&
+		    mg_strncasecmp(path + path_len - ext_vec.len,
+			    ext_vec.ptr, ext_vec.len) == 0)
+			return (TRUE);
+
+	return (FALSE);
+}
+#endif /* !(NO_CGI && NO_SSI) */
+
+/*
+ * Return TRUE if "uri" matches "regexp".
+ * '*' in the regexp means zero or more characters.
+ */
+static bool_t
+match_regex(const char *uri, const char *regexp)
+{
+	if (*regexp == '\0')
+		return (*uri == '\0');
+
+	if (*regexp == '*')
+		do {
+			if (match_regex(uri, regexp + 1))
+				return (TRUE);
+		} while (*uri++ != '\0');
+
+	if (*uri != '\0' && *regexp == *uri)
+		return (match_regex(uri + 1, regexp + 1));
+
+	return (FALSE);
+}
+
+static const struct callback *
+find_callback(struct mg_context *ctx, bool_t is_auth,
+		const char *uri, int status_code)
+{
+	const struct callback	*cb, *found;
+	int			i;
+
+	found = NULL;
+	pthread_mutex_lock(&ctx->bind_mutex);
+	for (i = 0; i < ctx->num_callbacks; i++) {
+		cb = ctx->callbacks + i;
+		if ((uri != NULL && cb->uri_regex != NULL &&
+		    ((is_auth && cb->is_auth) || (!is_auth && !cb->is_auth)) &&
+		    match_regex(uri, cb->uri_regex)) || (uri == NULL &&
+		     (cb->status_code == 0 ||
+		      cb->status_code == status_code))) {
+			found = cb;
+			break;
+		}
+	}
+	pthread_mutex_unlock(&ctx->bind_mutex);
+
+	return (found);
+}
+
+/*
+ * For use by external application. This sets custom logging function.
+ */
+void
+mg_set_log_callback(struct mg_context *ctx, mg_callback_t log_callback)
+{
+	/* If NULL is specified as a callback, revert back to the default */
+	if (log_callback == NULL)
+		ctx->log_callback = &builtin_error_log;
+	else
+		ctx->log_callback = log_callback;
+}
+
+/*
+ * Send error message back to the client.
+ */
+static void
+send_error(struct mg_connection *conn, int status, const char *reason,
+		const char *fmt, ...)
+{
+	const struct callback	*cb;
+	char		buf[BUFSIZ];
+	va_list		ap;
+	int		len;
+
+	conn->request_info.status_code = status;
+
+	/* If error handler is set, call it. Otherwise, send error message */
+	if ((cb = find_callback(conn->ctx, FALSE, NULL, status)) != NULL) {
+		cb->func(conn, &conn->request_info, cb->user_data);
+	} else {
+		buf[0] = '\0';
+		len = 0;
+
+		/* Errors 1xx, 204 and 304 MUST NOT send a body */
+		if (status > 199 && status != 204 && status != 304) {
+			len = mg_snprintf(conn, buf, sizeof(buf),
+			    "Error %d: %s\n", status, reason);
+			cry(conn, "%s", buf);
+
+			va_start(ap, fmt);
+			len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len,
+			    fmt, ap);
+			va_end(ap);
+			conn->num_bytes_sent = len;
+		}
+
+		(void) mg_printf(conn,
+		    "HTTP/1.1 %d %s\r\n"
+		    "Content-Type: text/plain\r\n"
+		    "Content-Length: %d\r\n"
+		    "Connection: close\r\n"
+		    "\r\n%s", status, reason, len, buf);
+	}
+}
+
+#ifdef _WIN32
+static int
+pthread_mutex_init(pthread_mutex_t *mutex, void *unused)
+{
+	unused = NULL;
+	*mutex = CreateMutex(NULL, FALSE, NULL);
+	return (*mutex == NULL ? -1 : 0);
+}
+
+static int
+pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+	return (CloseHandle(*mutex) == 0 ? -1 : 0);
+}
+
+static int
+pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+	return (WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1);
+}
+
+static int
+pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+	return (ReleaseMutex(*mutex) == 0 ? -1 : 0);
+}
+
+static int
+pthread_cond_init(pthread_cond_t *cv, const void *unused)
+{
+	unused = NULL;
+	*cv = CreateEvent(NULL, FALSE, FALSE, NULL);
+	return (*cv == NULL ? -1 : 0);
+}
+
+static int
+pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mutex,
+	const struct timespec *ts)
+{
+	DWORD	status;
+	DWORD	msec = INFINITE;
+	time_t	now;
+	
+	if (ts != NULL) {
+		now = time(NULL);
+		msec = 1000 * (now > ts->tv_sec ? 0 : ts->tv_sec - now);
+	}
+
+	(void) ReleaseMutex(*mutex);
+	status = WaitForSingleObject(*cv, msec);
+	(void) WaitForSingleObject(*mutex, INFINITE);
+	
+	return (status == WAIT_OBJECT_0 ? 0 : -1);
+}
+
+static int
+pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
+{
+	return (pthread_cond_timedwait(cv, mutex, NULL));
+}
+
+static int
+pthread_cond_signal(pthread_cond_t *cv)
+{
+	return (SetEvent(*cv) == 0 ? -1 : 0);
+}
+
+static int
+pthread_cond_destroy(pthread_cond_t *cv)
+{
+	return (CloseHandle(*cv) == 0 ? -1 : 0);
+}
+
+static pthread_t
+pthread_self(void)
+{
+	return (GetCurrentThreadId());
+}
+
+/*
+ * Change all slashes to backslashes. It is Windows.
+ */
+static void
+fix_directory_separators(char *path)
+{
+	int	i;
+
+	for (i = 0; path[i] != '\0'; i++) {
+		if (path[i] == '/')
+			path[i] = '\\';
+		/* i > 0 check is to preserve UNC paths, \\server\file.txt */
+		if (path[i] == '\\' && i > 0)
+			while (path[i + 1] == '\\' || path[i + 1] == '/')
+				(void) memmove(path + i + 1,
+				    path + i + 2, strlen(path + i + 1));
+	}
+}
+
+/*
+ * Encode 'path' which is assumed UTF-8 string, into UNICODE string.
+ * wbuf and wbuf_len is a target buffer and its length.
+ */
+static void
+to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len)
+{
+	char	buf[FILENAME_MAX], *p;
+
+	mg_strlcpy(buf, path, sizeof(buf));
+	fix_directory_separators(buf);
+
+	/* Point p to the end of the file name */
+	p = buf + strlen(buf) - 1;
+
+	/* Trim trailing backslash character */
+	while (p > buf && *p == '\\' && p[-1] != ':')
+		*p-- = '\0';
+
+	/*
+	 * Protect from CGI code disclosure.
+	 * 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!
+	 */
+	if (*p == 0x20 ||               /* No space at the end */
+	    (*p == 0x2e && p > buf) ||  /* No '.' but allow '.' as full path */
+	    *p == 0x2b ||               /* No '+' */
+	    (*p & ~0x7f)) {             /* And generally no non-ascii chars */
+		(void) fprintf(stderr, "Rejecting suspicious path: [%s]", buf);
+		buf[0] = '\0';
+	}
+
+	(void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
+}
+
+#if defined(_WIN32_WCE)
+
+static time_t
+time(time_t *ptime)
+{
+	time_t		t;
+	SYSTEMTIME	st;
+	FILETIME	ft;
+
+	GetSystemTime(&st);
+	SystemTimeToFileTime(&st, &ft);
+	t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
+
+	if (ptime != NULL)
+		*ptime = t;
+
+	return (t);
+}
+
+static time_t
+mktime(struct tm *ptm)
+{
+	SYSTEMTIME	st;
+	FILETIME	ft, lft;
+
+	st.wYear = ptm->tm_year + 1900;
+	st.wMonth = ptm->tm_mon + 1;
+	st.wDay = ptm->tm_mday;
+	st.wHour = ptm->tm_hour;
+	st.wMinute = ptm->tm_min;
+	st.wSecond = ptm->tm_sec;
+	st.wMilliseconds = 0;
+
+	SystemTimeToFileTime(&st, &ft);
+	LocalFileTimeToFileTime(&ft, &lft);
+	return (time_t)((MAKEUQUAD(lft.dwLowDateTime, lft.dwHighDateTime) -
+	    EPOCH_DIFF) / RATE_DIFF);
+}
+
+static struct tm *
+localtime(const time_t *ptime, struct tm *ptm)
+{
+	uint64_t	t = ((uint64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
+	FILETIME	ft, lft;
+	SYSTEMTIME	st;
+	TIME_ZONE_INFORMATION	tzinfo;
+
+	if (ptm == NULL)
+		return NULL;
+
+	* (uint64_t *) &ft = t;
+	FileTimeToLocalFileTime(&ft, &lft);
+	FileTimeToSystemTime(&lft, &st);
+	ptm->tm_year = st.wYear - 1900;
+	ptm->tm_mon = st.wMonth - 1;
+	ptm->tm_wday = st.wDayOfWeek;
+	ptm->tm_mday = st.wDay;
+	ptm->tm_hour = st.wHour;
+	ptm->tm_min = st.wMinute;
+	ptm->tm_sec = st.wSecond;
+	ptm->tm_yday = 0; // hope nobody uses this
+	ptm->tm_isdst = ((GetTimeZoneInformation(&tzinfo) ==
+	    TIME_ZONE_ID_DAYLIGHT) ? 1 : 0);
+
+	return ptm;
+}
+
+static size_t
+strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm)
+{
+	(void) snprintf(dst, dst_size, "implement strftime() for WinCE");
+	return (0);
+}	
+#endif
+
+static int
+mg_rename(const char* oldname, const char* newname)
+{
+	wchar_t	woldbuf[FILENAME_MAX];
+	wchar_t	wnewbuf[FILENAME_MAX];
+
+	to_unicode(oldname, woldbuf, ARRAY_SIZE(woldbuf));
+	to_unicode(newname, wnewbuf, ARRAY_SIZE(wnewbuf));
+
+	return (MoveFileW(woldbuf, wnewbuf) ? 0 : -1);
+}
+
+
+static FILE *
+mg_fopen(const char *path, const char *mode)
+{
+	wchar_t	wbuf[FILENAME_MAX], wmode[20];
+
+	to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+	MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
+
+	return (_wfopen(wbuf, wmode));
+}
+
+static int
+mg_stat(const char *path, struct mgstat *stp)
+{
+	int				ok = -1; /* Error */
+	wchar_t				wbuf[FILENAME_MAX];
+	WIN32_FILE_ATTRIBUTE_DATA	info;
+
+	to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+
+	if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
+		stp->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
+		stp->mtime = SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
+		    info.ftLastWriteTime.dwHighDateTime);
+		stp->is_directory =
+		    info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+		ok = 0;  /* Success */
+	}
+
+	return (ok);
+}
+
+static int
+mg_remove(const char *path)
+{
+	wchar_t	wbuf[FILENAME_MAX];
+
+	to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+
+	return (DeleteFileW(wbuf) ? 0 : -1);
+}
+
+static int
+mg_mkdir(const char *path, int mode)
+{
+	char	buf[FILENAME_MAX];
+	wchar_t	wbuf[FILENAME_MAX];
+
+	mode = 0; /* Unused */
+	mg_strlcpy(buf, path, sizeof(buf));
+	fix_directory_separators(buf);
+
+	(void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
+
+	return (CreateDirectoryW(wbuf, NULL) ? 0 : -1);
+}
+
+/*
+ * Implementation of POSIX opendir/closedir/readdir for Windows.
+ */
+static DIR *
+opendir(const char *name)
+{
+	DIR	*dir = NULL;
+	wchar_t	wpath[FILENAME_MAX];
+	DWORD attrs;
+
+	if (name == NULL) {
+		SetLastError(ERROR_BAD_ARGUMENTS);
+	} else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
+		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+	} else {
+		to_unicode(name, wpath, ARRAY_SIZE(wpath));
+		attrs = GetFileAttributesW(wpath);
+		if (attrs != 0xFFFFFFFF &&
+		    ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
+			(void) wcscat(wpath, L"\\*");
+			dir->handle = FindFirstFileW(wpath, &dir->info);
+			dir->result.d_name[0] = '\0';
+		} else {
+			free(dir);
+			dir = NULL;
+		}
+	}
+
+	return (dir);
+}
+
+static int
+closedir(DIR *dir)
+{
+	int result = 0;
+
+	if (dir != NULL) {
+		if (dir->handle != INVALID_HANDLE_VALUE)
+			result = FindClose(dir->handle) ? 0 : -1;
+
+		free(dir);
+	} else {
+		result = -1;
+		SetLastError(ERROR_BAD_ARGUMENTS);
+	}
+
+	return (result);
+}
+
+struct dirent *
+readdir(DIR *dir)
+{
+	struct dirent *result = 0;
+
+	if (dir) {
+		if (dir->handle != INVALID_HANDLE_VALUE) {
+			result = &dir->result;
+			(void) WideCharToMultiByte(CP_UTF8, 0,
+			    dir->info.cFileName, -1, result->d_name,
+			    sizeof(result->d_name), NULL, NULL);
+
+			if (!FindNextFileW(dir->handle, &dir->info)) {
+				(void) FindClose(dir->handle);
+				dir->handle = INVALID_HANDLE_VALUE;
+			}
+
+		} else {
+			SetLastError(ERROR_FILE_NOT_FOUND);
+		}
+	} else {
+		SetLastError(ERROR_BAD_ARGUMENTS);
+	}
+
+	return (result);
+}
+
+#define	set_close_on_exec(fd)	/* No FD_CLOEXEC on Windows */
+
+static int
+start_thread(struct mg_context *ctx, mg_thread_func_t func, void *param)
+{
+	HANDLE	hThread;
+
+	ctx = NULL;	/* Unused */
+	
+	hThread = CreateThread(NULL, 0,
+	    (LPTHREAD_START_ROUTINE) func, param, 0, NULL);
+
+	if (hThread != NULL)
+		(void) CloseHandle(hThread);
+
+	return (hThread == NULL ? -1 : 0);
+}
+
+static HANDLE
+dlopen(const char *dll_name, int flags)
+{
+	wchar_t	wbuf[FILENAME_MAX];
+
+	flags = 0; /* Unused */
+	to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
+
+	return (LoadLibraryW(wbuf));
+}
+
+#if !defined(NO_CGI)
+static int
+kill(pid_t pid, int sig_num)
+{
+	(void) TerminateProcess(pid, sig_num);
+	(void) CloseHandle(pid);
+	return (0);
+}
+
+static pid_t
+spawn_process(struct mg_connection *conn, const char *prog, char *envblk,
+		char *envp[], int fd_stdin, int fd_stdout, const char *dir)
+{
+	HANDLE	me;
+	char	*p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX];
+	FILE	*fp;
+	STARTUPINFOA		si;
+	PROCESS_INFORMATION	pi;
+
+	envp = NULL; /* Unused */
+
+	(void) memset(&si, 0, sizeof(si));
+	(void) memset(&pi, 0, sizeof(pi));
+
+	/* XXX redirect CGI errors to the error log file */
+	si.cb		= sizeof(si);
+	si.dwFlags	= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+	si.wShowWindow	= SW_HIDE;
+
+	me = GetCurrentProcess();
+	(void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,
+	    &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
+	(void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me,
+	    &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
+
+	/* If CGI file is a script, try to read the interpreter line */
+	interp = conn->ctx->options[OPT_CGI_INTERPRETER];
+	if (interp == NULL) {
+		line[2] = '\0';
+		(void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%c%s",
+		    dir, DIRSEP, prog);
+		if ((fp = fopen(cmdline, "r")) != NULL) {
+			(void) fgets(line, sizeof(line), fp);
+			if (memcmp(line, "#!", 2) != 0)
+				line[2] = '\0';
+			/* Trim whitespaces from interpreter name */
+			for (p = &line[strlen(line) - 1]; p > line &&
+			    isspace(*p); p--)
+				*p = '\0';
+			(void) fclose(fp);
+		}
+		interp = line + 2;
+	}
+
+	if ((p = (char *) strrchr(prog, '/')) != NULL)
+		prog = p + 1;
+
+	(void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s",
+	    interp, interp[0] == '\0' ? "" : " ", prog);
+
+	(void) mg_snprintf(conn, line, sizeof(line), "%s", dir);
+	fix_directory_separators(line);
+
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: Running [%s]", __func__, cmdline));
+	if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
+	    CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
+		cry(conn, "%s: CreateProcess(%s): %d",
+		    __func__, cmdline, ERRNO);
+		pi.hProcess = (pid_t) -1;
+	} else {
+		(void) close(fd_stdin);
+		(void) close(fd_stdout);
+	}
+
+	(void) CloseHandle(si.hStdOutput);
+	(void) CloseHandle(si.hStdInput);
+	(void) CloseHandle(pi.hThread);
+
+	return ((pid_t) pi.hProcess);
+}
+
+static int
+pipe(int *fds)
+{
+	return (_pipe(fds, BUFSIZ, _O_BINARY));
+}
+#endif /* !NO_CGI */
+
+static int
+set_non_blocking_mode(struct mg_connection *conn, SOCKET sock)
+{
+	unsigned long	on = 1;
+
+	conn = NULL; /* unused */
+	return (ioctlsocket(sock, FIONBIO, &on));
+}
+
+#else
+
+static int
+mg_stat(const char *path, struct mgstat *stp)
+{
+	struct stat	st;
+	int		ok;
+
+	if (stat(path, &st) == 0) {
+		ok = 0;
+		stp->size = st.st_size;
+		stp->mtime = st.st_mtime;
+		stp->is_directory = S_ISDIR(st.st_mode);
+	} else {
+		ok = -1;
+	}
+
+	return (ok);
+}
+
+static void
+set_close_on_exec(int fd)
+{
+	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+static int
+start_thread(struct mg_context *ctx, mg_thread_func_t func, void *param)
+{
+	pthread_t	thread_id;
+	pthread_attr_t	attr;
+	int		retval;
+
+	(void) pthread_attr_init(&attr);
+	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+	if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0)
+		cry(fc(ctx), "%s: %s", __func__, strerror(retval));
+
+	return (retval);
+}
+
+#ifndef NO_CGI
+static pid_t
+spawn_process(struct mg_connection *conn, const char *prog, char *envblk,
+		char *envp[], int fd_stdin, int fd_stdout, const char *dir)
+{
+	pid_t		pid;
+	const char	*interp;
+
+	envblk = NULL;	/* unused */
+
+	if ((pid = fork()) == -1) {
+		/* Parent */
+		send_error(conn, 500, http_500_error,
+		    "fork(): %s", strerror(ERRNO));
+	} else if (pid == 0) {
+		/* Child */
+		if (chdir(dir) != 0) {
+			cry(conn, "%s: chdir(%s): %s",
+			    __func__, dir, strerror(ERRNO));
+		} else if (dup2(fd_stdin, 0) == -1) {
+			cry(conn, "%s: dup2(stdin, %d): %s",
+			    __func__, fd_stdin, strerror(ERRNO));
+		} else if (dup2(fd_stdout, 1) == -1) {
+			cry(conn, "%s: dup2(stdout, %d): %s",
+			    __func__, fd_stdout, strerror(ERRNO));
+		} else {
+			/* If error file is specified, send errors there */
+			if (conn->ctx->error_log != NULL)
+				(void) dup2(fileno(conn->ctx->error_log), 2);
+
+			(void) close(fd_stdin);
+			(void) close(fd_stdout);
+
+			/* Execute CGI program */
+			interp = conn->ctx->options[OPT_CGI_INTERPRETER];
+			if (interp == NULL) {
+				(void) execle(prog, prog, NULL, envp);
+				cry(conn, "%s: execle(%s): %s",
+				    __func__, prog, strerror(ERRNO));
+			} else {
+				(void) execle(interp, interp, prog, NULL, envp);
+				cry(conn, "%s: execle(%s %s): %s",
+				    __func__, interp, prog, strerror(ERRNO));
+			}
+		}
+		exit(EXIT_FAILURE);
+	} else {
+		/* Parent. Close stdio descriptors */
+		(void) close(fd_stdin);
+		(void) close(fd_stdout);
+	}
+
+	return (pid);
+}
+#endif /* !NO_CGI */
+
+static int
+set_non_blocking_mode(struct mg_connection *conn, SOCKET sock)
+{
+	int	flags, ok = -1;
+
+	if ((flags = fcntl(sock, F_GETFL, 0)) == -1) {
+		cry(conn, "%s: fcntl(F_GETFL): %d", __func__, ERRNO);
+	} else if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) != 0) {
+		cry(conn, "%s: fcntl(F_SETFL): %d", __func__, ERRNO);
+	} else {
+		ok = 0;	/* Success */
+	}
+
+	return (ok);
+}
+#endif /* _WIN32 */
+
+static void
+lock_option(struct mg_context *ctx, int opt_index)
+{
+	if (pthread_mutex_lock(&ctx->opt_mutex[opt_index]) != 0)
+		cry(fc(ctx), "pthread_mutex_lock: %s", strerror(ERRNO));
+}
+
+static void
+unlock_option(struct mg_context *ctx, int opt_index)
+{
+	if (pthread_mutex_unlock(&ctx->opt_mutex[opt_index]) != 0)
+		cry(fc(ctx), "pthread_mutex_unlock: %s", strerror(ERRNO));
+}
+
+/*
+ * Write data to the IO channel - opened file descriptor, socket or SSL
+ * descriptor. Return number of bytes written.
+ */
+static uint64_t
+push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, uint64_t len)
+{
+	uint64_t	sent;
+	int		n, k;
+
+	sent = 0;
+	while (sent < len) {
+
+		/* How many bytes we send in this iteration */
+		k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
+
+		if (ssl != NULL) {
+			n = SSL_write(ssl, buf + sent, k);
+		} else if (fp != NULL) {
+			n = fwrite(buf + sent, 1, k, fp);
+			if (ferror(fp))
+				n = -1;
+		} else {
+			n = send(sock, buf + sent, k, 0);
+		}
+
+		if (n < 0)
+			break;
+
+		sent += n;
+	}
+
+	return (sent);
+}
+
+/*
+ * Read from IO channel - opened file descriptor, socket, or SSL descriptor.
+ * Return number of bytes read.
+ */
+static int
+pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len)
+{
+	int	nread;
+
+	if (ssl != NULL) {
+		nread = SSL_read(ssl, buf, len);
+	} else if (fp != NULL) {
+		nread = fread(buf, 1, (size_t) len, fp);
+		if (ferror(fp))
+			nread = -1;
+	} else {
+		nread = recv(sock, buf, (size_t) len, 0);
+	}
+
+	return (nread);
+}
+
+int
+mg_write(struct mg_connection *conn, const void *buf, int len)
+{
+	assert(len >= 0);
+	return ((int) push(NULL, conn->client.sock, conn->ssl,
+				(const char *) buf, (uint64_t) len));
+}
+
+int
+mg_printf(struct mg_connection *conn, const char *fmt, ...)
+{
+	char	buf[MAX_REQUEST_SIZE];
+	int	len;
+	va_list	ap;
+
+	va_start(ap, fmt);
+	len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+
+	return (mg_write(conn, buf, len));
+}
+
+/*
+ * Return content length of the request, or UNKNOWN_CONTENT_LENGTH constant if
+ * Content-Length header is not set.
+ */
+static uint64_t
+get_content_length(const struct mg_connection *conn)
+{
+	const char *cl = mg_get_header(conn, "Content-Length");
+	return (cl == NULL ? UNKNOWN_CONTENT_LENGTH : strtoull(cl, NULL, 10));
+}
+
+/*
+ * URL-decode input buffer into destination buffer.
+ * 0-terminate the destination buffer. Return the length of decoded data.
+ * form-url-encoded data differs from URI encoding in a way that it
+ * uses '+' as character for space, see RFC 1866 section 8.2.1
+ * http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
+ */
+static size_t
+url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
+		bool_t is_form_url_encoded)
+{
+	size_t	i, j;
+	int	a, b;
+#define	HEXTOI(x)	(isdigit(x) ? x - '0' : x - 'W')
+
+	for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
+		if (src[i] == '%' &&
+		    isxdigit(* (unsigned char *) (src + i + 1)) &&
+		    isxdigit(* (unsigned char *) (src + i + 2))) {
+			a = tolower(* (unsigned char *) (src + i + 1));
+			b = tolower(* (unsigned char *) (src + i + 2));
+			dst[j] = ((HEXTOI(a) << 4) | HEXTOI(b)) & 0xff;
+			i += 2;
+		} else if (is_form_url_encoded && src[i] == '+') {
+			dst[j] = ' ';
+		} else {
+			dst[j] = src[i];
+		}
+	}
+
+	dst[j] = '\0';	/* Null-terminate the destination */
+
+	return (j);
+}
+
+/*
+ * Search for a form variable in a given buffer.
+ * Semantic is the same as for mg_get_var().
+ */
+static char *
+get_var(const char *name, const char *buf, size_t buf_len)
+{
+	const char	*p, *e, *s;
+	char		*val;
+	size_t		var_len, len;
+
+	var_len = strlen(name);
+	e = buf + buf_len;
+	val = NULL;
+
+	/* buf is "var1=val1&var2=val2...". Find variable first */
+	for (p = buf; p + var_len < e; p++)
+		if ((p == buf || p[-1] == '&') && p[var_len] == '=' &&
+		    !mg_strncasecmp(name, p, var_len)) {
+
+			/* Point p to variable value */
+			p += var_len + 1;
+
+			/* Point s to the end of the value */
+			s = (const char *) memchr(p, '&', e - p);
+			if (s == NULL)
+				s = e;
+
+			/* Try to allocate the buffer */
+			len = s - p;
+			if ((val = (char *) malloc(len + 1)) != NULL)
+				(void) url_decode(p, len, val, len + 1, TRUE);
+			break;
+		}
+
+	return (val);
+}
+
+/*
+ * Free the pointer returned by mg_get_var(). This is needed for languages
+ * like python, to have an ability to free allocated data without
+ * loading C runtime library and calling free().
+ */
+void
+mg_free(char *data)
+{
+	free(data);
+}
+
+/*
+ * Return form data variable.
+ * It can be specified in query string, or in the POST data.
+ * Return NULL if the variable not found, or allocated 0-terminated value.
+ * It is caller's responsibility to free the returned value.
+ */
+char *
+mg_get_var(const struct mg_connection *conn, const char *name)
+{
+	const struct mg_request_info	*ri = &conn->request_info;
+	char				*v1, *v2;
+
+	v1 = v2 = NULL;
+
+	/* Look in both query_string and POST data */
+	if (ri->query_string != NULL)
+		v1 = get_var(name, ri->query_string, strlen(ri->query_string));
+	if (ri->post_data_len > 0)
+		v2 = get_var(name, ri->post_data, ri->post_data_len);
+
+	/* If they both have queried variable, POST data wins */
+	if (v1 != NULL && v2 != NULL)
+		free(v1);
+
+	return (v2 == NULL ? v1 : v2);
+}
+
+/*
+ * Transform URI to the file name.
+ */
+static void
+convert_uri_to_file_name(struct mg_connection *conn, const char *uri,
+		char *buf, size_t buf_len)
+{
+	struct mg_context	*ctx = conn->ctx;
+	struct vec		uri_vec, path_vec;
+	const char		*list;
+
+	lock_option(ctx, OPT_ROOT);
+	mg_snprintf(conn, buf, buf_len, "%s%s", ctx->options[OPT_ROOT], uri);
+	unlock_option(ctx, OPT_ROOT);
+
+	/* If requested URI has aliased prefix, use alternate root */
+	lock_option(ctx, OPT_ALIASES);
+	list = ctx->options[OPT_ALIASES];
+
+	while ((list = next_option(list, &uri_vec, &path_vec)) != NULL) {
+		if (memcmp(uri, uri_vec.ptr, uri_vec.len) == 0) {
+			(void) mg_snprintf(conn, buf, buf_len, "%.*s%s",
+			    path_vec.len, path_vec.ptr, uri + uri_vec.len);
+			break;
+		}
+	}
+	unlock_option(ctx, OPT_ALIASES);
+
+#ifdef _WIN32
+	fix_directory_separators(buf);
+#endif /* _WIN32 */
+
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: [%s] -> [%s]", __func__, uri, buf));
+}
+
+/*
+ * Setup listening socket on given address, return socket.
+ * Address format: [local_ip_address:]port_number
+ */
+static SOCKET
+mg_open_listening_port(struct mg_context *ctx, const char *str, struct usa *usa)
+{
+	SOCKET		sock;
+	int		on = 1, a, b, c, d, port;
+
+	/* MacOS needs that. If we do not zero it, bind() will fail. */
+	(void) memset(usa, 0, sizeof(*usa));
+
+	if (sscanf(str, "%d.%d.%d.%d:%d", &a, &b, &c, &d, &port) == 5) {
+		/* IP address to bind to is specified */
+		usa->u.sin.sin_addr.s_addr =
+		    htonl((a << 24) | (b << 16) | (c << 8) | d);
+	} else if (sscanf(str, "%d", &port) == 1) {
+		/* Only port number is specified. Bind to all addresses */
+		usa->u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+	} else {
+		return (INVALID_SOCKET);
+	}
+
+	usa->len			= sizeof(usa->u.sin);
+	usa->u.sin.sin_family		= AF_INET;
+	usa->u.sin.sin_port		= htons((uint16_t) port);
+
+	if ((sock = socket(PF_INET, SOCK_STREAM, 6)) != INVALID_SOCKET &&
+	    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+	    (char *) &on, sizeof(on)) == 0 &&
+	    bind(sock, &usa->u.sa, usa->len) == 0 &&
+	    listen(sock, 20) == 0) {
+		/* Success */
+		set_close_on_exec(sock);
+	} else {
+		/* Error */
+		cry(fc(ctx), "%s(%d): %s", __func__, port, strerror(ERRNO));
+		if (sock != INVALID_SOCKET)
+			(void) closesocket(sock);
+		sock = INVALID_SOCKET;
+	}
+
+	return (sock);
+}
+
+/*
+ * Check whether full request is buffered. Return:
+ *   -1         if request is malformed
+ *    0         if request is not yet fully buffered
+ *   >0         actual request length, including last \r\n\r\n
+ */
+static int
+get_request_len(const char *buf, size_t buflen)
+{
+	const char	*s, *e;
+	int		len = 0;
+
+	for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
+		/* Control characters are not allowed but >=128 is. */
+		if (!isprint(* (unsigned char *) s) && *s != '\r' &&
+		    *s != '\n' && * (unsigned char *) s < 128)
+			len = -1;
+		else if (s[0] == '\n' && s[1] == '\n')
+			len = (int) (s - buf) + 2;
+		else if (s[0] == '\n' && &s[1] < e &&
+		    s[1] == '\r' && s[2] == '\n')
+			len = (int) (s - buf) + 3;
+
+	return (len);
+}
+
+/*
+ * Convert month to the month number. Return -1 on error, or month number
+ */
+static int
+montoi(const char *s)
+{
+	size_t	i;
+
+	for (i = 0; i < sizeof(month_names) / sizeof(month_names[0]); i++)
+		if (!strcmp(s, month_names[i]))
+			return ((int) i);
+
+	return (-1);
+}
+
+/*
+ * Parse date-time string, and return the corresponding time_t value
+ */
+static time_t
+date_to_epoch(const char *s)
+{
+	time_t		current_time;
+	struct tm	tm, *tmp;
+	char		mon[32];
+	int		sec, min, hour, mday, month, year;
+
+	(void) memset(&tm, 0, sizeof(tm));
+	sec = min = hour = mday = month = year = 0;
+
+	if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
+	    &mday, mon, &year, &hour, &min, &sec) == 6) ||
+	    (sscanf(s, "%d %3s %d %d:%d:%d",
+	    &mday, mon, &year, &hour, &min, &sec) == 6) ||
+	    (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
+	    &mday, mon, &year, &hour, &min, &sec) == 6) ||
+	    (sscanf(s, "%d-%3s-%d %d:%d:%d",
+	    &mday, mon, &year, &hour, &min, &sec) == 6)) &&
+	    (month = montoi(mon)) != -1) {
+		tm.tm_mday	= mday;
+		tm.tm_mon	= month;
+		tm.tm_year	= year;
+		tm.tm_hour	= hour;
+		tm.tm_min	= min;
+		tm.tm_sec	= sec;
+	}
+
+	if (tm.tm_year > 1900)
+		tm.tm_year -= 1900;
+	else if (tm.tm_year < 70)
+		tm.tm_year += 100;
+
+	/* Set Daylight Saving Time field */
+	current_time = time(NULL);
+	tmp = localtime(&current_time);
+	tm.tm_isdst = tmp->tm_isdst;
+
+	return (mktime(&tm));
+}
+
+/*
+ * Protect against directory disclosure attack by removing '..',
+ * excessive '/' and '\' characters
+ */
+static void
+remove_double_dots_and_double_slashes(char *s)
+{
+	char	*p = s;
+
+	while (*s != '\0') {
+		*p++ = *s++;
+		if (s[-1] == '/' || s[-1] == '\\') {
+			/* Skip all following slashes and backslashes */
+			while (*s == '/' || *s == '\\')
+				s++;
+
+			/* Skip all double-dots */
+			while (*s == '.' && s[1] == '.')
+				s += 2;
+		}
+	}
+	*p = '\0';
+}
+
+/*
+ * Built-in mime types
+ */
+static const struct {
+	const char	*extension;
+	size_t		ext_len;
+	const char	*mime_type;
+	size_t		mime_type_len;
+} mime_types[] = {
+	{".html",	5,	"text/html",			9},
+	{".htm",	4,	"text/html",			9},
+	{".shtm",	5,	"text/html",			9},
+	{".shtml",	6,	"text/html",			9},
+	{".css",	4,	"text/css",			8},
+	{".js",		3,	"application/x-javascript",	24},
+	{".ico",	4,	"image/x-icon",			12},
+	{".gif",	4,	"image/gif",			9},
+	{".jpg",	4,	"image/jpeg",			10},
+	{".jpeg",	5,	"image/jpeg",			10},
+	{".png",	4,	"image/png",			9},
+	{".svg",	4,	"image/svg+xml",		13},
+	{".torrent",	8,	"application/x-bittorrent",	24},
+	{".wav",	4,	"audio/x-wav",			11},
+	{".mp3",	4,	"audio/x-mp3",			11},
+	{".mid",	4,	"audio/mid",			9},
+	{".m3u",	4,	"audio/x-mpegurl",		15},
+	{".ram",	4,	"audio/x-pn-realaudio",		20},
+	{".ra",		3,	"audio/x-pn-realaudio",		20},
+	{".doc",	4,	"application/msword",		19},
+	{".exe",	4,	"application/octet-stream",	24},
+	{".zip",	4,	"application/x-zip-compressed",	28},
+	{".xls",	4,	"application/excel",		17},
+	{".tgz",	4,	"application/x-tar-gz",		20},
+	{".tar",	4,	"application/x-tar",		17},
+	{".gz",		3,	"application/x-gunzip",		20},
+	{".arj",	4,	"application/x-arj-compressed",	28},
+	{".rar",	4,	"application/x-arj-compressed",	28},
+	{".rtf",	4,	"application/rtf",		15},
+	{".pdf",	4,	"application/pdf",		15},
+	{".swf",	4,	"application/x-shockwave-flash",29},
+	{".mpg",	4,	"video/mpeg",			10},
+	{".mpeg",	5,	"video/mpeg",			10},
+	{".asf",	4,	"video/x-ms-asf",		14},
+	{".avi",	4,	"video/x-msvideo",		15},
+	{".bmp",	4,	"image/bmp",			9},
+	{NULL,		0,	NULL,				0}
+};
+
+/*
+ * Look at the "path" extension and figure what mime type it has.
+ * Store mime type in the vector.
+ */
+static void
+get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec)
+{
+	struct vec	ext_vec, mime_vec;
+	const char	*list, *ext;
+	size_t		i, path_len;
+
+	path_len = strlen(path);
+
+	/*
+	 * Scan user-defined mime types first, in case user wants to
+	 * override default mime types.
+	 */
+	lock_option(ctx, OPT_MIME_TYPES);
+	list = ctx->options[OPT_MIME_TYPES];
+	while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
+		/* ext now points to the path suffix */
+		ext = path + path_len - ext_vec.len;
+		if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
+			*vec = mime_vec;
+			unlock_option(ctx, OPT_MIME_TYPES);
+			return;
+		}
+	}
+	unlock_option(ctx, OPT_MIME_TYPES);
+
+	/* Now scan built-in mime types */
+	for (i = 0; mime_types[i].extension != NULL; i++) {
+		ext = path + (path_len - mime_types[i].ext_len);
+		if (path_len > mime_types[i].ext_len &&
+		    mg_strcasecmp(ext, mime_types[i].extension) == 0) {
+			vec->ptr = mime_types[i].mime_type;
+			vec->len = mime_types[i].mime_type_len;
+			return;
+		}
+	}
+
+	/* Nothing found. Fall back to text/plain */
+	vec->ptr = "text/plain";
+	vec->len = 10;
+}
+
+#ifndef HAVE_MD5
+typedef struct MD5Context {
+	uint32_t	buf[4];
+	uint32_t	bits[2];
+	unsigned char	in[64];
+} MD5_CTX;
+
+#if __BYTE_ORDER == 1234
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+byteReverse(unsigned char *buf, unsigned longs)
+{
+	uint32_t t;
+	do {
+		t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+			((unsigned) buf[1] << 8 | buf[0]);
+		*(uint32_t *) buf = t;
+		buf += 4;
+	} while (--longs);
+}
+#endif /* __BYTE_ORDER */
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void
+MD5Init(MD5_CTX *ctx)
+{
+	ctx->buf[0] = 0x67452301;
+	ctx->buf[1] = 0xefcdab89;
+	ctx->buf[2] = 0x98badcfe;
+	ctx->buf[3] = 0x10325476;
+
+	ctx->bits[0] = 0;
+	ctx->bits[1] = 0;
+}
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+	register uint32_t a, b, c, d;
+
+	a = buf[0];
+	b = buf[1];
+	c = buf[2];
+	d = buf[3];
+
+	MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+	MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+	MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+	MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+	MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+	MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+	MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+	MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+	MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+	MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+	MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+	MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+	MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+	MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+	MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+	MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+	MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+	MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+	MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+	MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+	MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+	MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+	MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+	MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+	MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+	MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+	MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+	MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+	MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+	MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+	MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+	MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+	MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+	MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+	MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+	MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+	MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+	MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+	MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+	MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+	MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+	MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+	MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+	MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+	MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+	MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+	MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+	MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+	MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+	MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+	MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+	MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+	MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+	MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+	MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+	MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+	MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+	MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+	MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+	MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+	MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+	MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+	MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+	MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+	buf[0] += a;
+	buf[1] += b;
+	buf[2] += c;
+	buf[3] += d;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void
+MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
+{
+	uint32_t t;
+
+	/* Update bitcount */
+
+	t = ctx->bits[0];
+	if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+		ctx->bits[1]++;		/* Carry from low to high */
+	ctx->bits[1] += len >> 29;
+
+	t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+	/* Handle any leading odd-sized chunks */
+
+	if (t) {
+		unsigned char *p = (unsigned char *) ctx->in + t;
+
+		t = 64 - t;
+		if (len < t) {
+			memcpy(p, buf, len);
+			return;
+		}
+		memcpy(p, buf, t);
+		byteReverse(ctx->in, 16);
+		MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+		buf += t;
+		len -= t;
+	}
+	/* Process data in 64-byte chunks */
+
+	while (len >= 64) {
+		memcpy(ctx->in, buf, 64);
+		byteReverse(ctx->in, 16);
+		MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+		buf += 64;
+		len -= 64;
+	}
+
+	/* Handle any remaining bytes of data. */
+
+	memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void
+MD5Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+	unsigned count;
+	unsigned char *p;
+
+	/* Compute number of bytes mod 64 */
+	count = (ctx->bits[0] >> 3) & 0x3F;
+
+	/* Set the first char of padding to 0x80.  This is safe since there is
+	   always at least one byte free */
+	p = ctx->in + count;
+	*p++ = 0x80;
+
+	/* Bytes of padding needed to make 64 bytes */
+	count = 64 - 1 - count;
+
+	/* Pad out to 56 mod 64 */
+	if (count < 8) {
+		/* Two lots of padding:  Pad the first block to 64 bytes */
+		memset(p, 0, count);
+		byteReverse(ctx->in, 16);
+		MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+		/* Now fill the next block with 56 bytes */
+		memset(ctx->in, 0, 56);
+	} else {
+		/* Pad block to 56 bytes */
+		memset(p, 0, count - 8);
+	}
+	byteReverse(ctx->in, 14);
+
+	/* Append length in bits and transform */
+	((uint32_t *) ctx->in)[14] = ctx->bits[0];
+	((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	byteReverse((unsigned char *) ctx->buf, 4);
+	memcpy(digest, ctx->buf, 16);
+	memset((char *) ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+}
+#endif /* !HAVE_MD5 */
+
+/*
+ * Stringify binary data. Output buffer must be twice as big as input,
+ * because each byte takes 2 bytes in string representation
+ */
+static void
+bin2str(char *to, const unsigned char *p, size_t len)
+{
+	static const char *hex = "0123456789abcdef";
+
+	for (; len--; p++) {
+		*to++ = hex[p[0] >> 4];
+		*to++ = hex[p[0] & 0x0f];
+	}
+	*to = '\0';
+}
+
+/*
+ * Return stringified MD5 hash for list of vectors.
+ * buf must point to 33-bytes long buffer
+ */
+static void
+mg_md5(char *buf, ...)
+{
+	unsigned char	hash[16];
+	const char	*p;
+	va_list		ap;
+	MD5_CTX		ctx;
+
+	MD5Init(&ctx);
+
+	va_start(ap, buf);
+	while ((p = va_arg(ap, const char *)) != NULL)
+		MD5Update(&ctx, (unsigned char *) p, (int) strlen(p));
+	va_end(ap);
+
+	MD5Final(hash, &ctx);
+	bin2str(buf, hash, sizeof(hash));
+}
+
+/*
+ * Check the user's password, return 1 if OK
+ */
+static bool_t
+check_password(const char *method, const char *ha1, const char *uri,
+		const char *nonce, const char *nc, const char *cnonce,
+		const char *qop, const char *response)
+{
+	char	ha2[32 + 1], expected_response[32 + 1];
+
+	/* XXX  Due to a bug in MSIE, we do not compare the URI	 */
+	/* Also, we do not check for authentication timeout */
+	if (/*strcmp(dig->uri, c->ouri) != 0 || */
+	    strlen(response) != 32 /*||
+	    now - strtoul(dig->nonce, NULL, 10) > 3600 */)
+		return (FALSE);
+
+	mg_md5(ha2, method, ":", uri, NULL);
+	mg_md5(expected_response, ha1, ":", nonce, ":", nc,
+	    ":", cnonce, ":", qop, ":", ha2, NULL);
+
+	return (!mg_strcasecmp(response, expected_response));
+}
+
+/*
+ * Use the global passwords file, if specified by auth_gpass option,
+ * or search for .htpasswd in the requested directory.
+ */
+static FILE *
+open_auth_file(struct mg_connection *conn, const char *path)
+{
+	struct mg_context	*ctx = conn->ctx;
+	char 			name[FILENAME_MAX];
+	const char		*p, *e;
+	struct mgstat		st;
+	FILE			*fp;
+
+	if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
+		/* Use global passwords file */
+		fp =  mg_fopen(ctx->options[OPT_AUTH_GPASSWD], "r");
+		if (fp == NULL)
+			cry(fc(ctx), "fopen(%s): %s",
+			    ctx->options[OPT_AUTH_GPASSWD], strerror(ERRNO));
+	} else if (!mg_stat(path, &st) && st.is_directory) {
+		(void) mg_snprintf(conn, name, sizeof(name), "%s%c%s",
+		    path, DIRSEP, PASSWORDS_FILE_NAME);
+		fp = mg_fopen(name, "r");
+	} else {
+		/*
+		 * 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;
+
+		/*
+		 * Make up the path by concatenating directory name and
+		 * .htpasswd file name.
+		 */
+		(void) mg_snprintf(conn, name, sizeof(name), "%.*s%c%s",
+		    (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME);
+		fp = mg_fopen(name, "r");
+	}
+
+	return (fp);
+}
+
+/*
+ * Parsed Authorization: header
+ */
+struct ah {
+	char	*user, *uri, *cnonce, *response, *qop, *nc, *nonce;
+};
+
+static bool_t
+parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size,
+		struct ah *ah)
+{
+	char		*name, *value, *s;
+	const char	*auth_header;
+
+	if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
+	    mg_strncasecmp(auth_header, "Digest ", 7) != 0)
+		return (FALSE);
+
+	/* Make modifiable copy of the auth header */
+	(void) mg_strlcpy(buf, auth_header + 7, buf_size);
+
+	s = buf;
+	(void) memset(ah, 0, sizeof(*ah));
+
+	/* Gobble initial spaces */
+	while (isspace(* (unsigned char *) s))
+		s++;
+
+	/* Parse authorization header */
+	for (;;) {
+		name = skip(&s, "=");
+		value = skip(&s, ", ");
+
+		if (*value == '"') {
+			value++;
+			value[strlen(value) - 1] = '\0';
+		} else if (*value == '\0') {
+			break;
+		}
+
+		if (!strcmp(name, "username")) {
+			ah->user = value;
+		} else if (!strcmp(name, "cnonce")) {
+			ah->cnonce = value;
+		} else if (!strcmp(name, "response")) {
+			ah->response = value;
+		} else if (!strcmp(name, "uri")) {
+			ah->uri = value;
+		} else if (!strcmp(name, "qop")) {
+			ah->qop = value;
+		} else if (!strcmp(name, "nc")) {
+			ah->nc = value;
+		} else if (!strcmp(name, "nonce")) {
+			ah->nonce = value;
+		}
+	}
+
+	/* CGI needs it as REMOTE_USER */
+	if (ah->user != NULL)
+		conn->request_info.remote_user = mg_strdup(ah->user);
+
+	return (TRUE);
+}
+
+/*
+ * Authorize against the opened passwords file. Return 1 if authorized.
+ */
+static bool_t
+authorize(struct mg_connection *conn, FILE *fp)
+{
+	struct ah	ah;
+	char		line[256], f_user[256], domain[256], ha1[256],
+			buf[MAX_REQUEST_SIZE];
+
+	if (!parse_auth_header(conn, buf, sizeof(buf), &ah))
+		return (FALSE);
+
+	/* Loop over passwords file */
+	while (fgets(line, sizeof(line), fp) != NULL) {
+
+		if (sscanf(line, "%[^:]:%[^:]:%s", f_user, domain, ha1) != 3)
+			continue;
+
+		if (!strcmp(ah.user, f_user) &&
+		    !strcmp(domain, conn->ctx->options[OPT_AUTH_DOMAIN]))
+			return (check_password(
+			    conn->request_info.request_method, ha1,
+			    ah.uri, ah.nonce, ah.nc, ah.cnonce,
+			    ah.qop, ah.response));
+	}
+
+	return (FALSE);
+}
+
+/*
+ * Return TRUE if request is authorised, FALSE otherwise.
+ */
+static bool_t
+check_authorization(struct mg_connection *conn, const char *path)
+{
+	FILE		*fp;
+	char		fname[FILENAME_MAX];
+	struct vec	uri_vec, filename_vec;
+	const char	*list;
+	bool_t		authorized;
+
+	fp = NULL;
+	authorized = TRUE;
+
+	lock_option(conn->ctx, OPT_PROTECT);
+	list = conn->ctx->options[OPT_PROTECT];
+	while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
+		if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
+			(void) mg_snprintf(conn, fname, sizeof(fname), "%.*s",
+			    filename_vec.len, filename_vec.ptr);
+			if ((fp = mg_fopen(fname, "r")) == NULL)
+				cry(conn, "%s: cannot open %s: %s",
+				    __func__, fname, strerror(errno));
+			break;
+		}
+	}
+	unlock_option(conn->ctx, OPT_PROTECT);
+
+	if (fp == NULL)
+		fp = open_auth_file(conn, path);
+
+	if (fp != NULL) {
+		authorized = authorize(conn, fp);
+		(void) fclose(fp);
+	}
+
+	return (authorized);
+}
+
+static void
+send_authorization_request(struct mg_connection *conn)
+{
+	conn->request_info.status_code = 401;
+	(void) mg_printf(conn,
+	    "HTTP/1.1 401 Unauthorized\r\n"
+	    "WWW-Authenticate: Digest qop=\"auth\", "
+	    "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
+	    conn->ctx->options[OPT_AUTH_DOMAIN], (unsigned long) time(NULL));
+}
+
+static bool_t
+is_authorized_for_put(struct mg_connection *conn)
+{
+	FILE	*fp;
+	int	ret = FALSE;
+
+	if ((fp = mg_fopen(conn->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {
+		set_close_on_exec(fileno(fp));
+		ret = authorize(conn, fp);
+		(void) fclose(fp);
+	}
+
+	return (ret);
+}
+
+int
+mg_modify_passwords_file(struct mg_context *ctx, const char *fname,
+		const char *user, const char *pass)
+{
+	int		found;
+	char		line[512], u[512], d[512], ha1[33], tmp[FILENAME_MAX];
+	const char	*domain;
+	FILE		*fp, *fp2;
+
+	found = 0;
+	fp = fp2 = NULL;
+	domain = ctx->options[OPT_AUTH_DOMAIN];
+
+	/* Regard empty password as no password - remove user record. */
+	if (pass[0] == '\0')
+		pass = NULL;
+
+	(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
+
+	/* Create the file if does not exist */
+	if ((fp = mg_fopen(fname, "a+")) != NULL)
+		(void) fclose(fp);
+
+	/* Open the given file and temporary file */
+	if ((fp = mg_fopen(fname, "r")) == NULL) {
+		cry(fc(ctx), "Cannot open %s: %s", fname, strerror(errno));
+		return (0);
+	} else if ((fp2 = mg_fopen(tmp, "w+")) == NULL) {
+		cry(fc(ctx), "Cannot open %s: %s", tmp, strerror(errno));
+		return (0);
+	}
+
+	/* Copy the stuff to temporary file */
+	while (fgets(line, sizeof(line), fp) != NULL) {
+
+		if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2)
+			continue;
+
+		if (!strcmp(u, user) && !strcmp(d, domain)) {
+			found++;
+			if (pass != NULL) {
+				mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+				fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+			}
+		} else {
+			(void) fprintf(fp2, "%s", line);
+		}
+	}
+
+	/* If new user, just add it */
+	if (!found && pass != NULL) {
+		mg_md5(ha1, user, ":", domain, ":", pass, NULL);
+		(void) fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
+	}
+
+	/* Close files */
+	(void) fclose(fp);
+	(void) fclose(fp2);
+
+	/* Put the temp file in place of real file */
+	(void) mg_remove(fname);
+	(void) mg_rename(tmp, fname);
+
+	return (0);
+}
+
+struct de {
+	struct mg_connection	*conn;
+	char			*file_name;
+	struct mgstat		st;
+};
+
+static void
+url_encode(const char *src, char *dst, size_t dst_len)
+{
+	const char	*dont_escape = "._-$,;~()";
+	const char	*hex = "0123456789abcdef";
+	const char	*end = dst + dst_len - 1;
+	
+	for (; *src != '\0' && dst < end; src++, dst++) {
+		if (isalnum(*(unsigned char *) src) ||
+		    strchr(dont_escape, * (unsigned char *) src) != NULL) {
+			*dst = *src;
+		} else if (dst + 2 < end) {
+			dst[0] = '%';
+			dst[1] = hex[(* (unsigned char *) src) >> 4];
+			dst[2] = hex[(* (unsigned char *) src) & 0xf];
+			dst += 2;
+		}
+	}
+
+	*dst = '\0';
+}
+
+/*
+ * This function is called from send_directory() and prints out
+ * single directory entry.
+ */
+static void
+print_dir_entry(struct de *de)
+{
+	char		size[64], mod[64], href[FILENAME_MAX];
+
+	if (de->st.is_directory) {
+		(void) mg_snprintf(de->conn,
+		    size, sizeof(size), "%s", "[DIRECTORY]");
+	} else {
+		/*
+		 * We use (signed) cast below because MSVC 6 compiler cannot
+		 * convert unsigned __int64 to double. Sigh.
+		 */
+		if (de->st.size < 1024)
+			(void) mg_snprintf(de->conn, size, sizeof(size),
+			    "%lu", (unsigned long) de->st.size);
+		else if (de->st.size < 1024 * 1024)
+			(void) mg_snprintf(de->conn, size, sizeof(size),
+			    "%.1fk", (double) de->st.size / 1024.0);
+		else if (de->st.size < 1024 * 1024 * 1024)
+			(void) mg_snprintf(de->conn, size, sizeof(size),
+			    "%.1fM", (double) de->st.size / 1048576);
+		else
+			(void) mg_snprintf(de->conn, size, sizeof(size),
+			  "%.1fG", (double) de->st.size / 1073741824);
+	}
+	(void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
+		localtime(&de->st.mtime));
+
+	url_encode(de->file_name, href, sizeof(href));
+
+	de->conn->num_bytes_sent += mg_printf(de->conn,
+	    "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
+	    "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+	    de->conn->request_info.uri, href, de->st.is_directory ? "/" : "",
+	    de->file_name, de->st.is_directory ? "/" : "", mod, size);
+}
+
+/*
+ * This function is called from send_directory() and used for
+ * sorting direcotory entries by size, or name, or modification time.
+ */
+static int
+compare_dir_entries(const void *p1, const void *p2)
+{
+	const struct de	*a = (struct de *) p1, *b = (struct de *) p2;
+	const char	*query_string = a->conn->request_info.query_string;
+	int		cmp_result = 0;
+
+	if (query_string == NULL)
+		query_string = "na";
+
+	if (a->st.is_directory && !b->st.is_directory) {
+		return (-1);  /* Always put directories on top */
+	} else if (!a->st.is_directory && b->st.is_directory) {
+		return (1);   /* Always put directories on top */
+	} else if (*query_string == 'n') {
+		cmp_result = strcmp(a->file_name, b->file_name);
+	} else if (*query_string == 's') {
+		cmp_result = a->st.size == b->st.size ? 0 :
+			a->st.size > b->st.size ? 1 : -1;
+	} else if (*query_string == 'd') {
+		cmp_result = a->st.mtime == b->st.mtime ? 0 :
+			a->st.mtime > b->st.mtime ? 1 : -1;
+	}
+
+	return (query_string[1] == 'd' ? -cmp_result : cmp_result);
+}
+
+/*
+ * Send directory contents.
+ */
+static void
+send_directory(struct mg_connection *conn, const char *dir)
+{
+	struct dirent	*dp;
+	DIR		*dirp;
+	struct de	*entries = NULL;
+	char		path[FILENAME_MAX];
+	int		i, sort_direction, num_entries = 0, arr_size = 128;
+
+	if ((dirp = opendir(dir)) == NULL) {
+		send_error(conn, 500, "Cannot open directory",
+		    "Error: opendir(%s): %s", path, strerror(ERRNO));
+		return;
+	}
+
+	(void) mg_printf(conn, "%s",
+	    "HTTP/1.1 200 OK\r\n"
+	    "Connection: close\r\n"
+	    "Content-Type: text/html; charset=utf-8\r\n\r\n");
+
+	sort_direction = conn->request_info.query_string != NULL &&
+	    conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
+
+	while ((dp = readdir(dirp)) != NULL) {
+
+		/* Do not show current dir and passwords file */
+		if (!strcmp(dp->d_name, ".") ||
+		    !strcmp(dp->d_name, "..") ||
+		    !strcmp(dp->d_name, PASSWORDS_FILE_NAME))
+			continue;
+
+		if (entries == NULL || num_entries >= arr_size) {
+			arr_size *= 2;
+			entries = (struct de *) realloc(entries,
+			    arr_size * sizeof(entries[0]));
+		}
+
+		if (entries == NULL) {
+			send_error(conn, 500, "Cannot open directory",
+			    "%s", "Error: cannot allocate memory");
+			return;
+		}
+
+		(void) mg_snprintf(conn, path, sizeof(path), "%s%c%s",
+		    dir, DIRSEP, dp->d_name);
+
+		/*
+		 * If we don't memset stat structure to zero, mtime will have
+		 * garbage and strftime() will segfault later on in
+		 * print_dir_entry(). memset is required only if mg_stat()
+		 * fails. For more details, see
+		 * http://code.google.com/p/mongoose/issues/detail?id=79
+		 */
+		if (mg_stat(path, &entries[num_entries].st) != 0)
+			(void) memset(&entries[num_entries].st, 0,
+			    sizeof(entries[num_entries].st));
+
+		entries[num_entries].conn = conn;
+		entries[num_entries].file_name = mg_strdup(dp->d_name);
+		num_entries++;
+	}
+	(void) closedir(dirp);
+
+	conn->num_bytes_sent += mg_printf(conn,
+	    "<html><head><title>Index of %s</title>"
+	    "<style>th {text-align: left;}</style></head>"
+	    "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
+	    "<tr><th><a href=\"?n%c\">Name</a></th>"
+	    "<th><a href=\"?d%c\">Modified</a></th>"
+	    "<th><a href=\"?s%c\">Size</a></th></tr>"
+	    "<tr><td colspan=\"3\"><hr></td></tr>",
+	    conn->request_info.uri, conn->request_info.uri,
+	    sort_direction, sort_direction, sort_direction);
+
+	/* Print first entry - link to a parent directory */
+	conn->num_bytes_sent += mg_printf(conn,
+	    "<tr><td><a href=\"%s%s\">%s</a></td>"
+	    "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+	    conn->request_info.uri, "..", "Parent directory", "-", "-");
+
+	/* Sort and print directory entries */
+	qsort(entries, num_entries, sizeof(entries[0]), compare_dir_entries);
+	for (i = 0; i < num_entries; i++) {
+		print_dir_entry(&entries[i]);
+		free(entries[i].file_name);
+	}
+	free(entries);
+
+	conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");
+	conn->request_info.status_code = 200;
+}
+
+/*
+ * Send len bytes from the opened file to the client.
+ */
+static void
+send_opened_file_stream(struct mg_connection *conn, FILE *fp, uint64_t len)
+{
+	char	buf[BUFSIZ];
+	int	to_read, num_read, num_written;
+
+	while (len > 0) {
+		/* Calculate how much to read from the file in the buffer */
+		to_read = sizeof(buf);
+		if ((uint64_t) to_read > len)
+			to_read = (int) len;
+
+		/* Read from file, exit the loop on error */
+		if ((num_read = fread(buf, 1, to_read, fp)) == 0)
+			break;
+
+		/* Send read bytes to the client, exit the loop on error */
+		if ((num_written = mg_write(conn, buf, num_read)) != num_read)
+			break;
+
+		/* Both read and were successful, adjust counters */
+		conn->num_bytes_sent += num_written;
+		len -= num_written;
+	}
+}
+
+/*
+ * Send regular file contents.
+ */
+static void
+send_file(struct mg_connection *conn, const char *path, struct mgstat *stp)
+{
+	char		date[64], lm[64], etag[64], range[64];
+	const char	*fmt = "%a, %d %b %Y %H:%M:%S %Z", *msg = "OK", *hdr;
+	time_t		curtime = time(NULL);
+	uint64_t	cl, r1, r2;
+	struct vec	mime_vec;
+	FILE		*fp;
+	int		n;
+
+	get_mime_type(conn->ctx, path, &mime_vec);
+	cl = stp->size;
+	conn->request_info.status_code = 200;
+	range[0] = '\0';
+
+	if ((fp = mg_fopen(path, "rb")) == NULL) {
+		send_error(conn, 500, http_500_error,
+		    "fopen(%s): %s", path, strerror(ERRNO));
+		return;
+	}
+	set_close_on_exec(fileno(fp));
+
+	/* If Range: header specified, act accordingly */
+	r1 = r2 = 0;
+	hdr = mg_get_header(conn, "Range");
+	if (hdr != NULL && (n = sscanf(hdr,
+	    "bytes=%" UINT64_FMT "u-%" UINT64_FMT "u", &r1, &r2)) > 0) {
+		conn->request_info.status_code = 206;
+		(void) fseeko(fp, (off_t) r1, SEEK_SET);
+		cl = n == 2 ? r2 - r1 + 1: cl - r1;
+		(void) mg_snprintf(conn, range, sizeof(range),
+		    "Content-Range: bytes "
+		    "%" UINT64_FMT "u-%"
+		    UINT64_FMT "u/%" UINT64_FMT "u\r\n",
+		    r1, r1 + cl - 1, stp->size);
+		msg = "Partial Content";
+	}
+
+	/* Prepare Etag, Date, Last-Modified headers */
+	(void) strftime(date, sizeof(date), fmt, localtime(&curtime));
+	(void) strftime(lm, sizeof(lm), fmt, localtime(&stp->mtime));
+	(void) mg_snprintf(conn, etag, sizeof(etag), "%lx.%lx",
+	    (unsigned long) stp->mtime, (unsigned long) stp->size);
+
+	(void) mg_printf(conn,
+	    "HTTP/1.1 %d %s\r\n"
+	    "Date: %s\r\n"
+	    "Last-Modified: %s\r\n"
+	    "Etag: \"%s\"\r\n"
+	    "Content-Type: %.*s\r\n"
+	    "Content-Length: %" UINT64_FMT "u\r\n"
+	    "Connection: close\r\n"
+	    "Accept-Ranges: bytes\r\n"
+	    "%s\r\n",
+	    conn->request_info.status_code, msg, date, lm, etag,
+	    mime_vec.len, mime_vec.ptr, cl, range);
+
+	if (strcmp(conn->request_info.request_method, "HEAD") != 0)
+		send_opened_file_stream(conn, fp, cl);
+	(void) fclose(fp);
+}
+
+/*
+ * Parse HTTP headers from the given buffer, advance buffer to the point
+ * where parsing stopped.
+ */
+static void
+parse_http_headers(char **buf, struct mg_request_info *ri)
+{
+	int	i;
+
+	for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
+		ri->http_headers[i].name = skip(buf, ": ");
+		ri->http_headers[i].value = skip(buf, "\r\n");
+		if (ri->http_headers[i].name[0] == '\0')
+			break;
+		ri->num_headers = i + 1;
+	}
+}
+
+static bool_t
+is_valid_http_method(const char *method)
+{
+	return (!strcmp(method, "GET") ||
+	    !strcmp(method, "POST") ||
+	    !strcmp(method, "HEAD") ||
+	    !strcmp(method, "PUT") ||
+	    !strcmp(method, "DELETE"));
+}
+
+/*
+ * Parse HTTP request, fill in mg_request_info structure.
+ */
+static bool_t
+parse_http_request(char *buf, struct mg_request_info *ri, const struct usa *usa)
+{
+	int	success_code = FALSE;
+
+	ri->request_method = skip(&buf, " ");
+	ri->uri = skip(&buf, " ");
+	ri->http_version = skip(&buf, "\r\n");
+
+	if (is_valid_http_method(ri->request_method) &&
+	    ri->uri[0] == '/' &&
+	    strncmp(ri->http_version, "HTTP/", 5) == 0) {
+		ri->http_version += 5;   /* Skip "HTTP/" */
+		parse_http_headers(&buf, ri);
+		ri->remote_port = ntohs(usa->u.sin.sin_port);
+		(void) memcpy(&ri->remote_ip, &usa->u.sin.sin_addr.s_addr, 4);
+		ri->remote_ip = ntohl(ri->remote_ip);
+		success_code = TRUE;
+	}
+
+	return (success_code);
+}
+
+/*
+ * Keep reading the input (either opened file descriptor fd, or socket sock,
+ * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
+ * buffer (which marks the end of HTTP request). Buffer buf may already
+ * have some data. The length of the data is stored in nread.
+ * Upon every read operation, increase nread by the number of bytes read.
+ */
+static int
+read_request(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz, int *nread)
+{
+	int	n, request_len;
+
+	request_len = 0;
+	while (*nread < bufsiz && request_len == 0) {
+		n = pull(fp, sock, ssl, buf + *nread, bufsiz - *nread);
+		if (n <= 0) {
+			break;
+		} else {
+			*nread += n;
+			request_len = get_request_len(buf, (size_t) *nread);
+		}
+	}
+
+	return (request_len);
+}
+
+/*
+ * For given directory path, substitute it to valid index file.
+ * Return 0 if index file has been found, -1 if not found.
+ * If the file is found, it's stats is returned in stp.
+ */
+static bool_t
+substitute_index_file(struct mg_connection *conn,
+		char *path, size_t path_len, struct mgstat *stp)
+{
+	const char	*list;
+	struct mgstat	st;
+	struct vec	filename_vec;
+	size_t		n;
+	bool_t		found;
+
+	n = strlen(path);
+
+	/*
+	 * The 'path' given to us points to the directory. Remove all trailing
+	 * directory separator characters from the end of the path, and
+	 * then append single directory separator character.
+	 */
+	while (n > 0 && IS_DIRSEP_CHAR(path[n - 1]))
+		n--;
+	path[n] = DIRSEP;
+
+	/*
+	 * Traverse index files list. For each entry, append it to the given
+	 * path and see if the file exists. If it exists, break the loop
+	 */
+	lock_option(conn->ctx, OPT_INDEX_FILES);
+	list = conn->ctx->options[OPT_INDEX_FILES];
+	found = FALSE;
+
+	while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
+
+		/* Ignore too long entries that may overflow path buffer */
+		if (filename_vec.len > path_len - n)
+			continue;
+
+		/* Prepare full path to the index file  */
+		(void) mg_strlcpy(path + n + 1,
+		    filename_vec.ptr, filename_vec.len + 1);
+
+		/* Does it exist ? */
+		if (mg_stat(path, &st) == 0) {
+			/* Yes it does, break the loop */
+			*stp = st;
+			found = TRUE;
+			break;
+		}
+	}
+	unlock_option(conn->ctx, OPT_INDEX_FILES);
+
+	/* If no index file exists, restore directory path */
+	if (found == FALSE)
+		path[n] = '\0';
+
+	return (found);
+}
+
+static void
+remove_callback(struct mg_context *ctx,
+		const char *uri_regex, int status_code, bool_t is_auth)
+{
+	struct callback	*cb;
+	int		i;
+
+	for (i = 0; i < ctx->num_callbacks; i++) {
+		cb = ctx->callbacks + i;
+		if ((uri_regex != NULL && cb->uri_regex != NULL &&
+		    ((is_auth && cb->is_auth) || (!is_auth && !cb->is_auth)) &&
+		    !strcmp(uri_regex, cb->uri_regex)) || (uri_regex == NULL &&
+		     (cb->status_code == 0 ||
+		      cb->status_code == status_code))) {
+			(void) memmove(cb, cb + 1,
+			    (char *) (ctx->callbacks + ctx->num_callbacks) -
+			    (char *) (cb + 1));
+			break;
+		}
+	}
+}
+
+static void
+add_callback(struct mg_context *ctx, const char *uri_regex, int status_code,
+		mg_callback_t func, bool_t is_auth, void *user_data)
+{
+	struct callback	*cb;
+
+	pthread_mutex_lock(&ctx->bind_mutex);
+	if (func == NULL) {
+		remove_callback(ctx, uri_regex, status_code, is_auth);
+	} else if (ctx->num_callbacks >= (int) ARRAY_SIZE(ctx->callbacks) - 1) {
+		cry(fc(ctx), "Too many callbacks! Increase MAX_CALLBACKS.");
+	} else {
+		cb = &ctx->callbacks[ctx->num_callbacks];
+		cb->uri_regex = uri_regex ? mg_strdup(uri_regex) : NULL;
+		cb->func = func;
+		cb->is_auth = is_auth;
+		cb->status_code = status_code;
+		cb->user_data = user_data;
+		ctx->num_callbacks++;
+		DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: uri %s code %d",
+		    __func__, uri_regex ? uri_regex : "NULL", status_code));
+	}
+	pthread_mutex_unlock(&ctx->bind_mutex);
+}
+
+void
+mg_set_uri_callback(struct mg_context *ctx, const char *uri_regex,
+		mg_callback_t func, void *user_data)
+{
+	assert(uri_regex != NULL);
+	add_callback(ctx, uri_regex, -1, func, FALSE, user_data);
+}
+
+void
+mg_set_error_callback(struct mg_context *ctx, int error_code,
+		mg_callback_t func, void *user_data)
+{
+	assert(error_code >= 0 && error_code < 1000);
+	add_callback(ctx, NULL, error_code, func, FALSE, user_data);
+}
+
+void
+mg_set_auth_callback(struct mg_context *ctx, const char *uri_regex,
+		mg_callback_t func, void *user_data)
+{
+	assert(uri_regex != NULL);
+	add_callback(ctx, uri_regex, -1, func, TRUE, user_data);
+}
+
+/*
+ * Return True if we should reply 304 Not Modified.
+ */
+static bool_t
+is_not_modified(const struct mg_connection *conn, const struct mgstat *stp)
+{
+	const char *ims = mg_get_header(conn, "If-Modified-Since");
+	return (ims != NULL && stp->mtime < date_to_epoch(ims));
+}
+
+static bool_t
+append_chunk(struct mg_request_info *ri, FILE *fp, const char *buf, int len)
+{
+	bool_t	ret_code = TRUE;
+
+	if (fp == NULL) {
+		/* TODO: check for NULL here */
+		ri->post_data = (char *) realloc(ri->post_data,
+		    ri->post_data_len + len);
+		(void) memcpy(ri->post_data + ri->post_data_len, buf, len);
+		ri->post_data_len += len;
+	} else if (push(fp, INVALID_SOCKET,
+	    NULL, buf, (uint64_t) len) != (uint64_t) len) {
+		ret_code = FALSE;
+	}
+
+	return (ret_code);
+}
+
+static bool_t
+handle_request_body(struct mg_connection *conn, FILE *fp)
+{
+	struct mg_request_info	*ri = &conn->request_info;
+	const char	*expect, *tmp;
+	uint64_t	content_len;
+	char		buf[BUFSIZ];
+	int		to_read, nread, already_read;
+	bool_t		success_code = FALSE;
+
+	content_len = get_content_length(conn);
+	expect = mg_get_header(conn, "Expect");
+
+	if (content_len == UNKNOWN_CONTENT_LENGTH) {
+		send_error(conn, 411, "Length Required", "");
+	} else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
+		send_error(conn, 417, "Expectation Failed", "");
+	} else {
+		if (expect != NULL)
+			(void) mg_printf(conn, "HTTP/1.1 100 Continue\r\n\r\n");
+
+		already_read = ri->post_data_len;
+		assert(already_read >= 0);
+
+		if (content_len <= (uint64_t) already_read) {
+			ri->post_data_len = (int) content_len;
+			/*
+			 * If fp is NULL, this is embedded mode, and we do not
+			 * have to do anything: POST data is already there,
+			 * no need to allocate a buffer and copy it in.
+			 * If fp != NULL, we need to write the data.
+			 */
+			success_code = fp == NULL || (push(fp, INVALID_SOCKET,
+			    NULL, ri->post_data, content_len) == content_len) ?
+			    TRUE : FALSE;
+		} else {
+
+			if (fp == NULL) {
+				conn->free_post_data = TRUE;
+				tmp = ri->post_data;
+				/* +1 in case if already_read == 0 */
+				ri->post_data = (char*)malloc(already_read + 1);
+				(void) memcpy(ri->post_data, tmp, already_read);
+			} else {
+				(void) push(fp, INVALID_SOCKET, NULL,
+				    ri->post_data, (uint64_t) already_read);
+			}
+
+			content_len -= already_read;
+
+			while (content_len > 0) {
+				to_read = sizeof(buf);
+				if ((uint64_t) to_read > content_len)
+					to_read = (int) content_len;
+				nread = pull(NULL, conn->client.sock,
+				    conn->ssl, buf, to_read);
+				if (nread <= 0)
+					break;
+				if (!append_chunk(ri, fp, buf, nread))
+					break;
+				content_len -= nread;
+			}
+			success_code = content_len == 0 ? TRUE : FALSE;
+		}
+
+		/* Each error code path in this function must send an error */
+		if (success_code != TRUE)
+			send_error(conn, 577, http_500_error,
+			    "%s", "Error handling body data");
+	}
+
+	return (success_code);
+}
+
+#if !defined(NO_CGI)
+
+/*
+ * This structure helps to create an environment for the spawned CGI program.
+ * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
+ * last element must be NULL.
+ * However, on Windows there is a requirement that all these VARIABLE=VALUE\0
+ * strings must reside in a contiguous buffer. The end of the buffer is
+ * marked by two '\0' characters.
+ * We satisfy both worlds: we create an envp array (which is vars), all
+ * entries are actually pointers inside buf.
+ */
+struct cgi_env_block {
+	struct mg_connection *conn;
+	char	buf[CGI_ENVIRONMENT_SIZE];	/* Environment buffer	*/
+	int	len;				/* Space taken		*/
+	char	*vars[MAX_CGI_ENVIR_VARS];	/* char **envp		*/
+	int	nvars;				/* Number of variables	*/
+};
+
+/*
+ * Append VARIABLE=VALUE\0 string to the buffer, and add a respective
+ * pointer into the vars array.
+ */
+static char *
+addenv(struct cgi_env_block *block, const char *fmt, ...)
+{
+	int	n, space;
+	char	*added;
+	va_list	ap;
+
+	/* Calculate how much space is left in the buffer */
+	space = sizeof(block->buf) - block->len - 2;
+	assert(space >= 0);
+
+	/* Make a pointer to the free space int the buffer */
+	added = block->buf + block->len;
+
+	/* Copy VARIABLE=VALUE\0 string into the free space */
+	va_start(ap, fmt);
+	n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap);
+	va_end(ap);
+
+	/* Make sure we do not overflow buffer and the envp array */
+	if (n > 0 && n < space &&
+	    block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
+		/* Append a pointer to the added string into the envp array */
+		block->vars[block->nvars++] = block->buf + block->len;
+		/* Bump up used length counter. Include \0 terminator */
+		block->len += n + 1;
+	}
+
+	return (added);
+}
+
+static void
+prepare_cgi_environment(struct mg_connection *conn, const char *prog,
+		struct cgi_env_block *blk)
+{
+	const char	*s, *script_filename, *root, *slash;
+	struct vec	var_vec;
+	char		*p;
+	int		i;
+
+	blk->len = blk->nvars = 0;
+	blk->conn = conn;
+
+	/* SCRIPT_FILENAME */
+	script_filename = prog;
+	if ((s = strrchr(prog, '/')) != NULL)
+		script_filename = s + 1;
+
+	lock_option(conn->ctx, OPT_ROOT);
+	root = conn->ctx->options[OPT_ROOT];
+	addenv(blk, "SERVER_NAME=%s", conn->ctx->options[OPT_AUTH_DOMAIN]);
+	unlock_option(conn->ctx, OPT_ROOT);
+
+	/* 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", ntohs(conn->client.lsa.u.sin.sin_port));
+	addenv(blk, "SERVER_ROOT=%s", root);
+	addenv(blk, "DOCUMENT_ROOT=%s", root);
+	addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method);
+	addenv(blk, "REMOTE_ADDR=%s",
+	    inet_ntoa(conn->client.rsa.u.sin.sin_addr));
+	addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port);
+	addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
+
+	slash = strrchr(conn->request_info.uri, '/');
+	addenv(blk, "SCRIPT_NAME=%.*s%s",
+	    (slash - conn->request_info.uri) + 1, conn->request_info.uri,
+	    script_filename);
+
+	addenv(blk, "SCRIPT_FILENAME=%s", script_filename);	/* PHP */
+	addenv(blk, "PATH_TRANSLATED=%s", prog);
+	addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
+
+	if ((s = mg_get_header(conn, "Content-Type")) != NULL)
+		addenv(blk, "CONTENT_TYPE=%s", s);
+
+	if (conn->request_info.query_string != NULL)
+		addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string);
+
+	if ((s = mg_get_header(conn, "Content-Length")) != NULL)
+		addenv(blk, "CONTENT_LENGTH=%s", s);
+
+	if ((s = getenv("PATH")) != NULL)
+		addenv(blk, "PATH=%s", s);
+
+#if defined(_WIN32)
+	if ((s = getenv("COMSPEC")) != NULL)
+		addenv(blk, "COMSPEC=%s", s);
+	if ((s = getenv("SYSTEMROOT")) != NULL)
+		addenv(blk, "SYSTEMROOT=%s", s);
+#else
+	if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
+		addenv(blk, "LD_LIBRARY_PATH=%s", s);
+#endif /* _WIN32 */
+
+	if ((s = getenv("PERLLIB")) != NULL)
+		addenv(blk, "PERLLIB=%s", s);
+
+	if (conn->request_info.remote_user != NULL) {
+		addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user);
+		addenv(blk, "%s", "AUTH_TYPE=Digest");
+	}
+
+	/* Add all headers as HTTP_* variables */
+	for (i = 0; i < conn->request_info.num_headers; i++) {
+		p = addenv(blk, "HTTP_%s=%s",
+		    conn->request_info.http_headers[i].name,
+		    conn->request_info.http_headers[i].value);
+
+		/* Convert variable name into uppercase, and change - to _ */
+		for (; *p != '=' && *p != '\0'; p++) {
+			if (*p == '-')
+				*p = '_';
+			*p = (char) toupper(* (unsigned char *) p);
+		}
+	}
+
+	/* Add user-specified variables */
+	lock_option(conn->ctx, OPT_CGI_ENV);
+	s = conn->ctx->options[OPT_CGI_ENV];
+	while ((s = next_option(s, &var_vec, NULL)) != NULL)
+		addenv(blk, "%.*s", var_vec.len, var_vec.ptr);
+	unlock_option(conn->ctx, OPT_CGI_ENV);
+
+	blk->vars[blk->nvars++] = NULL;
+	blk->buf[blk->len++] = '\0';
+
+	assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
+	assert(blk->len > 0);
+	assert(blk->len < (int) sizeof(blk->buf));
+}
+
+static void
+send_cgi(struct mg_connection *conn, const char *prog)
+{
+	int			headers_len, data_len, i, n;
+	const char		*status;
+	char			buf[MAX_REQUEST_SIZE], *pbuf;
+	struct mg_request_info	ri;
+	struct cgi_env_block	blk;
+	char			dir[FILENAME_MAX], *p;
+	int			fd_stdin[2], fd_stdout[2];
+	FILE			*in, *out;
+	pid_t			pid;
+
+	prepare_cgi_environment(conn, prog, &blk);
+
+	/* CGI must be executed in its own directory */
+	(void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog);
+	if ((p = strrchr(dir, DIRSEP)) != NULL)
+		*p++ = '\0';
+
+	pid = (pid_t) -1;
+	fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1;
+	in = out = NULL;
+
+	if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
+		send_error(conn, 500, http_500_error,
+		    "Cannot create CGI pipe: %s", strerror(ERRNO));
+		goto done;
+	} else if ((pid = spawn_process(conn, p, blk.buf, blk.vars,
+	    fd_stdin[0], fd_stdout[1], dir)) == (pid_t) -1) {
+		goto done;
+	} else if ((in = fdopen(fd_stdin[1], "wb")) == NULL ||
+	    (out = fdopen(fd_stdout[0], "rb")) == NULL) {
+		send_error(conn, 500, http_500_error,
+		    "fopen: %s", strerror(ERRNO));
+		goto done;
+	}
+
+	setbuf(in, NULL);
+	setbuf(out, NULL);
+
+	/*
+	 * spawn_process() must close those!
+	 * If we don't mark them as closed, close() attempt before
+	 * return from this function throws an exception on Windows.
+	 * Windows does not like when closed descriptor is closed again.
+	 */
+	fd_stdin[0] = fd_stdout[1] = -1;
+
+	/* Send POST data to the CGI process if needed */
+	if (!strcmp(conn->request_info.request_method, "POST") &&
+	    !handle_request_body(conn, in)) {
+		goto done;
+	}
+
+	/*
+	 * Now read CGI reply into a buffer. We need to set correct
+	 * status code, thus we need to see all HTTP headers first.
+	 * Do not send anything back to client, until we buffer in all
+	 * HTTP headers.
+	 */
+	data_len = 0;
+	headers_len = read_request(out, INVALID_SOCKET, NULL,
+	    buf, sizeof(buf), &data_len);
+	if (headers_len <= 0) {
+		send_error(conn, 500, http_500_error,
+		    "CGI program sent malformed HTTP headers: [%.*s]",
+		    data_len, buf);
+		goto done;
+	}
+	pbuf = buf;
+	buf[headers_len - 1] = '\0';
+	parse_http_headers(&pbuf, &ri);
+
+	/* Make up and send the status line */
+	status = get_header(&ri, "Status");
+	conn->request_info.status_code = status == NULL ? 200 : atoi(status);
+	(void) mg_printf(conn, "HTTP/1.1 %d OK\r\n",
+	    conn->request_info.status_code);
+
+	/* Send headers */
+	for (i = 0; i < ri.num_headers; i++)
+		(void) mg_printf(conn, "%s: %s\r\n",
+		    ri.http_headers[i].name,
+		    ri.http_headers[i].value);
+	(void) mg_write(conn, "\r\n", 2);
+
+	/* Send chunk of data that may be read after the headers */
+	conn->num_bytes_sent += mg_write(conn,
+	    buf + headers_len, data_len - headers_len);
+
+	/*
+	 * Read the rest of CGI output and send to the client. If read from
+	 * CGI returns 0, CGI has finished output. If it returns < 0,
+	 * some read error occured (CGI process terminated unexpectedly?)
+	 * If write to the client fails, the means client has disconnected
+	 * unexpectedly.
+	 * In all such cases, stop data exchange and do cleanup.
+	 */
+	do {
+		n = pull(out, INVALID_SOCKET, NULL, buf, sizeof(buf));
+		if (n > 0)
+			n = mg_write(conn, buf, n);
+		if (n > 0)
+			conn->num_bytes_sent += n;
+	} while (n > 0);
+
+done:
+	if (pid != (pid_t) -1)
+		kill(pid, SIGTERM);
+	if (fd_stdin[0] != -1)
+		(void) close(fd_stdin[0]);
+	if (fd_stdout[1] != -1)
+		(void) close(fd_stdout[1]);
+
+	if (in != NULL)
+		(void) fclose(in);
+	else if (fd_stdin[1] != -1)
+		(void) close(fd_stdin[1]);
+
+	if (out != NULL)
+		(void) fclose(out);
+	else if (fd_stdout[0] != -1)
+		(void) close(fd_stdout[0]);
+}
+#endif /* !NO_CGI */
+
+/*
+ * For a given PUT path, create all intermediate subdirectories
+ * for given path. Return 0 if the path itself is a directory,
+ * or -1 on error, 1 if OK.
+ */
+static int
+put_dir(const char *path)
+{
+	char		buf[FILENAME_MAX];
+	const char	*s, *p;
+	struct mgstat	st;
+	size_t		len;
+
+	for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
+		len = p - path;
+		assert(len < sizeof(buf));
+		(void) memcpy(buf, path, len);
+		buf[len] = '\0';
+
+		/* Try to create intermediate directory */
+		if (mg_stat(buf, &st) == -1 && mg_mkdir(buf, 0755) != 0)
+			return (-1);
+
+		/* Is path itself a directory ? */
+		if (p[1] == '\0')
+			return (0);
+	}
+
+	return (1);
+}
+
+static void
+put_file(struct mg_connection *conn, const char *path)
+{
+	struct mgstat	st;
+	FILE		*fp;
+	int		rc;
+
+	conn->request_info.status_code = mg_stat(path, &st) == 0 ? 200 : 201;
+
+	if (mg_get_header(conn, "Range")) {
+		send_error(conn, 501, "Not Implemented",
+		    "%s", "Range support for PUT requests is not implemented");
+	} else if ((rc = put_dir(path)) == 0) {
+		(void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n",
+		    conn->request_info.status_code);
+	} else if (rc == -1) {
+		send_error(conn, 500, http_500_error,
+		    "put_dir(%s): %s", path, strerror(ERRNO));
+	} else if ((fp = mg_fopen(path, "wb+")) == NULL) {
+		send_error(conn, 500, http_500_error,
+		    "fopen(%s): %s", path, strerror(ERRNO));
+	} else {
+		set_close_on_exec(fileno(fp));
+		if (handle_request_body(conn, fp))
+			(void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n",
+			    conn->request_info.status_code);
+		(void) fclose(fp);
+	}
+}
+
+#if !defined(NO_SSI)
+static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
+
+static void
+do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag,
+		int include_level)
+{
+	char	file_name[BUFSIZ], path[FILENAME_MAX], *p;
+	FILE	*fp;
+
+	/*
+	 * sscanf() is safe here, since send_ssi_file() also uses buffer
+	 * of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ.
+	 */
+	if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
+		/* File name is relative to the webserver root */
+		lock_option(conn->ctx, OPT_ROOT);
+		(void) mg_snprintf(conn, path, sizeof(path), "%s%c%s",
+		    conn->ctx->options[OPT_ROOT], DIRSEP, file_name);
+		unlock_option(conn->ctx, OPT_ROOT);
+	} else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {
+		/*
+		 * File name is relative to the webserver working directory
+		 * or it is absolute system path
+		 */
+		(void) mg_snprintf(conn, path, sizeof(path), "%s", file_name);
+	} else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
+		/* File name is relative to the currect document */
+		(void) mg_snprintf(conn, path, sizeof(path), "%s", ssi);
+		if ((p = strrchr(path, DIRSEP)) != NULL)
+			p[1] = '\0';
+		(void) mg_snprintf(conn, path + strlen(path),
+		    sizeof(path) - strlen(path), "%s", file_name);
+	} else {
+		cry(conn, "Bad SSI #include: [%s]", tag);
+		return;
+	}
+
+	if ((fp = mg_fopen(path, "rb")) == NULL) {
+		cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
+		    tag, path, strerror(ERRNO));
+	} else {
+		set_close_on_exec(fileno(fp));
+		if (match_extension(path,
+		    conn->ctx->options[OPT_SSI_EXTENSIONS])) {
+			send_ssi_file(conn, path, fp, include_level + 1);
+		} else {
+			send_opened_file_stream(conn, fp,
+			    UNKNOWN_CONTENT_LENGTH);
+		}
+		(void) fclose(fp);
+	}
+}
+
+static void
+do_ssi_exec(struct mg_connection *conn, char *tag)
+{
+	char	cmd[BUFSIZ];
+	FILE	*fp;
+
+	if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
+		cry(conn, "Bad SSI #exec: [%s]", tag);
+	} else if ((fp = popen(cmd, "r")) == NULL) {
+		cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
+	} else {
+		send_opened_file_stream(conn, fp, UNKNOWN_CONTENT_LENGTH);
+		(void) pclose(fp);
+	}
+}
+
+static void
+send_ssi_file(struct mg_connection *conn, const char *path, FILE *fp,
+		int include_level)
+{
+	char	buf[BUFSIZ];
+	int	ch, len, in_ssi_tag;
+
+	if (include_level > 10) {
+		cry(conn, "SSI #include level is too deep (%s)", path);
+		return;
+	}
+
+	in_ssi_tag = FALSE;
+	len = 0;
+
+	while ((ch = fgetc(fp)) != EOF) {
+		if (in_ssi_tag && ch == '>') {
+			in_ssi_tag = FALSE;
+			buf[len++] = ch & 0xff;
+			buf[len] = '\0';
+			assert(len <= (int) sizeof(buf));
+			if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
+				/* Not an SSI tag, pass it */
+				(void) mg_write(conn, buf, len);
+			} else {
+				if (!memcmp(buf + 5, "include", 7)) {
+					do_ssi_include(conn, path, buf + 12,
+					    include_level);
+				} else if (!memcmp(buf + 5, "exec", 4)) {
+					do_ssi_exec(conn, buf + 9);
+				} else {
+					cry(conn, "%s: unknown SSI "
+					    "command: \"%s\"", path, buf);
+				}
+			}
+			len = 0;
+		} else if (in_ssi_tag) {
+			if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
+				/* Not an SSI tag */
+				in_ssi_tag = FALSE;
+			} else if (len == (int) sizeof(buf) - 2) {
+				cry(conn, "%s: SSI tag is too large", path);
+				len = 0;
+			}
+			buf[len++] = ch & 0xff;
+		} else if (ch == '<') {
+			in_ssi_tag = TRUE;
+			if (len > 0)
+				(void) mg_write(conn, buf, len);
+			len = 0;
+			buf[len++] = ch & 0xff;
+		} else {
+			buf[len++] = ch & 0xff;
+			if (len == (int) sizeof(buf)) {
+				(void) mg_write(conn, buf, len);
+				len = 0;
+			}
+		}
+	}
+
+	/* Send the rest of buffered data */
+	if (len > 0)
+		(void) mg_write(conn, buf, len);
+
+}
+
+static void
+send_ssi(struct mg_connection *conn, const char *path)
+{
+	FILE	*fp;
+
+	if ((fp = mg_fopen(path, "rb")) == NULL) {
+		send_error(conn, 500, http_500_error,
+		    "fopen(%s): %s", path, strerror(ERRNO));
+	} else {
+		set_close_on_exec(fileno(fp));
+		(void) mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n"
+		    "Content-Type: text/html\r\nConnection: close\r\n\r\n");
+		send_ssi_file(conn, path, fp, 0);
+		(void) fclose(fp);
+	}
+}
+#endif /* !NO_SSI */
+
+void
+mg_authorize(struct mg_connection *conn)
+{
+	conn->embedded_auth = TRUE;
+}
+
+static bool_t
+check_embedded_authorization(struct mg_connection *conn)
+{
+	const struct callback	*cb;
+	bool_t			authorized;
+
+	authorized = TRUE;
+	cb = find_callback(conn->ctx, TRUE, conn->request_info.uri, -1);
+
+	if (cb != NULL) {
+		cb->func(conn, &conn->request_info, cb->user_data);
+		authorized = conn->embedded_auth;
+	}
+
+	return (authorized);
+}
+
+/*
+ * This is the heart of the Mongoose's logic.
+ * This function is called when the request is read, parsed and validated,
+ * and Mongoose must decide what action to take: serve a file, or
+ * a directory, or call embedded function, etcetera.
+ */
+static void
+analyze_request(struct mg_connection *conn)
+{
+	struct mg_request_info *ri = &conn->request_info;
+	char			path[FILENAME_MAX], *uri = ri->uri;
+	struct mgstat		st;
+	const struct callback	*cb;
+
+	if ((conn->request_info.query_string = strchr(uri, '?')) != NULL)
+		* conn->request_info.query_string++ = '\0';
+
+	(void) url_decode(uri, (int) strlen(uri), uri, strlen(uri) + 1, FALSE);
+	remove_double_dots_and_double_slashes(uri);
+	convert_uri_to_file_name(conn, uri, path, sizeof(path));
+
+	if (!check_authorization(conn, path)) {
+		send_authorization_request(conn);
+	} else if (check_embedded_authorization(conn) == FALSE) {
+		/*
+		 * Embedded code failed authorization. Do nothing here, since
+		 * an embedded code must handle this itself by either
+		 * showing proper error message, or redirecting to some
+		 * sort of login page, or something else.
+		 */
+	} else if ((cb = find_callback(conn->ctx, FALSE, uri, -1)) != NULL) {
+		if ((strcmp(ri->request_method, "POST") != 0 &&
+		    strcmp(ri->request_method, "PUT") != 0) ||
+		    handle_request_body(conn, NULL))
+			cb->func(conn, &conn->request_info, cb->user_data);
+	} else if (strstr(path, PASSWORDS_FILE_NAME)) {
+		/* Do not allow to view passwords files */
+		send_error(conn, 403, "Forbidden", "Access Forbidden");
+	} else if ((!strcmp(ri->request_method, "PUT") ||
+	    !strcmp(ri->request_method, "DELETE")) &&
+	    (conn->ctx->options[OPT_AUTH_PUT] == NULL ||
+	     !is_authorized_for_put(conn))) {
+		send_authorization_request(conn);
+	} else if (!strcmp(ri->request_method, "PUT")) {
+		put_file(conn, path);
+	} else if (!strcmp(ri->request_method, "DELETE")) {
+		if (mg_remove(path) == 0)
+			send_error(conn, 200, "OK", "");
+		else
+			send_error(conn, 500, http_500_error,
+			    "remove(%s): %s", path, strerror(ERRNO));
+	} else if (mg_stat(path, &st) != 0) {
+		send_error(conn, 404, "Not Found", "%s", "File not found");
+	} else if (st.is_directory && uri[strlen(uri) - 1] != '/') {
+		(void) mg_printf(conn,
+		    "HTTP/1.1 301 Moved Permanently\r\n"
+		    "Location: %s/\r\n\r\n", uri);
+	} else if (st.is_directory &&
+	    substitute_index_file(conn, path, sizeof(path), &st) == FALSE) {
+		if (is_true(conn->ctx->options[OPT_DIR_LIST])) {
+			send_directory(conn, path);
+		} else {
+			send_error(conn, 403, "Directory Listing Denied",
+			    "Directory listing denied");
+		}
+#if !defined(NO_CGI)
+	} else if (match_extension(path,
+	    conn->ctx->options[OPT_CGI_EXTENSIONS])) {
+		if (strcmp(ri->request_method, "POST") &&
+		    strcmp(ri->request_method, "GET")) {
+			send_error(conn, 501, "Not Implemented",
+			    "Method %s is not implemented", ri->request_method);
+		} else {
+			send_cgi(conn, path);
+		}
+#endif /* NO_CGI */
+#if !defined(NO_SSI)
+	} else if (match_extension(path,
+	    conn->ctx->options[OPT_SSI_EXTENSIONS])) {
+		send_ssi(conn, path);
+#endif /* NO_SSI */
+	} else if (is_not_modified(conn, &st)) {
+		send_error(conn, 304, "Not Modified", "");
+	} else {
+		send_file(conn, path, &st);
+	}
+}
+
+static void
+close_all_listening_sockets(struct mg_context *ctx)
+{
+	int	i;
+
+	for (i = 0; i < ctx->num_listeners; i++)
+		(void) closesocket(ctx->listeners[i].sock);
+	ctx->num_listeners = 0;
+}
+
+static bool_t
+set_ports_option(struct mg_context *ctx, const char *list)
+{
+	SOCKET		sock;
+	int		is_ssl;
+	struct vec	vec;
+	struct socket	*listener;
+
+	close_all_listening_sockets(ctx);
+	assert(ctx->num_listeners == 0);
+
+	while ((list = next_option(list, &vec, NULL)) != NULL) {
+
+		is_ssl	= vec.ptr[vec.len - 1] == 's' ? TRUE : FALSE;
+		listener = ctx->listeners + ctx->num_listeners;
+
+		if (ctx->num_listeners >=
+		    (int) (ARRAY_SIZE(ctx->listeners) - 1)) {
+			cry(fc(ctx), "%s", "Too many listeninig sockets");
+			return (FALSE);
+		} else if ((sock = mg_open_listening_port(ctx,
+		    vec.ptr, &listener->lsa)) == INVALID_SOCKET) {
+			cry(fc(ctx), "cannot bind to %.*s", vec.len, vec.ptr);
+			return (FALSE);
+		} else if (is_ssl == TRUE && ctx->ssl_ctx == NULL) {
+			(void) closesocket(sock);
+			cry(fc(ctx), "cannot add SSL socket, please specify "
+			    "-ssl_cert option BEFORE -ports option");
+			return (FALSE);
+		} else {
+			listener->sock = sock;
+			listener->is_ssl = is_ssl;
+			ctx->num_listeners++;
+		}
+	}
+
+	return (TRUE);
+}
+
+static void
+log_header(const struct mg_connection *conn, const char *header, FILE *fp)
+{
+	const char	*header_value;
+
+	if ((header_value = mg_get_header(conn, header)) == NULL) {
+		(void) fprintf(fp, "%s", " -");
+	} else {
+		(void) fprintf(fp, " \"%s\"", header_value);
+	}
+}
+
+static void
+log_access(const struct mg_connection *conn)
+{
+	const struct mg_request_info *ri;
+	char		date[64];
+
+	if (conn->ctx->access_log == NULL)
+		return;
+
+	(void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
+	    localtime(&conn->birth_time));
+
+	ri = &conn->request_info;
+
+	flockfile(conn->ctx->access_log);
+
+	(void) fprintf(conn->ctx->access_log,
+	    "%s - %s [%s] \"%s %s HTTP/%s\" %d %" UINT64_FMT "u",
+	    inet_ntoa(conn->client.rsa.u.sin.sin_addr),
+	    ri->remote_user == NULL ? "-" : ri->remote_user,
+	    date,
+	    ri->request_method ? ri->request_method : "-",
+	    ri->uri ? ri->uri : "-",
+	    ri->http_version,
+	    conn->request_info.status_code, conn->num_bytes_sent);
+	log_header(conn, "Referer", conn->ctx->access_log);
+	log_header(conn, "User-Agent", conn->ctx->access_log);
+	(void) fputc('\n', conn->ctx->access_log);
+	(void) fflush(conn->ctx->access_log);
+
+	funlockfile(conn->ctx->access_log);
+}
+
+static bool_t
+isbyte(int n) {
+	return (n >= 0 && n <= 255);
+}
+
+/*
+ * Verify given socket address against the ACL.
+ * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
+ */
+static int
+check_acl(struct mg_context *ctx, const char *list, const struct usa *usa)
+{
+	int		a, b, c, d, n, mask, allowed;
+	char		flag;
+	uint32_t	acl_subnet, acl_mask, remote_ip;
+	struct vec	vec;
+
+	(void) memcpy(&remote_ip, &usa->u.sin.sin_addr, sizeof(remote_ip));
+
+	/* If any ACL is set, deny by default */
+	allowed = '-';
+
+	while ((list = next_option(list, &vec, NULL)) != NULL) {
+
+		mask = 32;
+
+		if (sscanf(vec.ptr, "%c%d.%d.%d.%d%n",
+		    &flag, &a, &b, &c, &d, &n) != 5) {
+			cry(fc(ctx),
+			    "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
+			return (-1);
+		} else if (flag != '+' && flag != '-') {
+			cry(fc(ctx), "%s: flag must be + or -: [%s]",
+			    __func__, vec.ptr);
+			return (-1);
+		} else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
+			cry(fc(ctx),
+			    "%s: bad ip address: [%s]", __func__, vec.ptr);
+			return (-1);
+		} else if (sscanf(vec.ptr + n, "/%d", &mask) == 0) {
+			/* Do nothing, no mask specified */
+		} else if (mask < 0 || mask > 32) {
+			cry(fc(ctx), "%s: bad subnet mask: %d [%s]",
+			    __func__, n, vec.ptr);
+			return (-1);
+		}
+
+		acl_subnet = (a << 24) | (b << 16) | (c << 8) | d;
+		acl_mask = mask ? 0xffffffffU << (32 - mask) : 0;
+
+		if (acl_subnet == (ntohl(remote_ip) & acl_mask))
+			allowed = flag;
+	}
+
+	return (allowed == '+' ? 1 : 0);
+}
+
+static void
+add_to_set(SOCKET fd, fd_set *set, int *max_fd)
+{
+	FD_SET(fd, set);
+	if (fd > (SOCKET) *max_fd)
+		*max_fd = (int) fd;
+}
+
+/*
+ * Deallocate mongoose context, free up the resources
+ */
+static void
+mg_fini(struct mg_context *ctx)
+{
+	int	i;
+
+	close_all_listening_sockets(ctx);
+
+	/* Wait until all threads finish */
+	(void) pthread_mutex_lock(&ctx->thr_mutex);
+	while (ctx->num_threads > 0)
+		(void) pthread_cond_wait(&ctx->thr_cond, &ctx->thr_mutex);
+	(void) pthread_mutex_unlock(&ctx->thr_mutex);
+
+	/* Deallocate all registered callbacks */
+	for (i = 0; i < ctx->num_callbacks; i++)
+		if (ctx->callbacks[i].uri_regex != NULL)
+			free(ctx->callbacks[i].uri_regex);
+
+	/* Deallocate all options */
+	for (i = 0; i < NUM_OPTIONS; i++)
+		if (ctx->options[i] != NULL)
+			free(ctx->options[i]);
+
+	/* Close log files */
+	if (ctx->access_log)
+		(void) fclose(ctx->access_log);
+	if (ctx->error_log)
+		(void) fclose(ctx->error_log);
+
+	/* Deallocate SSL context */
+	if (ctx->ssl_ctx)
+		SSL_CTX_free(ctx->ssl_ctx);
+
+	/* Deallocate mutexes and condvars */
+	for (i = 0; i < NUM_OPTIONS; i++)
+		(void) pthread_mutex_destroy(&ctx->opt_mutex[i]);
+
+	(void) pthread_mutex_destroy(&ctx->thr_mutex);
+	(void) pthread_mutex_destroy(&ctx->bind_mutex);
+	(void) pthread_cond_destroy(&ctx->thr_cond);
+	(void) pthread_cond_destroy(&ctx->empty_cond);
+	(void) pthread_cond_destroy(&ctx->full_cond);
+
+	/* Signal mg_stop() that we're done */
+	ctx->stop_flag = 2;
+}
+
+#if !defined(_WIN32)
+static bool_t
+set_uid_option(struct mg_context *ctx, const char *uid)
+{
+	struct passwd	*pw;
+	int		retval = FALSE;
+
+	if ((pw = getpwnam(uid)) == NULL)
+		cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
+	else if (setgid(pw->pw_gid) == -1)
+		cry(fc(ctx), "%s: setgid(%s): %s",
+		    __func__, uid, strerror(errno));
+	else if (setuid(pw->pw_uid) == -1)
+		cry(fc(ctx), "%s: setuid(%s): %s",
+		    __func__, uid, strerror(errno));
+	else
+		retval = TRUE;
+
+	return (retval);
+}
+#endif /* !_WIN32 */
+
+#if !defined(NO_SSL)
+void
+mg_set_ssl_password_callback(struct mg_context *ctx, mg_spcb_t func)
+{
+	ctx->ssl_password_callback = func;
+}
+
+static pthread_mutex_t *ssl_mutexes;
+
+static void
+ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
+{
+	line = 0;	/* Unused */
+	file = NULL;	/* Unused */
+
+	if (mode & CRYPTO_LOCK)
+		(void) pthread_mutex_lock(&ssl_mutexes[mutex_num]);
+	else
+		(void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
+}
+
+static unsigned long
+ssl_id_callback(void)
+{
+	return ((unsigned long) pthread_self());
+}
+
+static bool_t
+load_dll(struct mg_context *ctx, const char *dll_name, struct ssl_func *sw)
+{
+	union {void *p; void (*fp)(void);} u;
+	void		*dll_handle;
+	struct ssl_func	*fp;
+
+	if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
+		cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
+		return (FALSE);
+	}
+
+	for (fp = sw; fp->name != NULL; fp++) {
+#ifdef _WIN32
+		/* GetProcAddress() returns pointer to function */
+		u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
+#else
+		/*
+		 * dlsym() on UNIX returns void *.
+		 * ISO C forbids casts of data pointers to function
+		 * pointers. We need to use a union to make a cast.
+		 */
+		u.p = dlsym(dll_handle, fp->name);
+#endif /* _WIN32 */
+		if (u.fp == NULL) {
+			cry(fc(ctx), "%s: cannot find %s", __func__, fp->name);
+			return (FALSE);
+		} else {
+			fp->ptr = u.fp;
+		}
+	}
+
+	return (TRUE);
+}
+
+/*
+ * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
+ */
+static bool_t
+set_ssl_option(struct mg_context *ctx, const char *pem)
+{
+	SSL_CTX		*CTX;
+	int		i, size, retval = FALSE;
+
+	if (load_dll(ctx, SSL_LIB, ssl_sw) == FALSE ||
+	    load_dll(ctx, CRYPTO_LIB, crypto_sw) == FALSE)
+		return (FALSE);
+
+	/* Initialize SSL crap */
+	SSL_library_init();
+
+	if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
+		cry(fc(ctx), "SSL_CTX_new error");
+	else if (ctx->ssl_password_callback != NULL)
+		SSL_CTX_set_default_passwd_cb(CTX, ctx->ssl_password_callback);
+
+	if (CTX != NULL && SSL_CTX_use_certificate_file(
+	    CTX, pem, SSL_FILETYPE_PEM) == 0)
+		cry(fc(ctx), "%s: cannot open %s", __func__, pem);
+	else if (CTX != NULL && SSL_CTX_use_PrivateKey_file(
+	    CTX, pem, SSL_FILETYPE_PEM) == 0)
+		cry(fc(ctx), "%s: cannot open %s", NULL, pem);
+	else
+		retval = TRUE;
+
+	/*
+	 * Initialize locking callbacks, needed for thread safety.
+	 * http://www.openssl.org/support/faq.html#PROG1
+	 */
+	size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
+	if ((ssl_mutexes = (pthread_mutex_t *) malloc(size)) == NULL) {
+		cry(fc(ctx), "%s: cannot allocate mutexes", __func__);
+		return (FALSE);
+	}
+
+	for (i = 0; i < CRYPTO_num_locks(); i++)
+		pthread_mutex_init(&ssl_mutexes[i], NULL);
+
+	CRYPTO_set_locking_callback(&ssl_locking_callback);
+	CRYPTO_set_id_callback(&ssl_id_callback);
+
+	/* Done with everything. Save the context. */
+	ctx->ssl_ctx = CTX;
+
+	return (retval);
+}
+#endif /* !NO_SSL */
+
+static bool_t
+open_log_file(struct mg_context *ctx, FILE **fpp, const char *path)
+{
+	bool_t	retval = TRUE;
+
+	if (*fpp != NULL)
+		(void) fclose(*fpp);
+
+	if (path == NULL) {
+		*fpp = NULL;
+	} else if ((*fpp = mg_fopen(path, "a")) == NULL) {
+		cry(fc(ctx), "%s(%s): %s", __func__, path, strerror(errno));
+		retval = FALSE;
+	} else {
+		set_close_on_exec(fileno(*fpp));
+	}
+
+	return (retval);
+}
+
+static bool_t
+set_alog_option(struct mg_context *ctx, const char *path)
+{
+	return (open_log_file(ctx, &ctx->access_log, path));
+}
+
+static bool_t
+set_elog_option(struct mg_context *ctx, const char *path)
+{
+	return (open_log_file(ctx, &ctx->error_log, path));
+}
+
+static bool_t
+set_gpass_option(struct mg_context *ctx, const char *path)
+{
+	struct mgstat	mgstat;
+	ctx = NULL;
+	return (mg_stat(path, &mgstat) == 0);
+}
+
+static bool_t
+set_max_threads_option(struct mg_context *ctx, const char *str)
+{
+	ctx->max_threads = atoi(str);
+	return (TRUE);
+}
+
+static bool_t
+set_acl_option(struct mg_context *ctx, const char *acl)
+{
+	struct usa	fake;
+
+	return (check_acl(ctx, acl, &fake) != -1);
+}
+
+static void admin_page(struct mg_connection *,
+		const struct mg_request_info *, void *);
+static bool_t
+set_admin_uri_option(struct mg_context *ctx, const char *uri)
+{
+	mg_set_uri_callback(ctx, uri, &admin_page, NULL);
+	return (TRUE);
+}
+
+/*
+ * Check if the comma-separated list of options has a format of key-value
+ * pairs: "k1=v1,k2=v2". Return FALSE if any entry has invalid key or value.
+ */
+static bool_t
+set_kv_list_option(struct mg_context *ctx, const char *str)
+{
+	const char	*list;
+	struct vec	key, value;
+
+	list = str;
+	while ((list = next_option(list, &key, &value)) != NULL)
+		if (key.len == 0 || value.len == 0) {
+			cry(fc(ctx), "Invalid list specified: [%s], "
+			    "expecting key1=value1,key2=value2,...", str);
+			return (FALSE);
+		}
+
+	return (TRUE);
+}
+
+static const struct mg_option known_options[] = {
+	{"root", "\tWeb root directory", ".", OPT_ROOT, NULL},
+	{"index_files",	"Index files", "index.html,index.htm,index.cgi",
+		OPT_INDEX_FILES, NULL},
+#if !defined(NO_SSL)
+	{"ssl_cert", "SSL certificate file", NULL,
+		OPT_SSL_CERTIFICATE, &set_ssl_option},
+#endif /* !NO_SSL */
+	{"ports", "Listening ports", NULL,
+		OPT_PORTS, &set_ports_option},
+	{"dir_list", "Directory listing", "yes",
+		OPT_DIR_LIST, NULL},
+	{"protect", "URI to htpasswd mapping", NULL,
+		OPT_PROTECT, &set_kv_list_option},
+#if !defined(NO_CGI)
+	{"cgi_ext", "CGI extensions", ".cgi,.pl,.php",
+		OPT_CGI_EXTENSIONS, NULL},
+	{"cgi_interp", "CGI interpreter to use with all CGI scripts", NULL,
+		OPT_CGI_INTERPRETER, NULL},
+	{"cgi_env", "Custom CGI enviroment variables", NULL,
+		OPT_CGI_ENV, &set_kv_list_option},
+#endif /* NO_CGI */
+	{"ssi_ext", "SSI extensions", ".shtml,.shtm",
+		OPT_SSI_EXTENSIONS, NULL},
+	{"auth_realm", "Authentication domain name", "mydomain.com",
+		OPT_AUTH_DOMAIN, NULL},
+	{"auth_gpass", "Global passwords file", NULL,
+		OPT_AUTH_GPASSWD, &set_gpass_option},
+	{"auth_PUT", "PUT,DELETE auth file", NULL,
+		OPT_AUTH_PUT, NULL},
+#if !defined(_WIN32)
+	{"uid", "\tRun as user", NULL, OPT_UID, &set_uid_option},
+#endif /* !_WIN32 */
+	{"access_log", "Access log file", NULL,
+		OPT_ACCESS_LOG, &set_alog_option},
+	{"error_log", "Error log file", NULL,
+		OPT_ERROR_LOG, &set_elog_option},
+	{"aliases", "Path=URI mappings", NULL,
+		OPT_ALIASES, &set_kv_list_option},
+	{"admin_uri", "Administration page URI", NULL,
+		OPT_ADMIN_URI, &set_admin_uri_option},
+	{"acl", "\tAllow/deny IP addresses/subnets", NULL,
+		OPT_ACL, &set_acl_option},
+	{"max_threads", "Maximum simultaneous threads to spawn", "100",
+		OPT_MAX_THREADS, &set_max_threads_option},
+	{"idle_time", "Time in seconds connection stays idle", "10",
+		OPT_IDLE_TIME, NULL},
+	{"mime_types", "Comma separated list of ext=mime_type pairs", NULL,
+		OPT_MIME_TYPES, &set_kv_list_option},
+	{NULL, NULL, NULL, 0, NULL}
+};
+
+static const struct mg_option *
+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);
+
+	return (NULL);
+}
+
+int
+mg_set_option(struct mg_context *ctx, const char *opt, const char *val)
+{
+	const struct mg_option	*option;
+	int			i, retval;
+
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: [%s]->[%s]", __func__, opt, val));
+	if (opt != NULL && (option = find_opt(opt)) != NULL) {
+		i = (int) (option - known_options);
+		lock_option(ctx, i);
+
+		if (option->setter != NULL)
+			retval = option->setter(ctx, val);
+		else
+			retval = TRUE;
+
+		/* Free old value if any */
+		if (ctx->options[option->index] != NULL)
+			free(ctx->options[option->index]);
+
+		/* Set new option value */
+		ctx->options[option->index] = val ? mg_strdup(val) : NULL;
+		unlock_option(ctx, i);
+
+		if (retval == FALSE)
+			cry(fc(ctx), "%s(%s): failure", __func__, opt);
+	} else {
+		cry(fc(ctx), "%s: No such option: [%s]", __func__, opt);
+		retval = -1;
+	}
+
+	return (retval);
+}
+
+void
+mg_show_usage_string(FILE *fp)
+{
+	const struct mg_option	*o;
+
+	(void) fprintf(stderr,
+	    "Mongoose version %s (c) Sergey Lyubka\n"
+	    "usage: mongoose [options] [config_file]\n", mg_version());
+
+	fprintf(fp, "  -A <htpasswd_file> <realm> <user> <passwd>\n");
+
+	for (o = known_options; o->name != NULL; o++) {
+		(void) fprintf(fp, "  -%s\t%s", o->name, o->description);
+		if (o->default_value != NULL)
+			fprintf(fp, " (default: \"%s\")", o->default_value);
+		fputc('\n', fp);
+	}
+}
+
+const char *
+mg_get_option(const struct mg_context *ctx, const char *option_name)
+{
+	const struct mg_option	*option;
+
+	if ((option = find_opt(option_name)) != NULL)
+		return (ctx->options[option->index]);
+	else
+		return (NULL);
+}
+
+static void
+admin_page(struct mg_connection *conn, const struct mg_request_info *ri,
+			   void *user_data)
+{
+	const struct mg_option	*option;
+	const char		*option_name, *option_value;
+
+	user_data = NULL; /* Unused */
+
+	(void) mg_printf(conn,
+	"HTTP/1.1 200 OK\r\n"
+			"Content-Type: text/html\r\n\r\n"
+			"<html><body><h1>Mongoose v. %s</h1>", mg_version());
+
+	if (!strcmp(ri->request_method, "POST")) {
+		option_name = mg_get_var(conn, "o");
+		option_value = mg_get_var(conn, "v");
+		if (mg_set_option(conn->ctx,
+		    option_name, option_value) == -1) {
+			(void) mg_printf(conn,
+			    "<p style=\"background: red\">Error setting "
+			    "option \"%s\"</p>",
+			    option_name ? option_name : "(null)");
+		} else {
+			(void) mg_printf(conn,
+			    "<p style=\"color: green\">Saved: %s=%s</p>",
+			    option_name, option_value ? option_value : "NULL");
+		}
+	}
+
+	/* Print table with all options */
+	(void) mg_printf(conn, "%s", "<table border=\"1\""
+			"<tr><th>Option</th><th>Description</th>"
+					"<th colspan=2>Value</th></tr>");
+
+	for (option = known_options; option->name != NULL; option++) {
+		option_value = mg_get_option(conn->ctx, option->name);
+		if (option_value == NULL)
+			option_value = "";
+		(void) mg_printf(conn,
+		    "<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>",
+		    option->name, option->description,
+		    option->name, option_value);
+	}
+
+	(void) mg_printf(conn, "%s", "</table></body></html>");
+}
+
+static void
+reset_per_request_attributes(struct mg_connection *conn)
+{
+	if (conn->request_info.remote_user != NULL) {
+		free((void *) conn->request_info.remote_user);
+		conn->request_info.remote_user = NULL;
+	}
+	if (conn->free_post_data && conn->request_info.post_data != NULL) {
+		free((void *) conn->request_info.post_data);
+		conn->request_info.post_data = NULL;
+	}
+}
+
+static void
+close_socket_gracefully(struct mg_connection *conn, SOCKET sock)
+{
+	char	buf[BUFSIZ];
+	int	n;
+
+	/* Send FIN to the client */
+	(void) shutdown(sock, SHUT_WR);
+	set_non_blocking_mode(conn, sock);
+
+	/*
+	 * Read and discard pending data. If we do not do that and close the
+	 * socket, the data in the send buffer may be discarded. This
+	 * behaviour is seen on Windows, when client keeps sending data
+	 * when server decide to close the connection; then when client
+	 * does recv() it gets no data back.
+	 */
+	do {
+		n = pull(NULL, sock, NULL, buf, sizeof(buf));
+	} while (n > 0);
+
+	/* Now we know that our FIN is ACK-ed, safe to close */
+	(void) closesocket(sock);
+}
+
+static void
+close_connection(struct mg_connection *conn)
+{
+	reset_per_request_attributes(conn);
+
+	if (conn->ssl)
+		SSL_free(conn->ssl);
+
+	if (conn->client.sock != INVALID_SOCKET)
+		close_socket_gracefully(conn, conn->client.sock);
+}
+
+static void
+reset_connection_attributes(struct mg_connection *conn)
+{
+	reset_per_request_attributes(conn);
+	conn->free_post_data = FALSE;
+	conn->request_info.status_code = -1;
+	conn->num_bytes_sent = 0;
+	(void) memset(&conn->request_info, 0, sizeof(conn->request_info));
+}
+
+static void
+shift_to_next(struct mg_connection *conn, char *buf, int req_len, int *nread)
+{
+	uint64_t	cl;
+	int		over_len, body_len;
+
+	cl = get_content_length(conn);
+	over_len = *nread - req_len;
+	assert(over_len >= 0);
+
+	if (cl == UNKNOWN_CONTENT_LENGTH) {
+		body_len = 0;
+	} else if (cl < (uint64_t) over_len) {
+		body_len = (int) cl;
+	} else {
+		body_len = over_len;
+	}
+
+	*nread -= req_len + body_len;
+	(void) memmove(buf, buf + req_len + body_len, *nread);
+}
+
+static void
+process_new_connection(struct mg_connection *conn)
+{
+	struct mg_request_info *ri = &conn->request_info;
+	char	buf[MAX_REQUEST_SIZE];
+	int	request_len, nread;
+
+	nread = 0;
+	reset_connection_attributes(conn);
+
+	/* If next request is not pipelined, read it in */
+	if ((request_len = get_request_len(buf, (size_t) nread)) == 0)
+		request_len = read_request(NULL, conn->client.sock,
+		    conn->ssl, buf, sizeof(buf), &nread);
+	assert(nread >= request_len);
+
+	if (request_len <= 0)
+		return;	/* Remote end closed the connection */
+
+	/* 0-terminate the request: parse_request uses sscanf */
+	buf[request_len - 1] = '\0';
+
+	if (parse_http_request(buf, ri, &conn->client.rsa)) {
+		if (strcmp(ri->http_version, "1.0") != 0 &&
+		    strcmp(ri->http_version, "1.1") != 0) {
+			send_error(conn, 505,
+			    "HTTP version not supported",
+			    "%s", "Weird HTTP version");
+			log_access(conn);
+		} else {
+			ri->post_data = buf + request_len;
+			ri->post_data_len = nread - request_len;
+			conn->birth_time = time(NULL);
+			analyze_request(conn);
+			log_access(conn);
+			shift_to_next(conn, buf, request_len, &nread);
+		}
+	} else {
+		/* Do not put garbage in the access log */
+		send_error(conn, 400, "Bad Request",
+		    "Can not parse request: [%.*s]", nread, buf);
+	}
+
+}
+
+/*
+ * Worker threads take accepted socket from the queue
+ */
+static bool_t
+get_socket(struct mg_context *ctx, struct socket *sp)
+{
+	struct timespec	ts;
+
+	(void) pthread_mutex_lock(&ctx->thr_mutex);
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: thread %p: going idle",
+	    __func__, (void *) pthread_self()));
+
+	/* If the queue is empty, wait. We're idle at this point. */
+	ctx->num_idle++;
+	while (ctx->sq_head == ctx->sq_tail) {
+		ts.tv_nsec = 0;
+		ts.tv_sec = time(NULL) + atoi(ctx->options[OPT_IDLE_TIME]) + 1;
+		if (pthread_cond_timedwait(&ctx->empty_cond,
+		    &ctx->thr_mutex, &ts) != 0) {
+			/* Timeout! release the mutex and return */
+			(void) pthread_mutex_unlock(&ctx->thr_mutex);
+			return (FALSE);
+		}
+	}
+	assert(ctx->sq_head > ctx->sq_tail);
+
+	/* We're going busy now: got a socket to process! */
+	ctx->num_idle--;
+
+	/* Copy socket from the queue and increment tail */
+	*sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
+	ctx->sq_tail++;
+	DEBUG_TRACE((DEBUG_MGS_PREFIX
+	    "%s: thread %p grabbed socket %d, going busy",
+	    __func__, (void *) pthread_self(), sp->sock));
+
+	/* Wrap pointers if needed */
+	while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
+		ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
+		ctx->sq_head -= ARRAY_SIZE(ctx->queue);
+	}
+
+	pthread_cond_signal(&ctx->full_cond);
+	(void) pthread_mutex_unlock(&ctx->thr_mutex);
+
+	return (TRUE);
+}
+
+static void
+worker_thread(struct mg_context *ctx)
+{
+	struct mg_connection	conn;
+
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: thread %p starting",
+	    __func__, (void *) pthread_self()));
+
+	(void) memset(&conn, 0, sizeof(conn));
+
+	while (get_socket(ctx, &conn.client) == TRUE) {
+		conn.birth_time = time(NULL);
+		conn.ctx = ctx;
+
+		if (conn.client.is_ssl &&
+		    (conn.ssl = SSL_new(conn.ctx->ssl_ctx)) == NULL) {
+			cry(&conn, "%s: SSL_new: %d", __func__, ERRNO);
+		} else if (conn.client.is_ssl &&
+		    SSL_set_fd(conn.ssl, conn.client.sock) != 1) {
+			cry(&conn, "%s: SSL_set_fd: %d", __func__, ERRNO);
+		} else if (conn.client.is_ssl && SSL_accept(conn.ssl) != 1) {
+			cry(&conn, "%s: SSL handshake error", __func__);
+		} else {
+			process_new_connection(&conn);
+		}
+
+		close_connection(&conn);
+	}
+
+	/* Signal master that we're done with connection and exiting */
+	pthread_mutex_lock(&ctx->thr_mutex);
+	ctx->num_threads--;
+	ctx->num_idle--;
+	pthread_cond_signal(&ctx->thr_cond);
+	assert(ctx->num_threads >= 0);
+	pthread_mutex_unlock(&ctx->thr_mutex);
+
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: thread %p exiting",
+	    __func__, (void *) pthread_self()));
+}
+
+/*
+ * Master thread adds accepted socket to a queue
+ */
+static void
+put_socket(struct mg_context *ctx, const struct socket *sp)
+{
+	(void) pthread_mutex_lock(&ctx->thr_mutex);
+
+	/* If the queue is full, wait */
+	while (ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue))
+		(void) pthread_cond_wait(&ctx->full_cond, &ctx->thr_mutex);
+	assert(ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue));
+
+	/* Copy socket to the queue and increment head */
+	ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
+	ctx->sq_head++;
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: queued socket %d",
+	    __func__, sp->sock));
+
+	/* If there are no idle threads, start one */
+	if (ctx->num_idle == 0 && ctx->num_threads < ctx->max_threads) {
+		if (start_thread(ctx,
+		    (mg_thread_func_t) worker_thread, ctx) != 0)
+			cry(fc(ctx), "Cannot start thread: %d", ERRNO);
+		else
+			ctx->num_threads++;
+	}
+
+	pthread_cond_signal(&ctx->empty_cond);
+	(void) pthread_mutex_unlock(&ctx->thr_mutex);
+}
+
+static void
+accept_new_connection(const struct socket *listener, struct mg_context *ctx)
+{
+	struct socket	accepted;
+
+	accepted.rsa.len = sizeof(accepted.rsa.u.sin);
+	accepted.lsa = listener->lsa;
+	if ((accepted.sock = accept(listener->sock,
+	    &accepted.rsa.u.sa, &accepted.rsa.len)) == INVALID_SOCKET)
+		return;
+
+	lock_option(ctx, OPT_ACL);
+	if (ctx->options[OPT_ACL] != NULL &&
+	    !check_acl(ctx, ctx->options[OPT_ACL], &accepted.rsa)) {
+		cry(fc(ctx), "%s: %s is not allowed to connect",
+		    __func__, inet_ntoa(accepted.rsa.u.sin.sin_addr));
+		(void) closesocket(accepted.sock);
+		unlock_option(ctx, OPT_ACL);
+		return;
+	}
+	unlock_option(ctx, OPT_ACL);
+
+	/* Put accepted socket structure into the queue */
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: accepted socket %d",
+	    __func__, accepted.sock));
+	accepted.is_ssl = listener->is_ssl;
+	put_socket(ctx, &accepted);
+}
+
+static void
+master_thread(struct mg_context *ctx)
+{
+	fd_set		read_set;
+	struct timeval	tv;
+	int		i, max_fd;
+
+	while (ctx->stop_flag == 0) {
+		FD_ZERO(&read_set);
+		max_fd = -1;
+
+		/* Add listening sockets to the read set */
+		lock_option(ctx, OPT_PORTS);
+		for (i = 0; i < ctx->num_listeners; i++)
+			add_to_set(ctx->listeners[i].sock, &read_set, &max_fd);
+		unlock_option(ctx, OPT_PORTS);
+
+		tv.tv_sec = 1;
+		tv.tv_usec = 0;
+
+		if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {
+#ifdef _WIN32
+			/*
+			 * On windows, if read_set and write_set are empty,
+			 * select() returns "Invalid parameter" error
+			 * (at least on my Windows XP Pro). So in this case,
+			 * we sleep here.
+			 */
+			sleep(1);
+#endif /* _WIN32 */
+		} else {
+			lock_option(ctx, OPT_PORTS);
+			for (i = 0; i < ctx->num_listeners; i++)
+				if (FD_ISSET(ctx->listeners[i].sock, &read_set))
+					accept_new_connection(
+					    ctx->listeners + i, ctx);
+			unlock_option(ctx, OPT_PORTS);
+		}
+	}
+
+	/* Stop signal received: somebody called mg_stop. Quit. */
+	mg_fini(ctx);
+}
+
+void
+mg_stop(struct mg_context *ctx)
+{
+	ctx->stop_flag = 1;
+
+	/* Wait until mg_fini() stops */
+	while (ctx->stop_flag != 2)
+		(void) sleep(1);
+
+	assert(ctx->num_threads == 0);
+	free(ctx);
+
+#if defined(_WIN32)
+	(void) WSACleanup();
+#endif /* _WIN32 */
+}
+
+struct mg_context *
+mg_start(void)
+{
+	struct mg_context	*ctx;
+	const struct mg_option	*option;
+	int			i;
+
+#if defined(_WIN32)
+	WSADATA data;
+	WSAStartup(MAKEWORD(2,2), &data);
+#endif /* _WIN32 */
+
+	if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
+		cry(fc(ctx), "cannot allocate mongoose context");
+		return (NULL);
+	}
+
+	ctx->error_log = stderr;
+	mg_set_log_callback(ctx, builtin_error_log);
+
+	/* Initialize options. First pass: set default option values */
+	for (option = known_options; option->name != NULL; option++)
+		ctx->options[option->index] = option->default_value == NULL ?
+			NULL : mg_strdup(option->default_value);
+
+	/* Call setter functions */
+	for (option = known_options; option->name != NULL; option++)
+		if (option->setter != NULL &&
+		    ctx->options[option->index] != NULL)
+			if (option->setter(ctx,
+			    ctx->options[option->index]) == FALSE) {
+				mg_fini(ctx);
+				return (NULL);
+			}
+
+	DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: root [%s]",
+	    __func__, ctx->options[OPT_ROOT]));
+
+#if !defined(_WIN32)
+	/*
+	 * Ignore SIGPIPE signal, so if browser cancels the request, it
+	 * won't kill the whole process.
+	 */
+	(void) signal(SIGPIPE, SIG_IGN);
+#endif /* _WIN32 */
+
+	/* Initialize options mutexes */
+	for (i = 0; i < NUM_OPTIONS; i++)
+		(void) pthread_mutex_init(&ctx->opt_mutex[i], NULL);
+
+	(void) pthread_mutex_init(&ctx->thr_mutex, NULL);
+	(void) pthread_mutex_init(&ctx->bind_mutex, NULL);
+	(void) pthread_cond_init(&ctx->thr_cond, NULL);
+	(void) pthread_cond_init(&ctx->empty_cond, NULL);
+	(void) pthread_cond_init(&ctx->full_cond, NULL);
+
+	/* Start master (listening) thread */
+	start_thread(ctx, (mg_thread_func_t) master_thread, ctx);
+
+	return (ctx);
+}

Added: dss/trunk/external/mongoose/mongoose.h
===================================================================
--- dss/trunk/external/mongoose/mongoose.h	                        (rev 0)
+++ dss/trunk/external/mongoose/mongoose.h	2009-09-03 09:48:59 UTC (rev 8729)
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2004-2009 Sergey Lyubka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * $Id$
+ */
+
+#ifndef MONGOOSE_HEADER_INCLUDED
+#define	MONGOOSE_HEADER_INCLUDED
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct mg_context;	/* Handle for the HTTP service itself	*/
+struct mg_connection;	/* Handle for the individual connection	*/
+
+
+/*
+ * This structure contains full information about the HTTP request.
+ * It is passed to the user-specified callback function as a parameter.
+ */
+struct mg_request_info {
+	char	*request_method;	/* "GET", "POST", etc	*/
+	char	*uri;			/* Normalized URI	*/
+	char	*http_version;		/* E.g. "1.0", "1.1"	*/
+	char	*query_string;		/* \0 - terminated	*/
+	char	*post_data;		/* POST data buffer	*/
+	char	*remote_user;		/* Authenticated user	*/
+	long	remote_ip;		/* Client's IP address	*/
+	int	remote_port;		/* Client's port	*/
+	int	post_data_len;		/* POST buffer length	*/
+	int	status_code;		/* HTTP status code	*/
+	int	num_headers;		/* Number of headers	*/
+	struct mg_header {
+		char	*name;		/* HTTP header name	*/
+		char	*value;		/* HTTP header value	*/
+	} http_headers[64];		/* Maximum 64 headers	*/
+};
+
+
+/*
+ * User-defined callback function prototype for URI handling, error handling,
+ * or logging server messages.
+ */
+typedef void (*mg_callback_t)(struct mg_connection *,
+		const struct mg_request_info *info, void *user_data);
+
+
+/*
+ * Start the web server.
+ * This must be the first function called by the application.
+ * It creates a serving thread, and returns a context structure that
+ * can be used to alter the configuration, and stop the server.
+ */
+struct mg_context *mg_start(void);
+
+
+/*
+ * Stop the web server.
+ * Must be called last, when an application wants to stop the web server and
+ * release all associated resources. This function blocks until all Mongoose
+ * threads are stopped. Context pointer becomes invalid.
+ */
+void mg_stop(struct mg_context *);
+
+
+/*
+ * Return current value of a particular option.
+ */
+const char *mg_get_option(const struct mg_context *, const char *option_name);
+
+
+/*
+ * Set a value for a particular option.
+ * Mongoose makes an internal copy of the option value string, which must be
+ * valid nul-terminated ASCII or UTF-8 string. It is safe to change any option
+ * at any time. The order of setting various options is also irrelevant with
+ * one exception: if "ports" option contains SSL listening ports, a "ssl_cert"
+ * option must be set BEFORE the "ports" option.
+ * Return value:
+ *	-1 if option is unknown
+ *	0  if mg_set_option() failed
+ *	1  if mg_set_option() succeeded
+ */
+int mg_set_option(struct mg_context *, const char *opt_name, const char *value);
+
+
+/*
+ * Add, edit or delete the entry in the passwords file.
+ * This function allows an application to manipulate .htpasswd files on the
+ * fly by adding, deleting and changing user records. This is one of the two
+ * ways of implementing authentication on the server side. For another,
+ * cookie-based way please refer to the examples/authentication.c in the
+ * source tree.
+ * If password is not NULL, entry is added (or modified if already exists).
+ * If password is NULL, entry is deleted. Return:
+ *	1 on success
+ *	0 on error
+ */
+int mg_modify_passwords_file(struct mg_context *ctx, const char *file_name,
+		const char *user_name, const char *password);
+
+
+/*
+ * Register URI handler.
+ * It is possible to handle many URIs if using * in the uri_regex, which
+ * matches zero or more characters. user_data pointer will be passed to the
+ * handler as a third parameter. If func is NULL, then the previously installed
+ * handler for this uri_regex is removed.
+ */
+void mg_set_uri_callback(struct mg_context *ctx, const char *uri_regex,
+		mg_callback_t func, void *user_data);
+
+
+/*
+ * Register HTTP error handler.
+ * An application may use that function if it wants to customize the error
+ * page that user gets on the browser (for example, 404 File Not Found message).
+ * It is possible to specify a error handler for all errors by passing 0 as
+ * error_code. That '0' error handler must be set last, if more specific error
+ * handlers are also used. The actual error code value can be taken from
+ * the request info structure that is passed to the callback.
+ */
+void mg_set_error_callback(struct mg_context *ctx, int error_code,
+		mg_callback_t func, void *user_data);
+
+
+/*
+ * Register authorization handler.
+ * This function provides a mechanism to implement custom authorization,
+ * for example cookie based (look at examples/authorization.c).
+ * The callback function must analyze the request, and make its own judgement
+ * on wether it should be authorized or not. After the decision is made, a
+ * callback must call mg_authorize() if the request is authorized.
+ */
+void mg_set_auth_callback(struct mg_context *ctx, const char *uri_regex,
+		mg_callback_t func, void *user_data);
+
+
+/*
+ * Register log handler.
+ * By default, Mongoose logs all error messages to stderr. If "error_log"
+ * option is specified, the errors are written in the specified file. However,
+ * if an application registers its own log handler, Mongoose will not log
+ * anything but call the handler function, passing an error message as
+ * "user_data" callback argument.
+ */
+void mg_set_log_callback(struct mg_context *ctx, mg_callback_t func);
+
+
+/*
+ * Register SSL password handler.
+ * This is needed only if SSL certificate asks for a password. Instead of
+ * prompting for a password on a console a specified function will be called.
+ */
+typedef int (*mg_spcb_t)(char *buf, int num, int w, void *key);
+void mg_set_ssl_password_callback(struct mg_context *ctx, mg_spcb_t func);
+
+
+/*
+ * Send data to the browser.
+ * Return number of bytes sent. If the number of bytes sent is less then
+ * requested or equals to -1, network error occured, usually meaning the
+ * remote side has closed the connection.
+ */
+int mg_write(struct mg_connection *, const void *buf, int len);
+
+
+/*
+ * Send data to the browser using printf() semantics.
+ * Works exactly like mg_write(), but allows to do message formatting.
+ * Note that mg_printf() uses internal buffer of size MAX_REQUEST_SIZE
+ * (8 Kb by default) as temporary message storage for formatting. Do not
+ * print data that is bigger than that, otherwise it will be truncated.
+ * Return number of bytes sent.
+ */
+int mg_printf(struct mg_connection *, const char *fmt, ...);
+
+
+/*
+ * Read data from the remote or local end.
+ */
+int mg_read(struct mg_connection *, int local, void *buf, int len);
+
+/*
+ * Get the value of particular HTTP header.
+ * This is a helper function. It traverses request_info->http_headers array,
+ * and if the header is present in the array, returns its value. If it is
+ * not present, NULL is returned.
+ */
+const char *mg_get_header(const struct mg_connection *, const char *hdr_name);
+
+
+/*
+ * Authorize the request.
+ * See the documentation for mg_set_auth_callback() function.
+ */
+void mg_authorize(struct mg_connection *);
+
+
+/*
+ * Get a value of particular form variable.
+ * Both query string (whatever comes after '?' in the URL) and a POST buffer
+ * are scanned. If a variable is specified in both query string and POST
+ * buffer, POST buffer wins. Return value:
+ *	NULL      if the variable is not found
+ *	non-NULL  if found. NOTE: this returned value is dynamically allocated
+ *		  and is subject to mg_free() when no longer needed. It is
+ *		  an application's responsibility to mg_free() the variable.
+ */
+char *mg_get_var(const struct mg_connection *, const char *var_name);
+
+
+/*
+ * Free up memory returned by mg_get_var().
+ */
+void mg_free(char *var);
+
+
+/*
+ * Return Mongoose version.
+ */
+const char *mg_version(void);
+
+
+/*
+ * Print command line usage string.
+ */
+void mg_show_usage_string(FILE *fp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MONGOOSE_HEADER_INCLUDED */

Deleted: dss/trunk/external/shttpd/CMakeLists.txt
===================================================================
--- dss/trunk/external/shttpd/CMakeLists.txt	2009-09-03 09:34:09 UTC (rev 8728)
+++ dss/trunk/external/shttpd/CMakeLists.txt	2009-09-03 09:48:59 UTC (rev 8729)
@@ -1,3 +0,0 @@
-add_library(shttpd auth.c cgi.c compat_unix.c config.c io_cgi.c io_dir.c
-io_emb.c io_file.c io_socket.c io_ssi.c io_ssl.c log.c md5.c shttpd.c
-string.c)

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

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

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

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

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

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

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

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

Deleted: dss/trunk/external/shttpd/compat_wince.c
===================================================================
--- dss/trunk/external/shttpd/compat_wince.c	2009-09-03 09:34:09 UTC (rev 8728)
+++ dss/trunk/external/shttpd/compat_wince.c	2009-09-03 09:48:59 UTC (rev 8729)
@@ -1,1593 +0,0 @@
-/*
- vi:ts=8:sw=8:noet
- * Copyright (c) 2006 Luke Dunstan <infidel at users.sourceforge.net>
- * Partly based on code by David Kashtan, Validus Medical Systems
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-/*
- * This file provides various functions that are available on desktop Windows
- * but not on Windows CE
- */
-
-#ifdef _MSC_VER
-/* Level 4 warnings caused by windows.h */
-#pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int
-#pragma warning(disable : 4115) // named type definition in parentheses
-#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
-#pragma warning(disable : 4514) // unreferenced inline function has been removed
-#pragma warning(disable : 4244) // conversion from 'int ' to 'unsigned short ', possible loss of data
-#pragma warning(disable : 4100) // unreferenced formal parameter
-#endif
-
-#include <windows.h>
-#include <stdlib.h>
-
-#include "compat_wince.h"
-
-
-static WCHAR *to_wide_string(LPCSTR pStr)
-{
-	int nwide;
-	WCHAR *buf;
-
-	if(pStr == NULL)
-		return NULL;
-	nwide = MultiByteToWideChar(CP_ACP, 0, pStr, -1, NULL, 0);
-	if(nwide == 0)
-		return NULL;
-	buf = malloc(nwide * sizeof(WCHAR));
-	if(buf == NULL) {
-		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-		return NULL;
-	}
-	if(MultiByteToWideChar(CP_ACP, 0, pStr, -1, buf, nwide) == 0) {
-		free(buf);
-		return NULL;
-	}
-	return buf;
-}
-
-FILE *fdopen(int handle, const char *mode)
-{
-	WCHAR *wmode = to_wide_string(mode);
-	FILE *result;
-
-	if(wmode != NULL)
-		result = _wfdopen((void *)handle, wmode);
-	else
-		result = NULL;
-	free(wmode);
-	return result;
-}
-
-/*
- *	Time conversion constants
- */
-#define FT_EPOCH (116444736000000000i64)
-#define	FT_TICKS (10000000i64)
-
- /*
- *	Convert a FILETIME to a time_t
- */
-static time_t convert_FILETIME_to_time_t(FILETIME *File_Time)
-{
-	__int64 Temp;
-
-	/*
-	 *	Convert the FILETIME structure to 100nSecs since 1601 (as a 64-bit value)
-	 */
-	Temp = (((__int64)File_Time->dwHighDateTime) << 32) + (__int64)File_Time->dwLowDateTime;
-	/*
-	 *	Convert to seconds from 1970
-	 */
-	return((time_t)((Temp - FT_EPOCH) / FT_TICKS));
-}
-
-/*
- *	Convert a FILETIME to a tm structure
- */
-static struct tm *Convert_FILETIME_To_tm(FILETIME *File_Time)
-{
-	SYSTEMTIME System_Time;
-	static struct tm tm = {0};
-	static const short Day_Of_Year_By_Month[12] = {(short)(0),
-						       (short)(31),
-						       (short)(31 + 28),
-						       (short)(31 + 28 + 31),
-						       (short)(31 + 28 + 31 + 30),
-						       (short)(31 + 28 + 31 + 30 + 31),
-						       (short)(31 + 28 + 31 + 30 + 31 + 30),
-						       (short)(31 + 28 + 31 + 30 + 31 + 30 + 31),
-						       (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31),
-						       (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30),
-						       (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31),
-						       (short)(31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)};
-
-
-	/*
-	 *	Turn the FILETIME into a SYSTEMTIME
-	 */
-	FileTimeToSystemTime(File_Time, &System_Time);
-	/*
-	 *	Use SYSTEMTIME to fill in the tm structure
-	 */
-	tm.tm_sec = System_Time.wSecond;
-	tm.tm_min = System_Time.wMinute;
-	tm.tm_hour = System_Time.wHour;
-	tm.tm_mday = System_Time.wDay;
-	tm.tm_mon = System_Time.wMonth - 1;
-	tm.tm_year = System_Time.wYear - 1900;
-	tm.tm_wday = System_Time.wDayOfWeek;
-	tm.tm_yday = Day_Of_Year_By_Month[tm.tm_mon] + tm.tm_mday - 1;
-	if (tm.tm_mon >= 2) {
-		/*
-		 *	Check for leap year (every 4 years but not every 100 years but every 400 years)
-		 */
-		if ((System_Time.wYear % 4) == 0) {
-			/*
-			 *	It Is a 4th year
-			 */
-			if ((System_Time.wYear % 100) == 0) {
-				/*
-				 *	It is a 100th year
-				 */
-				if ((System_Time.wYear % 400) == 0) {
-					/*
-					 *	It is a 400th year: It is a leap year
-					 */
-					tm.tm_yday++;
-				}
-			} else {
-				/*
-				 *	It is not a 100th year: It is a leap year
-				 */
-				tm.tm_yday++;
-			}
-		}
-	}
-	return(&tm);
-}
-
-/*
- *	Convert a time_t to a FILETIME
- */
-static void Convert_time_t_To_FILETIME(time_t Time, FILETIME *File_Time)
-{
-	__int64 Temp;
-
-	/*
-	 *	Use 64-bit calculation to convert seconds since 1970 to
-	 *	100nSecs since 1601
-	 */
-	Temp = ((__int64)Time * FT_TICKS) + FT_EPOCH;
-	/*
-	 *	Put it into the FILETIME structure
-	 */
-	File_Time->dwLowDateTime = (DWORD)Temp;
-	File_Time->dwHighDateTime = (DWORD)(Temp >> 32);
-}
-
-/*
- *	Convert a tm structure to a FILETIME
- */
-static FILETIME *Convert_tm_To_FILETIME(struct tm *tm)
-{
-	SYSTEMTIME System_Time;
-	static FILETIME File_Time = {0};
-
-	/*
-	 *	Use the tm structure to fill in a SYSTEM
-	 */
-	System_Time.wYear = tm->tm_year + 1900;
-	System_Time.wMonth = tm->tm_mon + 1;
-	System_Time.wDayOfWeek = tm->tm_wday;
-	System_Time.wDay = tm->tm_mday;
-	System_Time.wHour = tm->tm_hour;
-	System_Time.wMinute = tm->tm_min;
-	System_Time.wSecond = tm->tm_sec;
-	System_Time.wMilliseconds = 0;
-	/*
-	 *	Convert it to a FILETIME and return it
-	 */
-	SystemTimeToFileTime(&System_Time, &File_Time);
-	return(&File_Time);
-}
-
-
-/************************************************************************/
-/*									*/
-/*	Errno emulation:  There is no errno on Windows/CE and we need	*/
-/*			  to make it per-thread.  So we have a function	*/
-/*			  that returns a pointer to the errno for the	*/
-/*			  current thread.				*/
-/*									*/
-/*			  If there is ONLY the main thread then we can	*/
-/* 			  quickly return some static storage.		*/
-/*									*/
-/*			  If we have multiple threads running, we use	*/
-/*			  Thread-Local Storage to hold the pointer	*/
-/*									*/
-/************************************************************************/
-
-/*
- *	Function pointer for returning errno pointer
- */
-static int *Initialize_Errno(void);
-int *(*__WinCE_Errno_Pointer_Function)(void) = Initialize_Errno;
-
-/*
- *	Static errno storage for the main thread
- */
-static int Errno_Storage = 0;
-
-/*
- *	Thread-Local storage slot for errno
- */
-static int TLS_Errno_Slot = 0xffffffff;
-
-/*
- *	Number of threads we have running and critical section protection
- *	for manipulating it
- */
-static int Number_Of_Threads = 0;
-static CRITICAL_SECTION Number_Of_Threads_Critical_Section;
-
-/*
- *	For the main thread only -- return the errno pointer
- */
-static int *Get_Main_Thread_Errno(void)
-{
-	return &Errno_Storage;
-}
-
-/*
- *	When there is more than one thread -- return the errno pointer
- */
-static int *Get_Thread_Errno(void)
-{
-	return (int *)TlsGetValue(TLS_Errno_Slot);
-}
-
-/*
- *	Initialize a thread's errno
- */
-static void Initialize_Thread_Errno(int *Errno_Pointer)
-{
-	/*
-	 *	Make sure we have a slot
-	 */
-	if (TLS_Errno_Slot == 0xffffffff) {
-		/*
-		 *	No: Get one
-		 */
-		TLS_Errno_Slot = (int)TlsAlloc();
-		if (TLS_Errno_Slot == 0xffffffff) ExitProcess(3);
-	}
-	/*
-	 *	We can safely check for 0 threads, because
-	 *	only the main thread will be initializing
-	 *	at this point.  Make sure the critical
-	 *	section that protects the number of threads
-	 *	is initialized
-	 */
-	if (Number_Of_Threads == 0)
-		InitializeCriticalSection(&Number_Of_Threads_Critical_Section);
-	/*
-	 *	Store the errno pointer
-	 */
-	if (TlsSetValue(TLS_Errno_Slot, (LPVOID)Errno_Pointer) == 0) ExitProcess(3);
-	/*
-	 *	Bump the number of threads
-	 */
-	EnterCriticalSection(&Number_Of_Threads_Critical_Section);
-	Number_Of_Threads++;
-	if (Number_Of_Threads > 1) {
-		/*
-		 *	We have threads other than the main thread:
-		 *	  Use thread-local storage
-		 */
-		__WinCE_Errno_Pointer_Function = Get_Thread_Errno;
-	}
-	LeaveCriticalSection(&Number_Of_Threads_Critical_Section);
-}
-
-/*
- *	Initialize errno emulation on Windows/CE (Main thread)
- */
-static int *Initialize_Errno(void)
-{
-	/*
-	 *	Initialize the main thread's errno in thread-local storage
-	 */
-	Initialize_Thread_Errno(&Errno_Storage);
-	/*
-	 *	Set the errno function to be the one that returns the
-	 *	main thread's errno
-	 */
-	__WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno;
-	/*
-	 *	Return the main thread's errno
-	 */
-	return &Errno_Storage;
-}
-
-/*
- *	Initialize errno emulation on Windows/CE (New thread)
- */
-void __WinCE_Errno_New_Thread(int *Errno_Pointer)
-{
-	Initialize_Thread_Errno(Errno_Pointer);
-}
-
-/*
- *	Note that a thread has exited
- */
-void __WinCE_Errno_Thread_Exit(void)
-{
-	/*
-	 *	Decrease the number of threads
-	 */
-	EnterCriticalSection(&Number_Of_Threads_Critical_Section);
-	Number_Of_Threads--;
-	if (Number_Of_Threads <= 1) {
-		/*
-		 *	We only have the main thread
-		 */
-		__WinCE_Errno_Pointer_Function = Get_Main_Thread_Errno;
-	}
-	LeaveCriticalSection(&Number_Of_Threads_Critical_Section);
-}
-
-
-char *
-strerror(int errnum)
-{
-	return "(strerror not implemented)";
-}
-
-#define FT_EPOCH (116444736000000000i64)
-#define	FT_TICKS (10000000i64)
-
-int
-_wstat(const WCHAR *path, struct _stat *buffer)
-{
-	WIN32_FIND_DATA data;
-	HANDLE handle;
-	WCHAR *p;
-
-	/* Fail if wildcard characters are specified */
-	if (wcscspn(path, L"?*") != wcslen(path))
-		return -1;
-
-	handle = FindFirstFile(path, &data);
-	if (handle == INVALID_HANDLE_VALUE) {
-		errno = GetLastError();
-		return -1;
-	}
-	FindClose(handle);
-
-	/* Found: Convert the file times */
-	buffer->st_mtime = convert_FILETIME_to_time_t(&data.ftLastWriteTime);
-	if (data.ftLastAccessTime.dwLowDateTime || data.ftLastAccessTime.dwHighDateTime)
-		buffer->st_atime = convert_FILETIME_to_time_t(&data.ftLastAccessTime);
-	else
-		buffer->st_atime = buffer->st_mtime;
-	if (data.ftCreationTime.dwLowDateTime || data.ftCreationTime.dwHighDateTime)
-		buffer->st_ctime = convert_FILETIME_to_time_t(&data.ftCreationTime);
-	else
-		buffer->st_ctime = buffer->st_mtime;
-
-	/* Convert the file modes */
-	buffer->st_mode = (unsigned short)((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG);
-	buffer->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE);
-	if((p = wcsrchr(path, L'.')) != NULL) {
-		p++;
-		if (_wcsicmp(p, L".exe") == 0)
-			buffer->st_mode |= S_IEXEC;
-	}
-	buffer->st_mode |= (buffer->st_mode & 0700) >> 3;
-	buffer->st_mode |= (buffer->st_mode & 0700) >> 6;
-	/* Set the other information */
-	buffer->st_nlink = 1;
-	buffer->st_size = (unsigned long int)data.nFileSizeLow;
-	buffer->st_uid = 0;
-	buffer->st_gid = 0;
-	buffer->st_ino = 0 /*data.dwOID ?*/;
-	buffer->st_dev = 0;
-
-	return 0;	/* success */
-}
-
-/*
- *	Helper function for cemodule -- do an fstat() operation on a Win32 File Handle
- */
-int
-_fstat(int handle, struct _stat *st)
-{
-	BY_HANDLE_FILE_INFORMATION Data;
-
-	/*
-	 *	Get the file information
-	 */
-	if (!GetFileInformationByHandle((HANDLE)handle, &Data)) {
-		/*
-		 *	Return error
-		 */
-		errno = GetLastError();
-		return(-1);
-	}
-	/*
-	 *	Found: Convert the file times
-	 */
-	st->st_mtime=(time_t)((*(__int64*)&Data.ftLastWriteTime-FT_EPOCH)/FT_TICKS);
-	if(Data.ftLastAccessTime.dwLowDateTime || Data.ftLastAccessTime.dwHighDateTime)
-		st->st_atime=(time_t)((*(__int64*)&Data.ftLastAccessTime-FT_EPOCH)/FT_TICKS);
-	else
-		st->st_atime=st->st_mtime ;
-	if(Data.ftCreationTime.dwLowDateTime || Data.ftCreationTime.dwHighDateTime )
-		st->st_ctime=(time_t)((*(__int64*)&Data.ftCreationTime-FT_EPOCH)/FT_TICKS);
-	else
-		st->st_ctime=st->st_mtime ;
-	/*
-	 *	Convert the file modes
-	 */
-	st->st_mode = (unsigned short)((Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR | S_IEXEC) : S_IFREG);
-	st->st_mode |= (Data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : (S_IREAD | S_IWRITE);
-	st->st_mode |= (st->st_mode & 0700) >> 3;
-	st->st_mode |= (st->st_mode & 0700) >> 6;
-	/*
-	 *	Set the other information
-	 */
-	st->st_nlink=1;
-	st->st_size=(unsigned long int)Data.nFileSizeLow;
-	st->st_uid=0;
-	st->st_gid=0;
-	st->st_ino=0;
-	st->st_dev=0;
-	/*
-	 *	Return success
-	 */
-	return(0);
-}
-
-int _wopen(const wchar_t *filename, int oflag, ...)
-{
-	DWORD Access, ShareMode, CreationDisposition;
-	HANDLE Handle;
-	static int Modes[4] = {0, (GENERIC_READ | GENERIC_WRITE), GENERIC_READ, GENERIC_WRITE};
-
-	/*
-	 *	Calculate the CreateFile arguments
-	 */
-	Access = Modes[oflag & O_MODE_MASK];
-	ShareMode = (oflag & O_EXCL) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE);
-	if (oflag & O_TRUNC)
-		CreationDisposition = (oflag & O_CREAT) ? CREATE_ALWAYS : TRUNCATE_EXISTING;
-	else
-		CreationDisposition = (oflag & O_CREAT) ? CREATE_NEW : OPEN_EXISTING;
-
-	Handle = CreateFileW(filename, Access, ShareMode, NULL, CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
-
-	/*
-	 *	Deal with errors
-	 */
-	if (Handle == INVALID_HANDLE_VALUE) {
-		errno = GetLastError();
-		if ((errno == ERROR_ALREADY_EXISTS) || (errno == ERROR_FILE_EXISTS))
-			errno = ERROR_ALREADY_EXISTS;
-		return -1;
-	}
-	/*
-	 *	Return the handle
-	 */
-	return (int)Handle;
-}
-
-int
-_close(int handle)
-{
-	if(CloseHandle((HANDLE)handle))
-		return 0;
-	errno = GetLastError();
-	return -1;
-}
-
-int
-_write(int handle, const void *buffer, unsigned int count)
-{
-	DWORD numwritten = 0;
-	if(!WriteFile((HANDLE)handle, buffer, count, &numwritten, NULL)) {
-		errno = GetLastError();
-		return -1;
-	}
-	return numwritten;
-}
-
-int
-_read(int handle, void *buffer, unsigned int count)
-{
-	DWORD numread = 0;
-	if(!ReadFile((HANDLE)handle, buffer, count, &numread, NULL)) {
-		errno = GetLastError();
-		return -1;
-	}
-	return numread;
-}
-
-long
-_lseek(int handle, long offset, int origin)
-{
-	DWORD dwMoveMethod;
-	DWORD result;
-
-	switch(origin) {
-		default:
-			errno = EINVAL;
-			return -1L;
-		case SEEK_SET:
-			dwMoveMethod = FILE_BEGIN;
-			break;
-		case SEEK_CUR:
-			dwMoveMethod = FILE_CURRENT;
-			break;
-		case SEEK_END:
-			dwMoveMethod = FILE_END;
-			break;
-	}
-	result = SetFilePointer((HANDLE)handle, offset, NULL, dwMoveMethod);
-	if(result == 0xFFFFFFFF) {
-		errno = GetLastError();
-		return -1;
-	}
-	return (long)result;
-}
-
-int
-_wmkdir(const wchar_t *dirname)
-{
-	if(!CreateDirectoryW(dirname, NULL)) {
-		errno = GetLastError();
-		return -1;
-	}
-	return 0;
-}
-
-int
-_wremove(const wchar_t *filename)
-{
-	if(!DeleteFileW(filename)) {
-		errno = GetLastError();
-		return -1;
-	}
-	return 0;
-}
-
-int
-_wrename(const wchar_t *oldname, const wchar_t *newname)
-{
-	if(!MoveFileW(oldname, newname)) {
-		errno = GetLastError();
-		return -1;
-	}
-	return 0;
-}
-
-wchar_t *
-_wgetcwd(wchar_t *buffer, int maxlen)
-{
-	wchar_t *result;
-	WCHAR wszPath[MAX_PATH + 1];
-	WCHAR *p;
-
-	if(!GetModuleFileNameW(NULL, wszPath, MAX_PATH + 1)) {
-		errno = GetLastError();
-		return NULL;
-	}
-	/* Remove the filename part of the path to leave the directory */
-	p = wcsrchr(wszPath, L'\\');
-	if(p)
-		*p = L'\0';
-
-	if(buffer == NULL)
-		result = _wcsdup(wszPath);
-	else if(wcslen(wszPath) + 1 > (size_t)maxlen) {
-		result = NULL;
-		errno = ERROR_INSUFFICIENT_BUFFER;
-	} else {
-		wcsncpy(buffer, wszPath, maxlen);
-		buffer[maxlen - 1] = L'\0';
-		result = buffer;
-	}
-	return result;
-}
-
-/*
- *	The missing "C" runtime gmtime() function
- */
-struct tm *gmtime(const time_t *TimeP)
-{
-	FILETIME File_Time;
-
-	/*
-	 *	Deal with null time pointer
-	 */
-	if (!TimeP) return(NULL);
-	/*
-	 *	time_t -> FILETIME -> tm
-	 */
-	Convert_time_t_To_FILETIME(*TimeP, &File_Time);
-	return(Convert_FILETIME_To_tm(&File_Time));
-}
-
-/*
- *	The missing "C" runtime localtime() function
- */
-struct tm *localtime(const time_t *TimeP)
-{
-	FILETIME File_Time, Local_File_Time;
-
-	/*
-	 *	Deal with null time pointer
-	 */
-	if (!TimeP) return(NULL);
-	/*
-	 *	time_t -> FILETIME -> Local FILETIME -> tm
-	 */
-	Convert_time_t_To_FILETIME(*TimeP, &File_Time);
-	FileTimeToLocalFileTime(&File_Time, &Local_File_Time);
-	return(Convert_FILETIME_To_tm(&Local_File_Time));
-}
-
-/*
- *	The missing "C" runtime mktime() function
- */
-time_t mktime(struct tm *tm)
-{
-	FILETIME *Local_File_Time;
-	FILETIME File_Time;
-
-	/*
-	 *	tm -> Local FILETIME -> FILETIME -> time_t
-	 */
-	Local_File_Time = Convert_tm_To_FILETIME(tm);
-	LocalFileTimeToFileTime(Local_File_Time, &File_Time);
-	return(convert_FILETIME_to_time_t(&File_Time));
-}
-
-/*
- *	Missing "C" runtime time() function
- */
-time_t time(time_t *TimeP)
-{
-	SYSTEMTIME System_Time;
-	FILETIME File_Time;
-	time_t Result;
-
-	/*
-	 *	Get the current system time
-	 */
-	GetSystemTime(&System_Time);
-	/*
-	 *	SYSTEMTIME -> FILETIME -> time_t
-	 */
-	SystemTimeToFileTime(&System_Time, &File_Time);
-	Result = convert_FILETIME_to_time_t(&File_Time);
-	/*
-	 *	Return the time_t
-	 */
-	if (TimeP) *TimeP = Result;
-	return(Result);
-}
-
-static char Standard_Name[32] = "GMT";
-static char Daylight_Name[32] = "GMT";
-char *tzname[2] = {Standard_Name, Daylight_Name};
-long timezone = 0;
-int daylight = 0;
-
-void tzset(void)
-{
-	TIME_ZONE_INFORMATION Info;
-	int Result;
-
-	/*
-	 *	Get our current timezone information
-	 */
-	Result = GetTimeZoneInformation(&Info);
-	switch(Result) {
-		/*
-		 *	We are on standard time
-		 */
-		case TIME_ZONE_ID_STANDARD:
-			daylight = 0;
-			break;
-		/*
-		 *	We are on daylight savings time
-		 */
-		case TIME_ZONE_ID_DAYLIGHT:
-			daylight = 1;
-			break;
-		/*
-		 *	We don't know the timezone information (leave it GMT)
-		 */
-		default: return;
-	}
-	/*
-	 *	Extract the timezone information
-	 */
-	timezone = Info.Bias * 60;
-	if (Info.StandardName[0])
-		WideCharToMultiByte(CP_ACP, 0, Info.StandardName, -1, Standard_Name, sizeof(Standard_Name) - 1, NULL, NULL);
-	if (Info.DaylightName[0])
-		WideCharToMultiByte(CP_ACP, 0, Info.DaylightName, -1, Daylight_Name, sizeof(Daylight_Name) - 1, NULL, NULL);
-}
-
-/*** strftime() from newlib libc/time/strftime.c ***/
-
-/*
- * Sane snprintf(). Acts like snprintf(), but never return -1 or the
- * value bigger than supplied buffer.
- */
-static int
-Snprintf(char *buf, size_t buflen, const char *fmt, ...)
-{
-	va_list		ap;
-	int		n;
-
-	if (buflen == 0)
-		return (0);
-
-	va_start(ap, fmt);
-	n = _vsnprintf(buf, buflen, fmt, ap);
-	va_end(ap);
-
-	if (n < 0 || n > (int) buflen - 1) {
-		n = buflen - 1;
-	}
-	buf[n] = '\0';
-
-	return (n);
-}
-
-#define snprintf Snprintf
-
-/* from libc/include/_ansi.h */
-#define _CONST const
-#define	_DEFUN(name, arglist, args)	name(args)
-#define	_AND		,
-/* from libc/time/local.h */
-#define TZ_LOCK
-#define TZ_UNLOCK
-#define _tzname tzname
-#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
-#define YEAR_BASE	1900
-#define SECSPERMIN	60L
-#define MINSPERHOUR	60L
-#define HOURSPERDAY	24L
-#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
-
-/*
- * strftime.c
- * Original Author:	G. Haley
- * Additions from:	Eric Blake
- *
- * Places characters into the array pointed to by s as controlled by the string
- * pointed to by format. If the total number of resulting characters including
- * the terminating null character is not more than maxsize, returns the number
- * of characters placed into the array pointed to by s (not including the
- * terminating null character); otherwise zero is returned and the contents of
- * the array indeterminate.
- */
-
-/*
-FUNCTION
-<<strftime>>---flexible calendar time formatter
-
-INDEX
-	strftime
-
-ANSI_SYNOPSIS
-	#include <time.h>
-	size_t strftime(char *<[s]>, size_t <[maxsize]>,
-			const char *<[format]>, const struct tm *<[timp]>);
-
-TRAD_SYNOPSIS
-	#include <time.h>
-	size_t strftime(<[s]>, <[maxsize]>, <[format]>, <[timp]>)
-	char *<[s]>;
-	size_t <[maxsize]>;
-	char *<[format]>;
-	struct tm *<[timp]>;
-
-DESCRIPTION
-<<strftime>> converts a <<struct tm>> representation of the time (at
-<[timp]>) into a null-terminated string, starting at <[s]> and occupying
-no more than <[maxsize]> characters.
-
-You control the format of the output using the string at <[format]>.
-<<*<[format]>>> can contain two kinds of specifications: text to be
-copied literally into the formatted string, and time conversion
-specifications.  Time conversion specifications are two- and
-three-character sequences beginning with `<<%>>' (use `<<%%>>' to
-include a percent sign in the output).  Each defined conversion
-specification selects only the specified field(s) of calendar time
-data from <<*<[timp]>>>, and converts it to a string in one of the
-following ways:
-
-o+
-o %a
-A three-letter abbreviation for the day of the week. [tm_wday]
-
-o %A
-The full name for the day of the week, one of `<<Sunday>>',
-`<<Monday>>', `<<Tuesday>>', `<<Wednesday>>', `<<Thursday>>',
-`<<Friday>>', or `<<Saturday>>'. [tm_wday]
-
-o %b
-A three-letter abbreviation for the month name. [tm_mon]
-
-o %B
-The full name of the month, one of `<<January>>', `<<February>>',
-`<<March>>', `<<April>>', `<<May>>', `<<June>>', `<<July>>',
-`<<August>>', `<<September>>', `<<October>>', `<<November>>',
-`<<December>>'. [tm_mon]
-
-o %c
-A string representing the complete date and time, in the form
-`<<"%a %b %e %H:%M:%S %Y">>' (example "Mon Apr 01 13:13:13
-1992"). [tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday]
-
-o %C
-The century, that is, the year divided by 100 then truncated.  For
-4-digit years, the result is zero-padded and exactly two characters;
-but for other years, there may a negative sign or more digits.  In
-this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year]
- 
-o %d
-The day of the month, formatted with two digits (from `<<01>>' to
-`<<31>>'). [tm_mday]
-
-o %D
-A string representing the date, in the form `<<"%m/%d/%y">>'.
-[tm_mday, tm_mon, tm_year]
-
-o %e
-The day of the month, formatted with leading space if single digit
-(from `<<1>>' to `<<31>>'). [tm_mday]
-
-o %E<<x>>
-In some locales, the E modifier selects alternative representations of
-certain modifiers <<x>>.  But in the "C" locale supported by newlib,
-it is ignored, and treated as %<<x>>.
-
-o %F
-A string representing the ISO 8601:2000 date format, in the form
-`<<"%Y-%m-%d">>'. [tm_mday, tm_mon, tm_year]
-
-o %g
-The last two digits of the week-based year, see specifier %G (from
-`<<00>>' to `<<99>>'). [tm_year, tm_wday, tm_yday]
-
-o %G
-The week-based year. In the ISO 8601:2000 calendar, week 1 of the year
-includes January 4th, and begin on Mondays. Therefore, if January 1st,
-2nd, or 3rd falls on a Sunday, that day and earlier belong to the last
-week of the previous year; and if December 29th, 30th, or 31st falls
-on Monday, that day and later belong to week 1 of the next year.  For
-consistency with %Y, it always has at least four characters. 
-Example: "%G" for Saturday 2nd January 1999 gives "1998", and for
-Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday]
-
-o %h
-A three-letter abbreviation for the month name (synonym for
-"%b"). [tm_mon]
-
-o %H
-The hour (on a 24-hour clock), formatted with two digits (from
-`<<00>>' to `<<23>>'). [tm_hour]
-
-o %I
-The hour (on a 12-hour clock), formatted with two digits (from
-`<<01>>' to `<<12>>'). [tm_hour]
-
-o %j
-The count of days in the year, formatted with three digits
-(from `<<001>>' to `<<366>>'). [tm_yday]
-
-o %k
-The hour (on a 24-hour clock), formatted with leading space if single
-digit (from `<<0>>' to `<<23>>'). Non-POSIX extension. [tm_hour]
-
-o %l
-The hour (on a 12-hour clock), formatted with leading space if single
-digit (from `<<1>>' to `<<12>>'). Non-POSIX extension. [tm_hour]
-
-o %m
-The month number, formatted with two digits (from `<<01>>' to `<<12>>').
-[tm_mon]
-
-o %M
-The minute, formatted with two digits (from `<<00>>' to `<<59>>'). [tm_min]
-
-o %n
-A newline character (`<<\n>>').
-
-o %O<<x>>
-In some locales, the O modifier selects alternative digit characters
-for certain modifiers <<x>>.  But in the "C" locale supported by newlib, it
-is ignored, and treated as %<<x>>.
-
-o %p
-Either `<<AM>>' or `<<PM>>' as appropriate. [tm_hour]
-
-o %r
-The 12-hour time, to the second.  Equivalent to "%I:%M:%S %p". [tm_sec,
-tm_min, tm_hour]
-
-o %R
-The 24-hour time, to the minute.  Equivalent to "%H:%M". [tm_min, tm_hour]
-
-o %S
-The second, formatted with two digits (from `<<00>>' to `<<60>>').  The
-value 60 accounts for the occasional leap second. [tm_sec]
-
-o %t
-A tab character (`<<\t>>').
-
-o %T
-The 24-hour time, to the second.  Equivalent to "%H:%M:%S". [tm_sec,
-tm_min, tm_hour]
-
-o %u
-The weekday as a number, 1-based from Monday (from `<<1>>' to
-`<<7>>'). [tm_wday]
-
-o %U
-The week number, where weeks start on Sunday, week 1 contains the first
-Sunday in a year, and earlier days are in week 0.  Formatted with two
-digits (from `<<00>>' to `<<53>>').  See also <<%W>>. [tm_wday, tm_yday]
-
-o %V
-The week number, where weeks start on Monday, week 1 contains January 4th,
-and earlier days are in the previous year.  Formatted with two digits
-(from `<<01>>' to `<<53>>').  See also <<%G>>. [tm_year, tm_wday, tm_yday]
-
-o %w
-The weekday as a number, 0-based from Sunday (from `<<0>>' to `<<6>>').
-[tm_wday]
-
-o %W
-The week number, where weeks start on Monday, week 1 contains the first
-Monday in a year, and earlier days are in week 0.  Formatted with two
-digits (from `<<00>>' to `<<53>>'). [tm_wday, tm_yday]
-
-o %x
-A string representing the complete date, equivalent to "%m/%d/%y".
-[tm_mon, tm_mday, tm_year]
-
-o %X
-A string representing the full time of day (hours, minutes, and
-seconds), equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour]
-
-o %y
-The last two digits of the year (from `<<00>>' to `<<99>>'). [tm_year]
-
-o %Y
-The full year, equivalent to <<%C%y>>.  It will always have at least four
-characters, but may have more.  The year is accurate even when tm_year
-added to the offset of 1900 overflows an int. [tm_year]
-
-o %z
-The offset from UTC.  The format consists of a sign (negative is west of
-Greewich), two characters for hour, then two characters for minutes
-(-hhmm or +hhmm).  If tm_isdst is negative, the offset is unknown and no
-output is generated; if it is zero, the offset is the standard offset for
-the current time zone; and if it is positive, the offset is the daylight
-savings offset for the current timezone. The offset is determined from
-the TZ environment variable, as if by calling tzset(). [tm_isdst]
-
-o %Z
-The time zone name.  If tm_isdst is negative, no output is generated.
-Otherwise, the time zone name is based on the TZ environment variable,
-as if by calling tzset(). [tm_isdst]
-
-o %%
-A single character, `<<%>>'.
-o-
-
-RETURNS
-When the formatted time takes up no more than <[maxsize]> characters,
-the result is the length of the formatted string.  Otherwise, if the
-formatting operation was abandoned due to lack of room, the result is
-<<0>>, and the string starting at <[s]> corresponds to just those
-parts of <<*<[format]>>> that could be completely filled in within the
-<[maxsize]> limit.
-
-PORTABILITY
-ANSI C requires <<strftime>>, but does not specify the contents of
-<<*<[s]>>> when the formatted string would require more than
-<[maxsize]> characters.  Unrecognized specifiers and fields of
-<<timp>> that are out of range cause undefined results.  Since some
-formats expand to 0 bytes, it is wise to set <<*<[s]>>> to a nonzero
-value beforehand to distinguish between failure and an empty string.
-This implementation does not support <<s>> being NULL, nor overlapping
-<<s>> and <<format>>.
-
-<<strftime>> requires no supporting OS subroutines.
-*/
-
-static _CONST int dname_len[7] =
-{6, 6, 7, 9, 8, 6, 8};
-
-static _CONST char *_CONST dname[7] =
-{"Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"};
-
-static _CONST int mname_len[12] =
-{7, 8, 5, 5, 3, 4, 4, 6, 9, 7, 8, 8};
-
-static _CONST char *_CONST mname[12] =
-{"January", "February", "March", "April",
- "May", "June", "July", "August", "September", "October", "November",
- "December"};
-
-/* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return
-   -1, 0, or 1 as the adjustment to add to the year for the ISO week
-   numbering used in "%g%G%V", avoiding overflow.  */
-static int
-_DEFUN (iso_year_adjust, (tim_p),
-	_CONST struct tm *tim_p)
-{
-  /* Account for fact that tm_year==0 is year 1900.  */
-  int leap = isleap (tim_p->tm_year + (YEAR_BASE
-				       - (tim_p->tm_year < 0 ? 0 : 2000)));
-
-  /* Pack the yday, wday, and leap year into a single int since there are so
-     many disparate cases.  */
-#define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp))
-  switch (PACK (tim_p->tm_yday, tim_p->tm_wday, leap))
-    {
-    case PACK (0, 5, 0): /* Jan 1 is Fri, not leap.  */
-    case PACK (0, 6, 0): /* Jan 1 is Sat, not leap.  */
-    case PACK (0, 0, 0): /* Jan 1 is Sun, not leap.  */
-    case PACK (0, 5, 1): /* Jan 1 is Fri, leap year.  */
-    case PACK (0, 6, 1): /* Jan 1 is Sat, leap year.  */
-    case PACK (0, 0, 1): /* Jan 1 is Sun, leap year.  */
-    case PACK (1, 6, 0): /* Jan 2 is Sat, not leap.  */
-    case PACK (1, 0, 0): /* Jan 2 is Sun, not leap.  */
-    case PACK (1, 6, 1): /* Jan 2 is Sat, leap year.  */
-    case PACK (1, 0, 1): /* Jan 2 is Sun, leap year.  */
-    case PACK (2, 0, 0): /* Jan 3 is Sun, not leap.  */
-    case PACK (2, 0, 1): /* Jan 3 is Sun, leap year.  */
-      return -1; /* Belongs to last week of previous year.  */
-    case PACK (362, 1, 0): /* Dec 29 is Mon, not leap.  */
-    case PACK (363, 1, 1): /* Dec 29 is Mon, leap year.  */
-    case PACK (363, 1, 0): /* Dec 30 is Mon, not leap.  */
-    case PACK (363, 2, 0): /* Dec 30 is Tue, not leap.  */
-    case PACK (364, 1, 1): /* Dec 30 is Mon, leap year.  */
-    case PACK (364, 2, 1): /* Dec 30 is Tue, leap year.  */
-    case PACK (364, 1, 0): /* Dec 31 is Mon, not leap.  */
-    case PACK (364, 2, 0): /* Dec 31 is Tue, not leap.  */
-    case PACK (364, 3, 0): /* Dec 31 is Wed, not leap.  */
-    case PACK (365, 1, 1): /* Dec 31 is Mon, leap year.  */
-    case PACK (365, 2, 1): /* Dec 31 is Tue, leap year.  */
-    case PACK (365, 3, 1): /* Dec 31 is Wed, leap year.  */
-      return 1; /* Belongs to first week of next year.  */
-    }
-  return 0; /* Belongs to specified year.  */
-#undef PACK
-}
-
-size_t
-_DEFUN (strftime, (s, maxsize, format, tim_p),
-	char *s _AND
-	size_t maxsize _AND
-	_CONST char *format _AND
-	_CONST struct tm *tim_p)
-{
-  size_t count = 0;
-  int i;
-
-  for (;;)
-    {
-      while (*format && *format != '%')
-	{
-	  if (count < maxsize - 1)
-	    s[count++] = *format++;
-	  else
-	    return 0;
-	}
-
-      if (*format == '\0')
-	break;
-
-      format++;
-      if (*format == 'E' || *format == 'O')
-	format++;
-
-      switch (*format)
-	{
-	case 'a':
-	  for (i = 0; i < 3; i++)
-	    {
-	      if (count < maxsize - 1)
-		s[count++] =
-		  dname[tim_p->tm_wday][i];
-	      else
-		return 0;
-	    }
-	  break;
-	case 'A':
-	  for (i = 0; i < dname_len[tim_p->tm_wday]; i++)
-	    {
-	      if (count < maxsize - 1)
-		s[count++] =
-		  dname[tim_p->tm_wday][i];
-	      else
-		return 0;
-	    }
-	  break;
-	case 'b':
-	case 'h':
-	  for (i = 0; i < 3; i++)
-	    {
-	      if (count < maxsize - 1)
-		s[count++] =
-		  mname[tim_p->tm_mon][i];
-	      else
-		return 0;
-	    }
-	  break;
-	case 'B':
-	  for (i = 0; i < mname_len[tim_p->tm_mon]; i++)
-	    {
-	      if (count < maxsize - 1)
-		s[count++] =
-		  mname[tim_p->tm_mon][i];
-	      else
-		return 0;
-	    }
-	  break;
-	case 'c':
-	  {
-	    /* Length is not known because of %C%y, so recurse. */
-	    size_t adjust = strftime (&s[count], maxsize - count,
-				      "%a %b %e %H:%M:%S %C%y", tim_p);
-	    if (adjust > 0)
-	      count += adjust;
-	    else
-	      return 0;
-	  }
-	  break;
-	case 'C':
-	  {
-	    /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y
-	       with 32-bit int.
-	       %Y		%C		%y
-	       2147485547	21474855	47
-	       10000		100		00
-	       9999		99		99
-	       0999		09		99
-	       0099		00		99
-	       0001		00		01
-	       0000		00		00
-	       -001		-0		01
-	       -099		-0		99
-	       -999		-9		99
-	       -1000		-10		00
-	       -10000		-100		00
-	       -2147481748	-21474817	48
-
-	       Be careful of both overflow and sign adjustment due to the
-	       asymmetric range of years.
-	    */
-	    int neg = tim_p->tm_year < -YEAR_BASE;
-	    int century = tim_p->tm_year >= 0
-	      ? tim_p->tm_year / 100 + YEAR_BASE / 100
-	      : abs (tim_p->tm_year + YEAR_BASE) / 100;
-            count += snprintf (&s[count], maxsize - count, "%s%.*d",
-                               neg ? "-" : "", 2 - neg, century);
-            if (count >= maxsize)
-              return 0;
-	  }
-	  break;
-	case 'd':
-	case 'e':
-	  if (count < maxsize - 2)
-	    {
-	      sprintf (&s[count], *format == 'd' ? "%.2d" : "%2d",
-		       tim_p->tm_mday);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'D':
-	case 'x':
-	  /* %m/%d/%y */
-	  if (count < maxsize - 8)
-	    {
-	      sprintf (&s[count], "%.2d/%.2d/%.2d",
-		       tim_p->tm_mon + 1, tim_p->tm_mday,
-		       tim_p->tm_year >= 0 ? tim_p->tm_year % 100
-		       : abs (tim_p->tm_year + YEAR_BASE) % 100);
-	      count += 8;
-	    }
-	  else
-	    return 0;
-	  break;
-        case 'F':
-	  {
-	    /* Length is not known because of %C%y, so recurse. */
-	    size_t adjust = strftime (&s[count], maxsize - count,
-				      "%C%y-%m-%d", tim_p);
-	    if (adjust > 0)
-	      count += adjust;
-	    else
-	      return 0;
-	  }
-          break;
-        case 'g':
-	  if (count < maxsize - 2)
-	    {
-	      /* Be careful of both overflow and negative years, thanks to
-		 the asymmetric range of years.  */
-	      int adjust = iso_year_adjust (tim_p);
-	      int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
-		: abs (tim_p->tm_year + YEAR_BASE) % 100;
-	      if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
-		adjust = 1;
-	      else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
-		adjust = -1;
-	      sprintf (&s[count], "%.2d",
-		       ((year + adjust) % 100 + 100) % 100);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-          break;
-        case 'G':
-	  {
-	    /* See the comments for 'C' and 'Y'; this is a variable length
-	       field.  Although there is no requirement for a minimum number
-	       of digits, we use 4 for consistency with 'Y'.  */
-	    int neg = tim_p->tm_year < -YEAR_BASE;
-	    int adjust = iso_year_adjust (tim_p);
-	    int century = tim_p->tm_year >= 0
-	      ? tim_p->tm_year / 100 + YEAR_BASE / 100
-	      : abs (tim_p->tm_year + YEAR_BASE) / 100;
-	    int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
-	      : abs (tim_p->tm_year + YEAR_BASE) % 100;
-	    if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
-	      neg = adjust = 1;
-	    else if (adjust > 0 && neg)
-	      adjust = -1;
-	    year += adjust;
-	    if (year == -1)
-	      {
-		year = 99;
-		--century;
-	      }
-	    else if (year == 100)
-	      {
-		year = 0;
-		++century;
-	      }
-            count += snprintf (&s[count], maxsize - count, "%s%.*d%.2d",
-                               neg ? "-" : "", 2 - neg, century, year);
-            if (count >= maxsize)
-              return 0;
-	  }
-          break;
-	case 'H':
-	case 'k':
-	  if (count < maxsize - 2)
-	    {
-	      sprintf (&s[count], *format == 'k' ? "%2d" : "%.2d",
-		       tim_p->tm_hour);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'I':
-	case 'l':
-	  if (count < maxsize - 2)
-	    {
-	      if (tim_p->tm_hour == 0 ||
-		  tim_p->tm_hour == 12)
-		{
-		  s[count++] = '1';
-		  s[count++] = '2';
-		}
-	      else
-		{
-		  sprintf (&s[count], *format == 'I' ? "%.2d" : "%2d",
-			   tim_p->tm_hour % 12);
-		  count += 2;
-		}
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'j':
-	  if (count < maxsize - 3)
-	    {
-	      sprintf (&s[count], "%.3d",
-		       tim_p->tm_yday + 1);
-	      count += 3;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'm':
-	  if (count < maxsize - 2)
-	    {
-	      sprintf (&s[count], "%.2d",
-		       tim_p->tm_mon + 1);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'M':
-	  if (count < maxsize - 2)
-	    {
-	      sprintf (&s[count], "%.2d",
-		       tim_p->tm_min);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'n':
-	  if (count < maxsize - 1)
-	    s[count++] = '\n';
-	  else
-	    return 0;
-	  break;
-	case 'p':
-	  if (count < maxsize - 2)
-	    {
-	      if (tim_p->tm_hour < 12)
-		s[count++] = 'A';
-	      else
-		s[count++] = 'P';
-
-	      s[count++] = 'M';
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'r':
-	  if (count < maxsize - 11)
-	    {
-	      if (tim_p->tm_hour == 0 ||
-		  tim_p->tm_hour == 12)
-		{
-		  s[count++] = '1';
-		  s[count++] = '2';
-		}
-	      else
-		{
-		  sprintf (&s[count], "%.2d", tim_p->tm_hour % 12);
-		  count += 2;
-		}
-	      s[count++] = ':';
-	      sprintf (&s[count], "%.2d",
-		       tim_p->tm_min);
-	      count += 2;
-	      s[count++] = ':';
-	      sprintf (&s[count], "%.2d",
-		       tim_p->tm_sec);
-	      count += 2;
-	      s[count++] = ' ';
-	      if (tim_p->tm_hour < 12)
-		s[count++] = 'A';
-	      else
-		s[count++] = 'P';
-
-	      s[count++] = 'M';
-	    }
-	  else
-	    return 0;
-	  break;
-        case 'R':
-          if (count < maxsize - 5)
-            {
-              sprintf (&s[count], "%.2d:%.2d", tim_p->tm_hour, tim_p->tm_min);
-              count += 5;
-            }
-          else
-            return 0;
-          break;
-	case 'S':
-	  if (count < maxsize - 2)
-	    {
-	      sprintf (&s[count], "%.2d",
-		       tim_p->tm_sec);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 't':
-	  if (count < maxsize - 1)
-	    s[count++] = '\t';
-	  else
-	    return 0;
-	  break;
-        case 'T':
-        case 'X':
-          if (count < maxsize - 8)
-            {
-              sprintf (&s[count], "%.2d:%.2d:%.2d", tim_p->tm_hour,
-                       tim_p->tm_min, tim_p->tm_sec);
-              count += 8;
-            }
-          else
-            return 0;
-          break;
-        case 'u':
-          if (count < maxsize - 1)
-            {
-              if (tim_p->tm_wday == 0)
-                s[count++] = '7';
-              else
-                s[count++] = '0' + tim_p->tm_wday;
-            }
-          else
-            return 0;
-          break;
-	case 'U':
-	  if (count < maxsize - 2)
-	    {
-	      sprintf (&s[count], "%.2d",
-		       (tim_p->tm_yday + 7 -
-			tim_p->tm_wday) / 7);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-        case 'V':
-	  if (count < maxsize - 2)
-	    {
-	      int adjust = iso_year_adjust (tim_p);
-	      int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
-	      int week = (tim_p->tm_yday + 10 - wday) / 7;
-	      if (adjust > 0)
-		week = 1;
-	      else if (adjust < 0)
-		/* Previous year has 53 weeks if current year starts on
-		   Fri, and also if current year starts on Sat and
-		   previous year was leap year.  */
-		week = 52 + (4 >= (wday - tim_p->tm_yday
-				   - isleap (tim_p->tm_year
-					     + (YEAR_BASE - 1
-						- (tim_p->tm_year < 0
-						   ? 0 : 2000)))));
-	      sprintf (&s[count], "%.2d", week);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-          break;
-	case 'w':
-	  if (count < maxsize - 1)
-            s[count++] = '0' + tim_p->tm_wday;
-	  else
-	    return 0;
-	  break;
-	case 'W':
-	  if (count < maxsize - 2)
-	    {
-	      int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
-	      sprintf (&s[count], "%.2d",
-		       (tim_p->tm_yday + 7 - wday) / 7);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'y':
-	  if (count < maxsize - 2)
-	    {
-	      /* Be careful of both overflow and negative years, thanks to
-		 the asymmetric range of years.  */
-	      int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
-		: abs (tim_p->tm_year + YEAR_BASE) % 100;
-	      sprintf (&s[count], "%.2d", year);
-	      count += 2;
-	    }
-	  else
-	    return 0;
-	  break;
-	case 'Y':
-	  {
-	    /* Length is not known because of %C%y, so recurse. */
-	    size_t adjust = strftime (&s[count], maxsize - count,
-				      "%C%y", tim_p);
-	    if (adjust > 0)
-	      count += adjust;
-	    else
-	      return 0;
-	  }
-	  break;
-        case 'z':
-#ifndef _WIN32_WCE
-          if (tim_p->tm_isdst >= 0)
-            {
-	      if (count < maxsize - 5)
-		{
-		  long offset;
-		  __tzinfo_type *tz = __gettzinfo ();
-		  TZ_LOCK;
-		  /* The sign of this is exactly opposite the envvar TZ.  We
-		     could directly use the global _timezone for tm_isdst==0,
-		     but have to use __tzrule for daylight savings.  */
-		  offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
-		  TZ_UNLOCK;
-		  sprintf (&s[count], "%+03ld%.2ld", offset / SECSPERHOUR,
-			   labs (offset / SECSPERMIN) % 60L);
-		  count += 5;
-		}
-	      else
-		return 0;
-            }
-          break;
-#endif
-	case 'Z':
-	  if (tim_p->tm_isdst >= 0)
-	    {
-	      int size;
-	      TZ_LOCK;
-	      size = strlen(_tzname[tim_p->tm_isdst > 0]);
-	      for (i = 0; i < size; i++)
-		{
-		  if (count < maxsize - 1)
-		    s[count++] = _tzname[tim_p->tm_isdst > 0][i];
-		  else
-		    {
-		      TZ_UNLOCK;
-		      return 0;
-		    }
-		}
-	      TZ_UNLOCK;
-	    }
-	  break;
-	case '%':
-	  if (count < maxsize - 1)
-	    s[count++] = '%';
-	  else
-	    return 0;
-	  break;
-	}
-      if (*format)
-	format++;
-      else
-	break;
-    }
-  if (maxsize)
-    s[count] = '\0';
-
-  return count;
-}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Deleted: dss/trunk/external/shttpd/shttpd.c
===================================================================
--- dss/trunk/external/shttpd/shttpd.c	2009-09-03 09:34:09 UTC (rev 8728)
+++ dss/trunk/external/shttpd/shttpd.c	2009-09-03 09:48:59 UTC (rev 8729)
@@ -1,1226 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka <valenok at gmail.com>
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file.  As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-/*
- * Small and portable HTTP server, http://shttpd.sourceforge.net
- * $Id$
- */
-
-#include "defs.h"
-
-time_t		current_time;	/* Current UTC time		*/
-int		tz_offset;	/* Time zone offset from UTC	*/
-
-const struct vec known_http_methods[] = {
-	{"GET",		3},
-	{"POST",	4},
-	{"PUT",		3},
-	{"DELETE",	6},
-	{"HEAD",	4},
-	{NULL,		0}
-};
-
-struct listener {
-	struct llhead	link;
-	struct shttpd_ctx *ctx;		/* Context that socket belongs	*/
-	int		sock;		/* Listening socket		*/
-	int		is_ssl;		/* Should be SSL-ed		*/
-};
-
-/*
- * This structure tells how HTTP headers must be parsed.
- * Used by parse_headers() function.
- */
-#define	OFFSET(x)	offsetof(struct headers, x)
-static const struct http_header http_headers[] = {
-	{16, HDR_INT,	 OFFSET(cl),		"Content-Length: "	},
-	{14, HDR_STRING, OFFSET(ct),		"Content-Type: "	},
-	{12, HDR_STRING, OFFSET(useragent),	"User-Agent: "		},
-	{19, HDR_DATE,	 OFFSET(ims),		"If-Modified-Since: "	},
-	{15, HDR_STRING, OFFSET(auth),		"Authorization: "	},
-	{9,  HDR_STRING, OFFSET(referer),	"Referer: "		},
-	{8,  HDR_STRING, OFFSET(cookie),	"Cookie: "		},
-	{10, HDR_STRING, OFFSET(location),	"Location: "		},
-	{8,  HDR_INT,	 OFFSET(status),	"Status: "		},
-	{7,  HDR_STRING, OFFSET(range),		"Range: "		},
-	{12, HDR_STRING, OFFSET(connection),	"Connection: "		},
-	{19, HDR_STRING, OFFSET(transenc),	"Transfer-Encoding: "	},
-	{0,  HDR_INT,	 0,			NULL			}
-};
-
-struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
-static void process_connection(struct conn *, int, int);
-
-int
-url_decode(const char *src, int src_len, char *dst, int dst_len)
-{
-	int	i, j, a, b;
-#define	HEXTOI(x)  (isdigit(x) ? x - '0' : x - 'W')
-
-	for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
-		switch (src[i]) {
-		case '%':
-			if (isxdigit(((unsigned char *) src)[i + 1]) &&
-			    isxdigit(((unsigned char *) src)[i + 2])) {
-				a = tolower(((unsigned char *)src)[i + 1]);
-				b = tolower(((unsigned char *)src)[i + 2]);
-				dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
-				i += 2;
-			} else {
-				dst[j] = '%';
-			}
-			break;
-		default:
-			dst[j] = src[i];
-			break;
-		}
-
-	dst[j] = '\0';	/* Null-terminate the destination */
-
-	return (j);
-}
-
-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->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 || 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;
-			a_path->len = (s + len) - p;
-			return (s);
-		}
-	}
-
-	return (NULL);
-}
-
-void
-stop_stream(struct stream *stream)
-{
-	if (stream->io_class != NULL && stream->io_class->close != NULL)
-		stream->io_class->close(stream);
-
-	stream->io_class= NULL;
-	stream->flags |= FLAG_CLOSED;
-	stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
-
-	DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
-	    stream->conn->rem.chan.sock,
-	    stream->io_class ? stream->io_class->name : "(null)",
-	    (unsigned long) stream->io.total, io_data_len(&stream->io)));
-}
-
-/*
- * Setup listening socket on given port, return socket
- */
-static int
-open_listening_port(int port)
-{
-	int		sock, on = 1;
-	struct usa	sa;
-
-#ifdef _WIN32
-	{WSADATA data;	WSAStartup(MAKEWORD(2,2), &data);}
-#endif /* _WIN32 */
-
-	sa.len				= sizeof(sa.u.sin);
-	sa.u.sin.sin_family		= AF_INET;
-	sa.u.sin.sin_port		= htons((uint16_t) port);
-	sa.u.sin.sin_addr.s_addr	= htonl(INADDR_ANY);
-
-	if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
-		goto fail;
-	if (set_non_blocking_mode(sock) != 0)
-		goto fail;
-	if (setsockopt(sock, SOL_SOCKET,
-	    SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
-		goto fail;
-	if (bind(sock, &sa.u.sa, sa.len) < 0)
-		goto fail;
-	if (listen(sock, 128) != 0)
-		goto fail;
-
-#ifndef _WIN32
-	(void) fcntl(sock, F_SETFD, FD_CLOEXEC);
-#endif /* !_WIN32 */
-
-	return (sock);
-fail:
-	if (sock != -1)
-		(void) closesocket(sock);
-	elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
-	return (-1);
-}
-
-/*
- * Check whether full request is buffered Return headers length, or 0
- */
-int
-get_headers_len(const char *buf, size_t buflen)
-{
-	const char	*s, *e;
-	int		len = 0;
-
-	for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
-		/* Control characters are not allowed but >=128 is. */
-		if (!isprint(* (unsigned char *) s) && *s != '\r' &&
-		    *s != '\n' && * (unsigned char *) s < 128)
-			len = -1;
-		else if (s[0] == '\n' && s[1] == '\n')
-			len = s - buf + 2;
-		else if (s[0] == '\n' && &s[1] < e &&
-		    s[1] == '\r' && s[2] == '\n')
-			len = s - buf + 3;
-
-	return (len);
-}
-
-/*
- * Send error message back to a client.
- */
-void
-send_server_error(struct conn *c, int status, const char *reason)
-{
-	struct llhead		*lp;
-	struct error_handler	*e;
-
-	LL_FOREACH(&c->ctx->error_handlers, lp) {
-		e = LL_ENTRY(lp, struct error_handler, link);
-
-		if (e->code == status) {
-			if (c->loc.io_class != NULL &&
-			    c->loc.io_class->close != NULL)
-				c->loc.io_class->close(&c->loc);
-			io_clear(&c->loc.io);
-			setup_embedded_stream(c, e->callback, e->callback_data);
-			return;
-		}
-	}
-
-	io_clear(&c->loc.io);
-	c->loc.io.head = my_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);
-}
-
-/*
- * Convert month to the month number. Return -1 on error, or month number
- */
-static int
-montoi(const char *s)
-{
-	static const char *ar[] = {
-		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-	};
-	size_t	i;
-
-	for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
-		if (!strcmp(s, ar[i]))
-			return (i);
-
-	return (-1);
-}
-
-/*
- * Parse date-time string, and return the corresponding time_t value
- */
-static time_t
-date_to_epoch(const char *s)
-{
-	struct tm	tm, *tmp;
-	char		mon[32];
-	int		sec, min, hour, mday, month, year;
-
-	(void) memset(&tm, 0, sizeof(tm));
-	sec = min = hour = mday = month = year = 0;
-
-	if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
-	    &mday, mon, &year, &hour, &min, &sec) == 6) ||
-	    (sscanf(s, "%d %3s %d %d:%d:%d",
-	    &mday, mon, &year, &hour, &min, &sec) == 6) ||
-	    (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
-	    &mday, mon, &year, &hour, &min, &sec) == 6) ||
-	    (sscanf(s, "%d-%3s-%d %d:%d:%d",
-	    &mday, mon, &year, &hour, &min, &sec) == 6)) &&
-	    (month = montoi(mon)) != -1) {
-		tm.tm_mday	= mday;
-		tm.tm_mon	= month;
-		tm.tm_year	= year;
-		tm.tm_hour	= hour;
-		tm.tm_min	= min;
-		tm.tm_sec	= sec;
-	}
-
-	if (tm.tm_year > 1900)
-		tm.tm_year -= 1900;
-	else if (tm.tm_year < 70)
-		tm.tm_year += 100;
-
-	/* Set Daylight Saving Time field */
-	tmp = localtime(&current_time);
-	tm.tm_isdst = tmp->tm_isdst;
-
-	return (mktime(&tm));
-}
-
-static void
-remove_double_dots(char *s)
-{
-	char	*p = s;
-
-	while (*s != '\0') {
-		*p++ = *s++;
-		if (s[-1] == '/' || s[-1] == '\\')
-			while (*s == '.' || *s == '/' || *s == '\\')
-				s++;
-	}
-	*p = '\0';
-}
-
-void
-parse_headers(const char *s, int len, struct headers *parsed)
-{
-	const struct http_header	*h;
-	union variant			*v;
-	const char			*p, *e = s + len;
-
-	DBG(("parsing headers (len %d): [%.*s]", len, len, s));
-
-	/* Loop through all headers in the request */
-	while (s < e) {
-
-		/* Find where this header ends */
-		for (p = s; p < e && *p != '\n'; ) p++;
-
-		/* Is this header known to us ? */
-		for (h = http_headers; h->len != 0; h++)
-			if (e - s > h->len &&
-			    !my_strncasecmp(s, h->name, h->len))
-				break;
-
-		/* If the header is known to us, store its value */
-		if (h->len != 0) {
-
-			/* Shift to where value starts */
-			s += h->len;
-
-			/* Find place to store the value */
-			v = (union variant *) ((char *) parsed + h->offset);
-
-			/* Fetch header value into the connection structure */
-			if (h->type == HDR_STRING) {
-				v->v_vec.ptr = s;
-				v->v_vec.len = p - s;
-				if (p[-1] == '\r' && v->v_vec.len > 0)
-					v->v_vec.len--;
-			} else if (h->type == HDR_INT) {
-				v->v_big_int = strtoul(s, NULL, 10);
-			} else if (h->type == HDR_DATE) {
-				v->v_time = date_to_epoch(s);
-			}
-		}
-
-		s = p + 1;	/* Shift to the next header */
-	}
-}
-
-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"			},
-	{"xml",   3,  "text/xml"      },
-  {"xslt",  4,  "application/xml"  },
-	{"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
-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] == '.' &&
-		    !my_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] == '.' &&
-		    !my_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
- */
-static int
-find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
-{
-	char		buf[FILENAME_MAX];
-	const char	*s = c->ctx->options[OPT_INDEX_FILES];
-	size_t		len;
-
-	FOR_EACH_WORD_IN_LIST(s, len) {
-		my_snprintf(buf, sizeof(buf), "%s%c%.*s",path, DIRSEP, len, s);
-		if (my_stat(buf, stp) == 0) {
-			my_strlcpy(path, buf, maxpath);
-			get_mime_type(c->ctx, s, len, &c->mime_type);
-			return (0);
-		}
-	}
-
-	return (-1);
-}
-
-/*
- * Try to open requested file, return 0 if OK, -1 if error.
- * If the file is given arguments using PATH_INFO mechanism,
- * initialize pathinfo pointer.
- */
-static int
-get_path_info(struct conn *c, char *path, struct stat *stp)
-{
-	char	*p, *e;
-
-	if (my_stat(path, stp) == 0)
-		return (0);
-
-	p = path + strlen(path);
-	e = path + strlen(c->ctx->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)) {
-				c->path_info = p + 1;
-				return (0);
-			} else {
-				*p = '/';
-			}
-		}
-
-	return (-1);
-}
-
-
-static void
-decide_what_to_do(struct conn *c)
-{
-	char		path[URI_MAX], buf[1024], *root;
-	struct vec	alias_uri, alias_path;
-	struct stat	st;
-	int		rc;
-	struct registered_uri	*ruri;
-
-	DBG(("decide_what_to_do: [%s]", c->uri));
-
-	if ((c->query = strchr(c->uri, '?')) != NULL)
-		*c->query++ = '\0';
-
-	url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
-	remove_double_dots(c->uri);
-
-	root = c->ctx->options[OPT_ROOT];
-	if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
-		send_server_error(c, 400, "URI is too long");
-		return;
-	}
-
-	(void) my_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) my_snprintf(path, sizeof(path), "%.*s%s",
-		    alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
-		DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
-		    alias_path.len, alias_path.ptr));
-	}
-
-#if !defined(NO_AUTH)
-	if (check_authorization(c, path) != 1) {
-		send_authorization_request(c);
-	} else
-#endif /* NO_AUTH */
-	if ((ruri = is_registered_uri(c->ctx, c->uri)) != NULL) {
-		setup_embedded_stream(c, ruri->callback, ruri->callback_data);
-	} else
-	if (strstr(path, HTPASSWD)) {
-		/* Do not allow to view passwords files */
-		send_server_error(c, 403, "Forbidden");
-	} else
-#if !defined(NO_AUTH)
-	if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
-	    (c->ctx->options[OPT_AUTH_PUT] == NULL ||
-	     !is_authorized_for_put(c))) {
-		send_authorization_request(c);
-	} else
-#endif /* NO_AUTH */
-	if (c->method == METHOD_PUT) {
-		c->status = my_stat(path, &st) == 0 ? 200 : 201;
-
-		if (c->ch.range.v_vec.len > 0) {
-			send_server_error(c, 501, "PUT Range Not Implemented");
-		} else if ((rc = put_dir(path)) == 0) {
-			send_server_error(c, 200, "OK");
-		} else if (rc == -1) {
-			send_server_error(c, 500, "PUT Directory Error");
-		} else if (c->rem.content_len == 0) {
-			send_server_error(c, 411, "Length Required");
-		} else if ((c->loc.chan.fd = my_open(path, O_WRONLY | O_BINARY |
-		    O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
-			send_server_error(c, 500, "PUT Error");
-		} else {
-			DBG(("PUT file [%s]", c->uri));
-			c->loc.io_class = &io_file;
-			c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
-		}
-	} else if (c->method == METHOD_DELETE) {
-		DBG(("DELETE [%s]", c->uri));
-		if (my_remove(path) == 0)
-			send_server_error(c, 200, "OK");
-		else
-			send_server_error(c, 500, "DELETE Error");
-	} else if (get_path_info(c, path, &st) != 0) {
-		send_server_error(c, 404, "Not Found");
-	} else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
-		(void) my_snprintf(buf, sizeof(buf),
-			"Moved Permanently\r\nLocation: %s/", c->uri);
-		send_server_error(c, 301, buf);
-	} else if (S_ISDIR(st.st_mode) &&
-	    find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
-	    !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
-		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 = my_strdup(path)) != NULL)
-			get_dir(c);
-		else
-			send_server_error(c, 500, "GET Directory Error");
-	} else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
-		send_server_error(c, 403, "Directory listing denied");
-#if !defined(NO_CGI)
-	} else if (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");
-		} else {
-			do_cgi(c);
-		}
-#endif /* NO_CGI */
-#if !defined(NO_SSI)
-	} else if (match_extension(path, c->ctx->options[OPT_SSI_EXTENSIONS])) {
-		if ((c->loc.chan.fd = my_open(path,
-		    O_RDONLY | O_BINARY, 0644)) == -1) {
-			send_server_error(c, 500, "SSI open error");
-		} else {
-			do_ssi(c);
-		}
-#endif /* NO_CGI */
-	} else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
-		send_server_error(c, 304, "Not Modified");
-	} else if ((c->loc.chan.fd = my_open(path,
-	    O_RDONLY | O_BINARY, 0644)) != -1) {
-		get_file(c, &st);
-	} else {
-		send_server_error(c, 500, "Internal Error");
-	}
-}
-
-static int
-set_request_method(struct conn *c)
-{
-	const struct vec	*v;
-
-	/* Set the request method */
-	for (v = known_http_methods; v->ptr != NULL; v++)
-		if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
-			c->method = v - known_http_methods;
-			break;
-		}
-
-	return (v->ptr == NULL);
-}
-
-static void
-parse_http_request(struct conn *c)
-{
-	char	*s, *e, *p, *start;
-	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));
-
-	if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
-		io_clear(&c->rem.io);
-		send_server_error(c, 400, "Request is too big");
-	}
-
-	io_inc_tail(&c->rem.io, req_len);
-
-	if (req_len == 0) {
-		return;
-	} else if (req_len < 16) {	/* Minimal: "GET / HTTP/1.0\n\n" */
-		send_server_error(c, 400, "Bad request");
-	} else if (set_request_method(c)) {
-		send_server_error(c, 501, "Method Not Implemented");
-	} else if ((c->request = my_strndup(s, req_len)) == NULL) {
-		send_server_error(c, 500, "Cannot allocate request");
-	}
-
-	if (c->loc.flags & FLAG_CLOSED)
-		return;
-
-	DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
-	c->rem.flags |= FLAG_HEADERS_PARSED;
-
-	/* Set headers pointer. Headers follow the request line */
-	c->headers = memchr(c->request, '\n', req_len);
-	assert(c->headers != NULL);
-	assert(c->headers < c->request + req_len);
-	if (c->headers > c->request && c->headers[-1] == '\r')
-		c->headers[-1] = '\0';
-	*c->headers++ = '\0';
-
-	/*
-	 * Now make a copy of the URI, because it will be URL-decoded,
-	 * and we need a copy of unmodified URI for the access log.
-	 * First, we skip the REQUEST_METHOD and shift to the URI.
-	 */
-	for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
-	while (p < e && *p == ' ')
-		p++;
-
-	/* Now remember where URI starts, and shift to the end of URI */
-	for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
-	uri_len = p - start;
-
-	/* Skip space following the URI */
-	while (p < e && *p == ' ')
-		p++;
-
-	/* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
-	if (sscanf(p, "HTTP/%lu.%lu%n",
-	    &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
-		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");
-	} else if (uri_len <= 0) {
-		send_server_error(c, 400, "Bad URI");
-	} else if ((c->uri = malloc(uri_len + 1)) == NULL) {
-		send_server_error(c, 500, "Cannot allocate URI");
-	} else {
-		my_strlcpy(c->uri, (char *) start, uri_len + 1);
-		parse_headers(c->headers,
-		    (c->request + req_len) - c->headers, &c->ch);
-
-		/* Remove the length of request from total, count only data */
-		assert(c->rem.io.total >= (big_int_t) req_len);
-		c->rem.io.total -= req_len;
-		c->rem.content_len = c->ch.cl.v_big_int;
-		decide_what_to_do(c);
-	}
-}
-
-void
-shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
-{
-	struct conn	*c;
-	struct usa	sa;
-	int		l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
-#if !defined(NO_SSL)
-	SSL		*ssl = NULL;
-#endif /* NO_SSL */
-
-	sa.len = sizeof(sa.u.sin);
-	(void) set_non_blocking_mode(sock);
-
-	if (getpeername(sock, &sa.u.sa, &sa.len)) {
-		elog(l, NULL, "add_socket: %s", strerror(errno));
-#if !defined(NO_SSL)
-	} else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
-		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));
-		(void) closesocket(sock);
-		SSL_free(ssl);
-#endif /* NO_SSL */
-	} 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));
-	} else {
-		ctx->nrequests++;
-		c->rem.conn = c->loc.conn = c;
-		c->ctx		= ctx;
-		c->sa		= sa;
-		c->birth_time	= current_time;
-		c->expire_time	= current_time + EXPIRE_TIME;
-
-		(void) getsockname(sock, &sa.u.sa, &sa.len);
-		c->loc_port = sa.u.sin.sin_port;
-
-		set_close_on_exec(sock);
-
-		c->loc.io_class	= NULL;
-
-		c->rem.io_class	= &io_socket;
-		c->rem.chan.sock = sock;
-
-		/* Set IO buffers */
-		c->loc.io.buf	= (char *) (c + 1);
-		c->rem.io.buf	= c->loc.io.buf + 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.chan.ssl.sock = sock;
-			c->rem.chan.ssl.ssl = ssl;
-			ssl_handshake(&c->rem);
-		}
-#endif /* NO_SSL */
-
-		EnterCriticalSection(&ctx->mutex);
-		LL_TAIL(&ctx->connections, &c->link);
-		ctx->nactive++;
-		LeaveCriticalSection(&ctx->mutex);
-
-		DBG(("%s:%hu connected (socket %d)",
-		    inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
-		    ntohs(sa.u.sin.sin_port), sock));
-	}
-}
-
-int
-shttpd_active(struct shttpd_ctx *ctx)
-{
-	return (ctx->nactive);
-}
-
-/*
- * Setup a listening socket on given port. Return opened socket or -1
- */
-int
-shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl)
-{
-	struct listener	*l;
-	int		sock;
-
-	if ((sock = open_listening_port(port)) == -1) {
-		elog(E_FATAL, NULL, "cannot open port %d", port);
-	} else if ((l = calloc(1, sizeof(*l))) == NULL) {
-		(void) closesocket(sock);
-		elog(E_FATAL, NULL, "cannot allocate listener");
-	} else if (is_ssl && ctx->ssl_ctx == NULL) {
-		(void) closesocket(sock);
-		elog(E_FATAL, NULL, "cannot add SSL socket, "
-		    "please specify certificate file");
-	} else {
-		l->is_ssl = is_ssl;
-		l->sock	= sock;
-		l->ctx	= ctx;
-		LL_TAIL(&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);
-}
-
-static void
-read_stream(struct stream *stream)
-{
-	int	n, len;
-
-	len = io_space_len(&stream->io);
-	assert(len > 0);
-
-	/* Do not read more that needed */
-	if (stream->content_len > 0 &&
-	    stream->io.total + len > stream->content_len)
-		len = stream->content_len - stream->io.total;
-
-	if(!stream->io_class) {
-	  assert(0);
-	}
-
-	/* Read from underlying channel */
-	n = stream->nread_last = stream->io_class->read(stream,
-	    io_space(&stream->io), len);
-
-	if (n > 0)
-		io_inc_head(&stream->io, n);
-	else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
-		n = n;	/* Ignore EINTR and EAGAIN */
-	else if (!(stream->flags & FLAG_DONT_CLOSE))
-		stop_stream(stream);
-
-	DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
-	    stream->conn->rem.chan.sock,
-	    stream->io_class ? stream->io_class->name : "(null)",
-	    n, len, (unsigned long) stream->io.total, ERRNO));
-
-	/*
-	 * Close the local stream if everything was read
-	 * XXX We do not close the remote stream though! It may be
-	 * a POST data completed transfer, we do not want the socket
-	 * to be closed.
-	 */
-	if (stream->content_len > 0 && stream == &stream->conn->loc) {
-		assert(stream->io.total <= stream->content_len);
-		if (stream->io.total == stream->content_len)
-			stop_stream(stream);
-	}
-
-	stream->conn->expire_time = current_time + EXPIRE_TIME;
-}
-
-static void
-write_stream(struct stream *from, struct stream *to)
-{
-	int	n, len;
-
-	len = io_data_len(&from->io);
-	assert(len > 0);
-
-	/* TODO: should be assert on CAN_WRITE flag */
-	n = to->io_class->write(to, io_data(&from->io), len);
-	to->conn->expire_time = current_time + EXPIRE_TIME;
-	DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
-	    to->conn->rem.chan.sock,
-	    to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
-
-	if (n > 0)
-		io_inc_tail(&from->io, n);
-	else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
-		n = n;	/* Ignore EINTR and EAGAIN */
-	else if (!(to->flags & FLAG_DONT_CLOSE))
-		stop_stream(to);
-}
-
-
-static void
-disconnect(struct llhead *lp)
-{
-	struct conn		*c = LL_ENTRY(lp, struct conn, link);
-	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 (c->request != NULL && c->ctx->access_log != NULL)
-		log_access(c->ctx->access_log, c);
-
-	/* In inetd mode, exit if request is finished. */
-	if (IS_TRUE(c->ctx, OPT_INETD))
-		exit(0);
-
-	if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
-		c->loc.io_class->close(&c->loc);
-
-	/*
-	 * Check the "Connection: " header before we free c->request
-	 * If it its 'keep-alive', then do not close the connection
-	 */
-	do_close = (c->ch.connection.v_vec.len >= vec.len &&
-	    !my_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);
-
-	/* 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->loc.content_len = 0;
-		c->rem.flags = FLAG_W | FLAG_R;
-		c->query = c->request = c->uri = c->path_info = NULL;
-		c->mime_type.len = 0;
-		(void) memset(&c->ch, 0, sizeof(c->ch));
-		io_clear(&c->loc.io);
-		c->birth_time = 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);
-
-		free(c);
-	}
-}
-
-static int
-is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
-{
-	const struct acl	*acl;
-	const struct llhead	*lp;
-	int			allowed = '+';
-	uint32_t		ip;
-
-	LL_FOREACH(&ctx->acl, lp) {
-		acl = LL_ENTRY(lp, struct acl, link);
-		(void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
-		if (acl->ip == (ntohl(ip) & acl->mask))
-			allowed = acl->flag;
-	}
-
-	return (allowed == '+');
-}
-
-static void
-add_to_set(int fd, fd_set *set, int *max_fd)
-{
-	FD_SET(fd, set);
-	if (fd > *max_fd)
-		*max_fd = fd;
-}
-
-static void
-process_connection(struct conn *c, int remote_ready, int local_ready)
-{
-	/* Read from remote end if it is ready */
-	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: %u [%.*s]", io_data_len(&c->loc.io),
-	    io_data_len(&c->loc.io), io_data(&c->loc.io)));
-	DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
-	    io_data_len(&c->rem.io), io_data(&c->rem.io)));
-
-	/* Read from the local end if it is ready */
-	if (local_ready && io_space_len(&c->loc.io))
-		read_stream(&c->loc);
-
-	if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
-	    c->loc.io_class != NULL && c->loc.io_class->write != NULL)
-		write_stream(&c->rem, &c->loc);
-
-	if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
-		write_stream(&c->loc, &c->rem);
-
-	if (c->rem.nread_last > 0)
-		c->ctx->in += c->rem.nread_last;
-	if (c->loc.nread_last > 0)
-		c->ctx->out += c->loc.nread_last;
-
-	/* Check whether we should close this connection */
-	if ((current_time > c->expire_time) ||
-	    (c->rem.flags & FLAG_CLOSED) ||
-	    ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
-		disconnect(&c->link);
-}
-
-/*
- * One iteration of server loop. This is the core of the data exchange.
- */
-void
-shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
-{
-	struct llhead	*lp, *tmp;
-	struct listener	*l;
-	struct conn	*c;
-	struct timeval	tv;			/* Timeout for select() */
-	fd_set		read_set, write_set;
-	int		sock, max_fd = -1, msec = milliseconds;
-	struct usa	sa;
-
-	current_time = time(0);
-	FD_ZERO(&read_set);
-	FD_ZERO(&write_set);
-
-	/* Add listening sockets to the read set */
-	LL_FOREACH(&ctx->listeners, lp) {
-		l = LL_ENTRY(lp, struct listener, link);
-		FD_SET(l->sock, &read_set);
-		if (l->sock > max_fd)
-			max_fd = l->sock;
-		DBG(("FD_SET(%d) (listening)", l->sock));
-	}
-
-	/* Multiplex streams */
-	LL_FOREACH(&ctx->connections, lp) {
-		c = LL_ENTRY(lp, struct conn, link);
-
-		/* If there is a space in remote IO, check remote socket */
-		if (io_space_len(&c->rem.io))
-			add_to_set(c->rem.chan.fd, &read_set, &max_fd);
-
-#if !defined(NO_CGI)
-		/*
-		 * If there is a space in local IO, and local endpoint is
-		 * CGI, check local socket for read availability
-		 */
-		if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
-		    c->loc.io_class == &io_cgi)
-			add_to_set(c->loc.chan.fd, &read_set, &max_fd);
-
-		/*
-		 * If there is some data read from remote socket, and
-		 * local endpoint is CGI, check local for write availability
-		 */
-		if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
-		    c->loc.io_class == &io_cgi)
-			add_to_set(c->loc.chan.fd, &write_set, &max_fd);
-#endif /* NO_CGI */
-
-		/*
-		 * If there is some data read from local endpoint, check the
-		 * remote socket for write availability
-		 */
-		if (io_data_len(&c->loc.io))
-			add_to_set(c->rem.chan.fd, &write_set, &max_fd);
-
-		if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
-		    (c->loc.flags & FLAG_ALWAYS_READY))
-			msec = 0;
-
-		if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
-		    (c->loc.flags & FLAG_ALWAYS_READY))
-			msec = 0;
-	}
-
-	tv.tv_sec = msec / 1000;
-	tv.tv_usec = (msec % 1000) * 1000;
-
-	/* Check IO readiness */
-	if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
-#ifdef _WIN32
-		/*
-		 * On windows, if read_set and write_set are empty,
-		 * select() returns "Invalid parameter" error
-		 * (at least on my Windows XP Pro). So in this case,
-		 * we sleep here.
-		 */
-		Sleep(milliseconds);
-#endif /* _WIN32 */
-		DBG(("select: %d", ERRNO));
-		return;
-	}
-
-	/* Check for incoming connections on listener sockets */
-	LL_FOREACH(&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 */
-			}
-		} while (sock != -1);
-	}
-
-	/* Process all connections */
-	LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
-		c = LL_ENTRY(lp, struct conn, link);
-		process_connection(c, FD_ISSET(c->rem.chan.fd, &read_set),
-		    ((c->loc.flags & FLAG_ALWAYS_READY)
-#if !defined(NO_CGI)
-		    || (c->loc.io_class == &io_cgi &&
-		     FD_ISSET(c->loc.chan.fd, &read_set))
-#endif /* NO_CGI */
-		    ));
-	}
-}
-
-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);
-	}
-}
-
-void
-listener_destructor(struct llhead *lp)
-{
-	struct listener	*listener = LL_ENTRY(lp, struct listener, link);
-
-	(void) closesocket(listener->sock);
-	free(listener);
-}
-
-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);
-}
-
-/*
- * Deallocate shttpd object, free up the resources
- */
-void
-shttpd_fini(struct shttpd_ctx *ctx)
-{
-	size_t	i;
-
-	free_list(&ctx->connections, disconnect);
-	free_list(&ctx->registered_uris, registered_uri_destructor);
-	free_list(&ctx->acl, acl_destructor);
-	free_list(&ctx->listeners, listener_destructor);
-	free_list(&ctx->ssi_funcs, ssi_func_destructor);
-
-	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);
-
-	/* TODO: free SSL context */
-
-	free(ctx);
-}

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

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

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

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

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



More information about the dss-commits mailing list