[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(¤t_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> %s</td><td> %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> %s</td><td> %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, ®istered_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(¤t_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","<DIR>");
- } 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> %s</td><td> %s</td></tr>\n",
- c->uri, slash, dp->d_name, dp->d_name,
- S_ISDIR(st.st_mode) ? "/" : "", mod, size);
- (void) memcpy(buf, line, n);
- buf = (char *) buf + n;
- nwritten += n;
- len -= n;
- } while (dp != NULL);
-
- return (nwritten);
-}
-
-static void
-close_dir(struct stream *stream)
-{
- assert(stream->chan.dir.dirp != NULL);
- assert(stream->chan.dir.path != NULL);
- (void) closedir(stream->chan.dir.dirp);
- free(stream->chan.dir.path);
-}
-
-void
-get_dir(struct conn *c)
-{
- if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) {
- (void) free(c->loc.chan.dir.path);
- send_server_error(c, 500, "Cannot open directory");
- } else {
- c->loc.io.head = my_snprintf(c->loc.io.buf, c->loc.io.size,
- "HTTP/1.1 200 OK\r\n"
- "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(¤t_time));
- (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
- (void) my_snprintf(etag, sizeof(etag), "%lx.%lx",
- (unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
-
- /*
- * We do not do io_inc_head here, because it will increase 'total'
- * member in io. We want 'total' to be equal to the content size,
- * and exclude the headers length from it.
- */
- c->loc.io.head = c->loc.headers_len = my_snprintf(c->loc.io.buf,
- c->loc.io.size,
- "HTTP/1.1 %d %s\r\n"
- "Date: %s\r\n"
- "Last-Modified: %s\r\n"
- "Etag: \"%s\"\r\n"
- "Content-Type: %.*s\r\n"
- "Content-Length: %lu\r\n"
- "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(¤t_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(¤t_time));
-
- len = my_snprintf(buf, sizeof(buf),
- "[%s] [error] [client %s] \"%s\" ",
- date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
- c && c->request ? c->request : "-");
-
- va_start(ap, fmt);
- (void) vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
- va_end(ap);
-
- buf[sizeof(buf) - 1] = '\0';
-
- if (fp != NULL && (flags & (E_FATAL | E_LOG))) {
- (void) fprintf(fp, "%s\n", buf);
- (void) fflush(fp);
- }
-
- if (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 = ‐
-
- if (referer->len == 0) {
- referer = ‐
- q1 = "";
- }
-
- if (user_agent->len == 0) {
- user_agent = ‐
- q2 = "";
- }
-
- (void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
- localtime(&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(¤t_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