Browse Source

修复时间错误的问题

vothin 2 years ago
parent
commit
84efbe12e2
72 changed files with 7220 additions and 14 deletions
  1. 2 0
      i18n/zh_CN.tr
  2. 17 0
      jni/activity/DeviceUpdateActivity.cpp
  3. 5 0
      jni/activity/DeviceUpdateActivity.h
  4. 30 0
      jni/include/utils/TimeHelper.h
  5. 23 2
      jni/logic/DeviceUpdateLogic.cc
  6. 2 10
      jni/logic/mainLogic.cc
  7. 9 0
      jni/logic/startLogic.cc
  8. 617 0
      jni/server/http_server.cpp
  9. 66 0
      jni/server/http_server.h
  10. 2 2
      jni/service/BusinessConfig.h
  11. 419 0
      jni/shttpd/auth.c
  12. 229 0
      jni/shttpd/cgi.c
  13. 128 0
      jni/shttpd/compat_unix.c
  14. 36 0
      jni/shttpd/compat_unix.h
  15. 29 0
      jni/shttpd/config.h
  16. 395 0
      jni/shttpd/defs.h
  17. 97 0
      jni/shttpd/io.h
  18. 127 0
      jni/shttpd/io_cgi.c
  19. 153 0
      jni/shttpd/io_dir.c
  20. 313 0
      jni/shttpd/io_emb.c
  21. 157 0
      jni/shttpd/io_file.c
  22. 39 0
      jni/shttpd/io_socket.c
  23. 488 0
      jni/shttpd/io_ssi.c
  24. 85 0
      jni/shttpd/io_ssl.c
  25. 59 0
      jni/shttpd/llist.h
  26. 93 0
      jni/shttpd/log.c
  27. 249 0
      jni/shttpd/md5.c
  28. 24 0
      jni/shttpd/md5.h
  29. 641 0
      jni/shttpd/multipart_parser.c
  30. 120 0
      jni/shttpd/multipart_parser.h
  31. 94 0
      jni/shttpd/multipart_reader.c
  32. 32 0
      jni/shttpd/multipart_reader.h
  33. 1903 0
      jni/shttpd/shttpd.c
  34. 112 0
      jni/shttpd/shttpd.h
  35. 52 0
      jni/shttpd/ssl.h
  36. 72 0
      jni/shttpd/standalone.c
  37. 40 0
      jni/shttpd/std_includes.h
  38. 95 0
      jni/shttpd/string.c
  39. BIN
      libs/armeabi/libzkgui.so
  40. BIN
      obj/activity/DeviceUpdateActivity.o
  41. BIN
      obj/activity/callActivity.o
  42. BIN
      obj/activity/mainActivity.o
  43. BIN
      obj/activity/sipTestActivity.o
  44. BIN
      obj/activity/startActivity.o
  45. BIN
      obj/activity/statusbar.o
  46. BIN
      obj/activity/ui3Activity.o
  47. BIN
      obj/activity/warnActivity.o
  48. BIN
      obj/core/update_assistant.o
  49. BIN
      obj/net/tcp_client.o
  50. BIN
      obj/server/http_server.o
  51. BIN
      obj/service/BusinessConfig.o
  52. BIN
      obj/service/time.o
  53. BIN
      obj/shttpd/auth.o
  54. BIN
      obj/shttpd/cgi.o
  55. BIN
      obj/shttpd/compat_unix.o
  56. BIN
      obj/shttpd/io_cgi.o
  57. BIN
      obj/shttpd/io_dir.o
  58. BIN
      obj/shttpd/io_emb.o
  59. BIN
      obj/shttpd/io_file.o
  60. BIN
      obj/shttpd/io_socket.o
  61. BIN
      obj/shttpd/io_ssi.o
  62. BIN
      obj/shttpd/io_ssl.o
  63. BIN
      obj/shttpd/log.o
  64. BIN
      obj/shttpd/md5.o
  65. BIN
      obj/shttpd/multipart_parser.o
  66. BIN
      obj/shttpd/multipart_reader.o
  67. BIN
      obj/shttpd/shttpd.o
  68. BIN
      obj/shttpd/standalone.o
  69. BIN
      obj/shttpd/string.o
  70. 155 0
      resources/html/index.html
  71. 11 0
      resources/html/setting_result.html
  72. BIN
      ui/DeviceUpdate.ftu

+ 2 - 0
i18n/zh_CN.tr

@@ -108,6 +108,8 @@
     <string name="Downloading">下载中</string>
     <string name="EthernetDisconnect">未连接网络</string>
     <string name="DownloadFailed">下载错误,错误码</string>
+    <string name="PasswordText">3位密码</string>
+    <string name="PasswordInput">输入密码</string>
     <!-- 通话界面 -->
     <string name="RemoteRefuse">对方拒绝</string>
     <string name="RemoteBusy">对方忙线</string>

+ 17 - 0
jni/activity/DeviceUpdateActivity.cpp

@@ -4,6 +4,11 @@
 #include "DeviceUpdateActivity.h"
 
 /*TAG:GlobalVariable全局变量*/
+static ZKTextView* mTextPwdInfoPtr;
+static ZKButton* mBtnPwdConfirmPtr;
+static ZKTextView* mPasswordTextViewPtr;
+static ZKEditText* mEditTextPwdPtr;
+static ZKWindow* mWindowPwdPtr;
 static ZKButton* mFindBackButtonPtr;
 static ZKButton* mBackButtonPtr;
 static ZKListView* mDeviceTypeListViewPtr;
@@ -70,6 +75,7 @@ typedef struct {
 
 /*TAG:ButtonCallbackTab按键映射表*/
 static S_ButtonCallback sButtonCallbackTab[] = {
+    ID_DEVICEUPDATE_BtnPwdConfirm, onButtonClick_BtnPwdConfirm,
     ID_DEVICEUPDATE_FindBackButton, onButtonClick_FindBackButton,
     ID_DEVICEUPDATE_BackButton, onButtonClick_BackButton,
     ID_DEVICEUPDATE_ButtonInit, onButtonClick_ButtonInit,
@@ -123,6 +129,7 @@ typedef struct {
 }S_EditTextInputCallback;
 /*TAG:EditTextInputCallback*/
 static S_EditTextInputCallback SEditTextInputCallbackTab[] = {
+    ID_DEVICEUPDATE_EditTextPwd, onEditTextChanged_EditTextPwd,
 };
 
 typedef void (*VideoViewCallback)(ZKVideoView *pVideoView, int msg);
@@ -149,6 +156,11 @@ DeviceUpdateActivity::~DeviceUpdateActivity() {
     EASYUICONTEXT->unregisterGlobalTouchListener(this);
     onUI_quit();
     unregisterProtocolDataUpdateListener(onProtocolDataUpdate);
+    mTextPwdInfoPtr = NULL;
+    mBtnPwdConfirmPtr = NULL;
+    mPasswordTextViewPtr = NULL;
+    mEditTextPwdPtr = NULL;
+    mWindowPwdPtr = NULL;
     mFindBackButtonPtr = NULL;
     mBackButtonPtr = NULL;
     mDeviceTypeListViewPtr = NULL;
@@ -188,6 +200,11 @@ const char* DeviceUpdateActivity::getAppName() const{
 //TAG:onCreate
 void DeviceUpdateActivity::onCreate() {
 	Activity::onCreate();
+    mTextPwdInfoPtr = (ZKTextView*)findControlByID(ID_DEVICEUPDATE_TextPwdInfo);
+    mBtnPwdConfirmPtr = (ZKButton*)findControlByID(ID_DEVICEUPDATE_BtnPwdConfirm);
+    mPasswordTextViewPtr = (ZKTextView*)findControlByID(ID_DEVICEUPDATE_PasswordTextView);
+    mEditTextPwdPtr = (ZKEditText*)findControlByID(ID_DEVICEUPDATE_EditTextPwd);if(mEditTextPwdPtr!= NULL){mEditTextPwdPtr->setTextChangeListener(this);}
+    mWindowPwdPtr = (ZKWindow*)findControlByID(ID_DEVICEUPDATE_WindowPwd);
     mFindBackButtonPtr = (ZKButton*)findControlByID(ID_DEVICEUPDATE_FindBackButton);
     mBackButtonPtr = (ZKButton*)findControlByID(ID_DEVICEUPDATE_BackButton);
     mDeviceTypeListViewPtr = (ZKListView*)findControlByID(ID_DEVICEUPDATE_DeviceTypeListView);if(mDeviceTypeListViewPtr!= NULL){mDeviceTypeListViewPtr->setListAdapter(this);mDeviceTypeListViewPtr->setItemClickListener(this);}

+ 5 - 0
jni/activity/DeviceUpdateActivity.h

@@ -26,6 +26,11 @@
 #include "window/ZKSlideWindow.h"
 
 /*TAG:Macro宏ID*/
+#define ID_DEVICEUPDATE_TextPwdInfo    50022
+#define ID_DEVICEUPDATE_BtnPwdConfirm    20007
+#define ID_DEVICEUPDATE_PasswordTextView    50017
+#define ID_DEVICEUPDATE_EditTextPwd    51001
+#define ID_DEVICEUPDATE_WindowPwd    110006
 #define ID_DEVICEUPDATE_FindBackButton    20005
 #define ID_DEVICEUPDATE_BackButton    20004
 #define ID_DEVICEUPDATE_IntervalSubItem    24002

+ 30 - 0
jni/include/utils/TimeHelper.h

@@ -70,6 +70,36 @@ public:
 		std::string result = timeStr;
 		return result;
 	}
+
+	static std::string getTimeStrOnTimeDifference(std::string str, int timeDifference) {
+		// 先把时间戳调整到对应时差
+		time_t timet = stoi(str);
+		struct tm *t = gmtime(&timet);
+		char pDate[25];
+		sprintf(pDate,"%d-%02d-%02d %02d:%02d:%02d",
+				1900 + t->tm_year, 1+ t->tm_mon, t->tm_mday,
+				t->tm_hour + timeDifference,t->tm_min,t->tm_sec);
+
+		tm tm_;                                    // 定义tm结构体。
+		int year, month, day, hour, minute, second;// 定义时间的各个int临时变量。
+		sscanf(pDate, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);// 将string存储的日期时间,转换为int临时变量。
+		tm_.tm_year = year - 1900;                 // 年,由于tm结构体存储的是从1900年开始的时间,所以tm_year为int临时变量减去1900。
+		tm_.tm_mon = month - 1;                    // 月,由于tm结构体的月份存储范围为0-11,所以tm_mon为int临时变量减去1。
+		tm_.tm_mday = day;                         // 日。
+		tm_.tm_hour = hour;                        // 时。
+		tm_.tm_min = minute;                       // 分。
+		tm_.tm_sec = second;                       // 秒。
+		tm_.tm_isdst = 0;                          // 非夏令时。
+		time_t _timet = mktime(&tm_);                  // 将tm结构体转换成time_t格式。
+
+		// 再转换成字符串
+		struct tm *_t = gmtime(&_timet);
+		char _pDate[25];
+		sprintf(_pDate,"%d-%02d-%02d",
+					1900 + _t->tm_year, 1+ _t->tm_mon, _t->tm_mday);
+		std::string result = _pDate;
+		return result;
+	}
 };
 
 #endif /* _UTILS_TIME_HELPER_H_ */

+ 23 - 2
jni/logic/DeviceUpdateLogic.cc

@@ -303,8 +303,9 @@ static bool onButtonClick_ButtonDialog1(ZKButton *pButton) {
 }
 static bool onButtonClick_ButtonInit(ZKButton *pButton) {
     LOGD(" ButtonClick ButtonInit !!!\n");
-    mWindowInitPtr->showWnd();
-	getLinuxVersionList();
+//	getLinuxVersionList();
+//  mWindowInitPtr->showWnd();
+	mWindowPwdPtr->showWnd();
     return false;
 }
 static int getListItemCount_DeviceTypeListView(const ZKListView *pListView) {
@@ -390,3 +391,23 @@ static bool onButtonClick_FindBackButton(ZKButton *pButton) {
     EASYUICONTEXT->goBack();
     return false;
 }
+static void onEditTextChanged_EditTextPwd(const std::string &text) {
+    //LOGD(" onEditTextChanged_ EditTextPwd %s !!!\n", text.c_str());
+}
+
+static bool onButtonClick_BtnPwdConfirm(ZKButton *pButton) {
+    LOGD(" ButtonClick BtnPwdConfirm !!!\n");
+
+    string pwd = mEditTextPwdPtr->getText();
+	// 密码为666
+	string cpwd = "666";
+    if (pwd == cpwd){
+    	getLinuxVersionList();
+    	mWindowPwdPtr->hideWnd();
+        mWindowInitPtr->showWnd();
+		return false;
+    } else {
+    	mTextPwdInfoPtr->setTextTr("PasswordWrong");
+    }
+    return false;
+}

+ 2 - 10
jni/logic/mainLogic.cc

@@ -265,11 +265,7 @@ public:
 		string inDateStr = LANGUAGEMANAGER->getValue("InTime");
 		string inDate = frameBed["customer_in_date"].asString();
 		if (inDate != "") {
-			time_t timet = stoi(inDate);
-			struct tm *t = gmtime(&timet);
-			char pDate[25];
-			sprintf(pDate,"%d-%02d-%02d",
-					1900 + t->tm_year, 1+ t->tm_mon, t->tm_mday);
+			std::string pDate = TimeHelper::getTimeStrOnTimeDifference(inDate, 8);
 			inDate = pDate;
 		}
 		mInDateTextViewPtr->setText(inDateStr + inDate);
@@ -277,11 +273,7 @@ public:
 		string outDateStr = LANGUAGEMANAGER->getValue("OutTime");
 		string outDate = frameBed["customer_out_date"].asString();
 		if (outDate != "") {
-			time_t timet = stoi(outDate);
-			struct tm *t = gmtime(&timet);
-			char pDate[25];
-			sprintf(pDate,"%d-%02d-%02d",
-					1900 + t->tm_year, 1+ t->tm_mon, t->tm_mday);
+			std::string pDate = TimeHelper::getTimeStrOnTimeDifference(outDate, 8);
 			outDate = pDate;
 		}
 		if (outDate == "") {

+ 9 - 0
jni/logic/startLogic.cc

@@ -5,6 +5,7 @@
 #include "edge/call_log.h"
 #include "net/tcp_client.h"
 #include "net/tcp_model.h"
+#include "server/http_server.h"
 
 #include "utils/GpioHelper.h"
 #include "utils/TimeHelper.h"
@@ -27,6 +28,11 @@ static bool isSleep = false;	// 是否在息屏
 #define SLEEP_STRAT_TIME_HANDLE 9	// 息屏
 #define SYS_RESTART 10	// 重启
 
+
+#define HTTP_SERVER_PORT  80
+
+srv::HttpServer httpServer;
+
 namespace {
 	std::string uilogic[] = {
 			"testActivity",
@@ -600,6 +606,9 @@ static void onUI_init(){
 	//监听SIP信令
 	GetTelephone()->AddCallStateListener(OnCallStateChanged);
 
+	//开启网页
+	httpServer.RunAsync(HTTP_SERVER_PORT);
+
 	//请求版本号
 //	if(UartContext::Uart3IsOpen()) {
 //		string heartStr = "ASK,VNF-0";

+ 617 - 0
jni/server/http_server.cpp

@@ -0,0 +1,617 @@
+#include "http_server.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/reboot.h>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string.h>
+
+#include "shttpd/shttpd.h"
+#include "storage/StoragePreferences.h"
+#include "service/BusinessConfig.h"
+#include "manager/ConfigManager.h"
+#include "utils/Log.h"
+#include "shttpd/multipart_reader.h"
+#include "os/MountMonitor.h"
+#include "net/NetManager.h"
+
+#define ETHERNETMANAGER		NETMANAGER->getEthernetManager()
+
+namespace srv {
+
+class MyThread: public Thread {
+public:
+	virtual bool readyToRun() {
+		return true;
+	}
+	virtual bool threadLoop() {
+		//检查是否有退出线程的请求,如果有,则返回false,立即退出线程
+		if (exitPending()) {
+			return false;
+		}
+		//休眠5s
+		usleep(5000 * 1000);
+		//重启
+		sync();
+		reboot(RB_AUTOBOOT);
+		//返回真,继续下次线程循环
+		return false;
+	}
+};
+
+static MyThread my_thread;
+
+//地址总长
+#define ADDRLEN 15
+
+//定义字符串数组保存IP地址、掩码地址、网关地址、首选DNS服务器地址以及备选DNS服务器地址
+static std::string addr[5];
+/**
+ * 字符长度是否正确
+ * */
+const bool isLengthCorrect(const std::string addr)
+{
+	return addr.size() <= ADDRLEN;
+}
+
+/**
+ * 检查是不是最多只有3个.字符
+ * */
+const bool noMoreThan3charactor(const std::string addr)
+{
+	//.字符个数
+	int count = 0;
+	for(unsigned int level1 = 0; level1 < addr.size(); ++level1)
+	{
+		if('.' == addr[level1])
+		{
+			++count;
+		}
+	}
+	return 3 >= count;
+}
+
+/*
+ * 判断所有字符是不是由0~9和.组成
+ * */
+const bool allCharacterIsLegal(const std::string addr)
+{
+	for(unsigned int level1 = 0; level1 < addr.size(); ++level1)
+	{
+		if(false == (('0' <= addr[level1] && addr[level1] <= '9') || '.' == addr[level1]))
+		{
+			return false;
+		}
+	}
+	return true;
+}
+
+/**
+ * 判断开头是否是1`9
+ * */
+const bool headIsCorrect(std::string addr, unsigned int pos = 0)
+{
+	//查找到达末尾,直接返回true
+	if(addr.size() == pos)
+	{
+		return true;
+	}
+	//查找.字符
+	if(std::string::npos == addr.find('.', pos))
+	{
+		if('0' < addr[pos] && addr[pos] <= '9')
+		{
+			return true;
+		}
+		//如果开头为字符'0'且其后没有字符,那么返回true
+		else if(addr.size()-1 == pos)
+		{
+			return true;
+		}
+		return false;
+	}
+	else
+	{
+		//开头为0~9
+		if('0' > addr[pos] || addr[pos] > '9')
+		{
+			return false;
+		}
+		//开头为0,后面没有字符返回false
+		if('0' == addr[pos] && addr.size()-1 <= pos)
+		{
+			return false;
+		}
+		//跳过.字符继续查找
+		return headIsCorrect(addr, addr.find('.', pos)+1);
+	}
+	return true; //默认返回true
+}
+
+/**
+ * 检查地址每一小段的长度,最长为3个字符
+ * */
+const bool partCheractorCorrect(const std::string addr, unsigned int pos = 0)
+{
+	//查找到达末尾,直接返回true
+	if(addr.size() == pos)
+	{
+		return true;
+	}
+	//查找.字符
+	if(std::string::npos == addr.find('.', pos))
+	{
+		if(3 >= addr.size()-pos)
+		{
+			return true;
+		}
+		return false;
+	}
+	else
+	{
+		//由于跳过了.字符进行查找,因此判断条件应该是>3
+		if(3 < addr.find('.', pos) - pos)
+		{
+			return false;
+		}
+		//跳过.字符继续查找
+		return partCheractorCorrect(addr, addr.find('.', pos)+1);
+	}
+	return true; //默认返回true
+}
+
+/**
+ * 检测地址是否有误
+ * */
+const bool checkAddr(std::string addr)
+{
+	return  (true == headIsCorrect(addr))?			//地址每小段开头是否为0~9
+			(true == partCheractorCorrect(addr))?	//每小段字符串最大长度是否为3
+			(true == noMoreThan3charactor(addr))?	//最多是否只有3个.字符
+			(true == isLengthCorrect(addr))?		//地址最大长度是否为15
+			(true == allCharacterIsLegal(addr))?	//所有的字符是否都由0~9和.组成
+			true: false: false: false: false: false;
+}
+
+struct NetworkConfiguration {
+	std::string ip;
+	std::string sub_network_mask;
+	std::string gateway;
+	std::string dns1;
+	std::string dns2;
+};
+NetworkConfiguration GetNetworkConfiguration() {
+	char ip[64] = {0};
+	char mask[64] = {0};
+	char gateway[64] = {0};
+	char dns1[64] = {0};
+	char dns2[64] = {0};
+	if (ETHERNETMANAGER->isAutoMode()) {
+		ETHERNETMANAGER->getConfigureInfo(ip, mask, gateway, dns1, dns2);
+	} else {
+		ETHERNETMANAGER->getStaticConfigureInfo(ip, mask, gateway, dns1, dns2);
+	}
+	return NetworkConfiguration{ip, mask, gateway, dns1, dns2};
+}
+
+std::string read_file(const std::string &file) {
+  std::ifstream ifs(file.c_str(), std::ios::binary | std::ios::in);
+  if (!ifs.is_open()) {
+    return "";
+  }
+  std::stringstream ss;
+  ss << ifs.rdbuf();
+  ifs.close();
+  std::string content(ss.str());
+  return content;
+}
+
+//  Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4MxyDJzPWCVPEFFc
+std::string get_boundary(shttpd_arg *arg) {
+  int len = 0;
+  const char* boundary = shttpd_get_boundary(arg, &len);
+  if (boundary) {
+    return std::string(boundary, len);
+  }
+  return "";
+}
+
+/*
+ * This callback function is attached to the "/" and "/abc.html" URIs,
+ * thus is acting as "index.html" file. It shows a bunch of links
+ * to other URIs, and allows to change the value of program's
+ * internal variable. The pointer to that variable is passed to the
+ * callback function as arg->user_data.
+ */
+static void
+show_index(struct shttpd_arg *arg)
+{
+//  int   *p = arg->user_data;  /* integer passed to us */
+  char    wifi_name[128] = {0};
+  char    wifi_password[128] = {0};
+  const char  *request_method, *query_string;
+
+  request_method = shttpd_get_env(arg, "REQUEST_METHOD");
+//  request_uri = shttpd_get_env(arg, "REQUEST_URI");
+  query_string = shttpd_get_env(arg, "QUERY_STRING");
+
+  /* Change the value of integer variable */
+  if (!strcmp(request_method, "POST")) {
+    /* If not all data is POSTed, wait for the rest */
+    if (arg->flags & SHTTPD_MORE_POST_DATA)
+      return;
+
+    shttpd_get_var("wifi_name", arg->in.buf, arg->in.len,
+        wifi_name, sizeof(wifi_name));
+    shttpd_get_var("wifi_password", arg->in.buf, arg->in.len,
+        wifi_password, sizeof(wifi_password));
+//    mTextViewSetupNamePtr->setText(wifi_name);
+//    mTextViewSetupPasswordPtr->setText(wifi_password);
+//    StoragePreferences::putString(PROP_WIFI_NAME, wifi_name);
+//    StoragePreferences::putString(PROP_WIFI_PASSWORD, wifi_password);
+  } else if (query_string != NULL) { // GET
+//    (void) shttpd_get_var("name1", query_string,
+//        strlen(query_string), wifi_name, sizeof(wifi_name));
+  }
+
+  shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
+  shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");
+
+  std::string content = read_file(CONFIGMANAGER->getResFilePath("html/index.html"));
+  std::string serverIp = StoragePreferences::getString(STORE_GATEWAY, serverIP);
+  content = srv::format(content.c_str(),
+		  NETMANAGER->getEthernetManager()->getMacAddr(),
+		  NETMANAGER->getEthernetManager()->getIp(),
+		  GetNetworkConfiguration().sub_network_mask.c_str(),
+		  GetNetworkConfiguration().gateway.c_str(),
+		  GetNetworkConfiguration().dns1.c_str(),
+		  GetNetworkConfiguration().dns2.c_str(),
+		  serverIp.c_str());
+  std::string html(content);
+  shttpd_printf(arg, "%s", html.c_str());
+  if (html.empty()) {
+    shttpd_printf(arg, "%s", "read file failed");
+  }
+
+  arg->flags |= SHTTPD_END_OF_OUTPUT;
+}
+
+struct state {
+  size_t cl; /* Content-Length   */
+  size_t nread; /* Number of bytes read */
+  FILE *fp;
+  size_t file_length;
+  multipart_reader *reader;
+  HttpServer* server;
+  bool is_tf_mounted;
+};
+
+static void
+on_form_data_receive(const multipart_reader* reader, const char* data, uint64_t data_length) {
+//  LOGD("write form data [%s], filename = [%s] length %lld", reader->form_data_name, reader->form_data_filename, data_length);
+  if (strcmp(reader->form_data_name, "uploadFile") != 0) {
+    return;
+  }
+  if (data_length == 0) {
+    return;
+  }
+  if (reader->user_data == NULL) {
+    return;
+  }
+
+  struct state* state = (struct state*)reader->user_data;
+  if (state->fp == NULL) {
+    if (!state->is_tf_mounted) {
+      LOGD("未插入SD卡");
+      return;
+    }
+    state->fp = fopen(srv::format("/mnt/extsd/%s", reader->form_data_filename).c_str(), "wb");
+    if (state->fp == NULL) {
+      LOGE("创建文件失败");
+      return;
+    }
+  }
+
+  state->file_length += data_length;
+
+  if (data_length != fwrite(data, 1, data_length, state->fp)) {
+    LOGE("write form data failed");
+    return;
+  }
+}
+
+static void
+upload_file(struct shttpd_arg *arg)
+ {
+  const char *s;
+
+  const char* request_method = shttpd_get_env(arg, "REQUEST_METHOD");
+  if (strcmp(request_method, "POST") != 0) {
+    return;
+  }
+
+  /* If the connection was broken prematurely, cleanup */
+  if ((arg->flags & SHTTPD_CONNECTION_ERROR) && arg->state) {
+    struct state* state = (struct state*) arg->state;
+    if (state->fp) {
+      fclose(state->fp);
+    }
+    sync();
+    multipart_reader_destroy(state->reader);
+    free(state);
+  } else if ((s = shttpd_get_header(arg, "Content-Length")) == NULL) {
+    shttpd_printf(arg, "HTTP/1.0 411 Length Required\n\n");
+    arg->flags |= SHTTPD_END_OF_OUTPUT;
+  } else if (arg->state == NULL) {
+    /* New request. Allocate a state structure, and open a file */
+    struct state* state = (struct state*)calloc(1, sizeof(*state));
+    arg->state = state;
+    state->cl = strtoul(s, NULL, 10);
+    state->server = (HttpServer*)arg->user_data;
+    state->is_tf_mounted = MOUNTMONITOR->isMount();
+
+    std::string boundary = get_boundary(arg);
+    if (!boundary.empty()) {
+      LOGD("boundary=%s", boundary.c_str());
+    }
+    state->reader = multipart_reader_create(boundary.c_str(), on_form_data_receive, state);
+
+    shttpd_printf(arg, "HTTP/1.0 200 OK\n"
+        "Content-Type: text/plain\n\n");
+  } else {
+    struct state* state = (struct state*)arg->state;
+
+    if(0 != multipart_reader_execute(state->reader, arg->in.buf, arg->in.len)) {
+      LOGE("multipart_parser failed");
+    }
+
+    state->nread += arg->in.len;
+
+    /* Tell SHTTPD we have processed all data */
+    arg->in.num_bytes = arg->in.len;
+
+    if (state->server->upload_filename_listener()) {
+      state->server->upload_filename_listener()(state->reader->form_data_filename);
+    }
+    if (state->server->upload_progress_listener()) {
+      state->server->upload_progress_listener()(
+          (state->nread > state->cl) ? state->cl : state->nread,
+              state->cl);
+    }
+
+    /* Data stream finished? Close the file, and free the state */
+    if (state->nread >= state->cl) {
+      shttpd_printf(arg, "Written %d bytes", state->file_length);
+      if (!state->is_tf_mounted) {
+        shttpd_printf(arg, ", No TF card is mounted!");
+      }
+      LOGD("file length = %d", state->file_length);
+      if (state->fp) {
+        fflush(state->fp);
+        fclose(state->fp);
+      }
+      sync();
+      free(state);
+      arg->flags |= SHTTPD_END_OF_OUTPUT;
+    }
+  }
+}
+
+static void
+setting(struct shttpd_arg *arg){
+	char dhcp[10] = {0};
+	char ip[64] = {0};
+	char netmask[64] = {0};
+	char gateway[64] = {0};
+	char dns1[64] = {0};
+	char dns2[64] = {0};
+	char serverIp[64] = {0};
+
+	const char *s;
+
+	const char* request_method = shttpd_get_env(arg, "REQUEST_METHOD");
+	//仅接受post
+	if (strcmp(request_method, "POST") != 0) {
+		return;
+	}
+
+	/* If the connection was broken prematurely, cleanup */
+	if ((arg->flags & SHTTPD_CONNECTION_ERROR) && arg->state) {
+		struct state* state = (struct state*) arg->state;
+		if (state->fp) {
+			fclose(state->fp);
+		}
+		sync();
+		free(state);
+	} else if ((s = shttpd_get_header(arg, "Content-Length")) == NULL) {
+		shttpd_printf(arg, "HTTP/1.0 411 Length Required\n\n");
+		arg->flags |= SHTTPD_END_OF_OUTPUT;
+	} else if (arg->state == NULL) {
+		/* New request. Allocate a state structure, and open a file */
+		struct state* state = (struct state*)calloc(1, sizeof(*state));
+		arg->state = state;
+		state->cl = strtoul(s, NULL, 10);
+		state->server = (HttpServer*)arg->user_data;
+
+		shttpd_get_var("dhcp", arg->in.buf, arg->in.len,
+				dhcp, sizeof(dhcp));
+		LOGD(">>>>>>>>>>>>> dhcp %s", dhcp);
+		shttpd_get_var("ip", arg->in.buf, arg->in.len,
+				ip, sizeof(ip));
+		LOGD(">>>>>>>>>>>>> ip %s", ip);
+		shttpd_get_var("netmask", arg->in.buf, arg->in.len,
+				netmask, sizeof(netmask));
+		LOGD(">>>>>>>>>>>>> netmask %s", netmask);
+		shttpd_get_var("gateway", arg->in.buf, arg->in.len,
+				gateway, sizeof(gateway));
+		LOGD(">>>>>>>>>>>>> gateway %s", gateway);
+		shttpd_get_var("dns1", arg->in.buf, arg->in.len,
+				dns1, sizeof(dns1));
+		LOGD(">>>>>>>>>>>>> dns1 %s", dns1);
+		shttpd_get_var("dns2", arg->in.buf, arg->in.len,
+				dns2, sizeof(dns2));
+		LOGD(">>>>>>>>>>>>> dns2 %s", dns2);
+		shttpd_get_var("server_ip", arg->in.buf, arg->in.len,
+				serverIp, sizeof(serverIp));
+		LOGD(">>>>>>>>>>>>> server ip %s", serverIp);
+
+		std::string content = read_file(CONFIGMANAGER->getResFilePath("html/setting_result.html"));
+
+//		std::string _ip = ip;
+//		std::string _netmask = netmask;
+//		std::string _gateway = gateway;
+//		std::string _dns1 = dns1;
+//		std::string _dns2 = dns2;
+//		std::string _serverIp = serverIp;
+
+		std::string msg = "setting ok. rebooting... pls wait.";
+		if (!checkAddr(ip)){
+			msg = "ip wrong input";
+		} else if (!checkAddr(netmask)){
+			msg = "netmask wrong input";
+		} else if (!checkAddr(gateway)){
+			msg = "gateway wrong input";
+		} else if (!checkAddr(serverIp)){
+			msg = "server_ip wrong input";
+		} else {
+			std::string _dhcp = dhcp;
+			if (_dhcp == "1"){
+				ETHERNETMANAGER->setAutoMode(true);
+			} else {
+				ETHERNETMANAGER->setAutoMode(false);
+				ETHERNETMANAGER->configure(ip, netmask, gateway, dns1, dns2);
+			}
+
+			StoragePreferences::putString(STORE_GATEWAY, serverIp);
+		}
+
+		content = srv::format(content.c_str(), msg.c_str());
+
+		shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
+		shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");
+
+		std::string html(content);
+		shttpd_printf(arg, "%s", html.c_str());
+		if (html.empty()) {
+			shttpd_printf(arg, "%s", "read file failed");
+		}
+
+		arg->flags |= SHTTPD_END_OF_OUTPUT;
+		my_thread.run("reboot");
+	} else {
+
+	}
+}
+
+/*
+ * This callback function is used to show how to handle 404 error
+ */
+static void
+show_404(struct shttpd_arg *arg)
+{
+  shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
+  shttpd_printf(arg, "%s", "Content-Type: text/plain\r\n\r\n");
+  shttpd_printf(arg, "%s", "Oops. File not found! ");
+  shttpd_printf(arg, "%s", "This is a custom error handler.");
+  arg->flags |= SHTTPD_END_OF_OUTPUT;
+}
+
+/*
+ * Make sure we have ho zombies from CGIs
+ */
+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:
+    break;
+  }
+}
+
+HttpServer::HttpServer() {
+  stop_ = false;
+  port_ = 80;
+  upload_filename_listener_ = NULL;
+  upload_progress_listener_ = NULL;
+}
+
+HttpServer::~HttpServer() {
+}
+
+bool HttpServer::threadLoop() {
+  stop_ = false;
+  RunSync(port_);
+  return false;
+}
+
+void HttpServer::RunAsync(int port) {
+  port_ = port;
+  stop_ = false;
+  run("shttpd");
+}
+
+int HttpServer::RunSync(int port) {
+  struct shttpd_ctx *ctx;
+
+#ifndef _WIN32
+  signal(SIGPIPE, SIG_IGN);
+  signal(SIGCHLD, &signal_handler);
+#endif /* !_WIN32 */
+
+  /*
+   * Initialize SHTTPD context.
+   * Start listening on ports 8080 and 8081
+   */
+  ctx = shttpd_init(0, NULL);
+  shttpd_set_option(ctx, "ports", format("%d", port).c_str());
+
+  /* 注册uri和它的回调函数*/
+  shttpd_register_uri(ctx, "/", &show_index, NULL);
+  shttpd_register_uri(ctx, "/setting", &setting, this);
+
+  shttpd_handle_error(ctx, 404, show_404, NULL);
+
+  /* Serve connections infinitely until someone kills us */
+  while (!stop_) {
+    shttpd_poll(ctx, 1000);
+  }
+
+  /* Probably unreached, because we will be killed by a signal */
+  shttpd_fini(ctx);
+
+  return 0;
+}
+
+void HttpServer::Stop() {
+  stop_ = true;
+  requestExitAndWait();
+  stop_ = false;
+}
+
+void HttpServer::set_upload_filename_listener(UploadFilenameListener listener) {
+  upload_filename_listener_ = listener;
+}
+void HttpServer::set_upload_progress_listener(UploadProgressListener listener) {
+  upload_progress_listener_ = listener;
+}
+
+HttpServer::UploadFilenameListener HttpServer::upload_filename_listener() {
+  return upload_filename_listener_;
+}
+HttpServer::UploadProgressListener HttpServer::upload_progress_listener() {
+  return upload_progress_listener_;
+}
+
+
+} /* namespace srv */

+ 66 - 0
jni/server/http_server.h

@@ -0,0 +1,66 @@
+#pragma once
+
+#include <stdarg.h>
+#include <stdint.h>
+
+#include <string>
+#include <system/Thread.h>
+
+
+namespace srv {
+
+class HttpServer : protected Thread {
+public:
+  typedef void (*UploadFilenameListener)(const std::string& filename);
+  typedef void (*UploadProgressListener)(uint64_t uploaded_bytes, uint64_t all_bytes);
+
+  HttpServer();
+  virtual ~HttpServer();
+  virtual bool threadLoop();
+
+  /**
+   * 在新线程运行
+   */
+  void RunAsync(int port);
+  /**
+   * 在当前线程运行,会阻塞
+   */
+  int RunSync(int port);
+  /**
+   * 停止
+   */
+  void Stop();
+
+  /**
+   * 设置上传文件名回调
+   */
+  void set_upload_filename_listener(UploadFilenameListener listener);
+  /**
+   * 设置上传进度回调
+   */
+  void set_upload_progress_listener(UploadProgressListener listener);
+
+  UploadFilenameListener upload_filename_listener();
+  UploadProgressListener upload_progress_listener();
+
+private:
+  bool stop_;
+  int port_;
+  UploadFilenameListener upload_filename_listener_;
+  UploadProgressListener upload_progress_listener_;
+};
+
+inline std::string format(const char* format, ...) {
+  std::string tmp;
+  va_list vl;
+  va_start(vl, format);
+  size_t num = vsnprintf(0, 0, format, vl);
+  if (num >= tmp.capacity()) tmp.reserve(num + sizeof(char));
+
+  tmp.resize(num);
+  vsnprintf((char*)tmp.data(), tmp.capacity(), format, vl);
+  va_end(vl);
+  return tmp;
+}
+
+} /* namespace srv */

+ 2 - 2
jni/service/BusinessConfig.h

@@ -51,8 +51,8 @@
 #define STORE_SCREEN_LIGHT "screen_light"
 #define SIGNAL_TYPE "TCP" //TCP,SIP
 
-static std::string version = "v1.0.23";
-static int versionNo = 23;
+static std::string version = "v1.0.25";
+static int versionNo = 25;
 static std::string serverIP = "172.28.100.100";
 static std::string tcpIP = "172.28.100.100";
 static int serverHttpPort = 8006;

+ 419 - 0
jni/shttpd/auth.c

@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_AUTH)
+/*
+ * Stringify binary data. Output buffer must be twice as big as input,
+ * because each byte takes 2 bytes in string representation
+ */
+static void
+bin2str(char *to, const unsigned char *p, size_t len)
+{
+	const char	*hex = "0123456789abcdef";
+
+	for (;len--; p++) {
+		*to++ = hex[p[0] >> 4];
+		*to++ = hex[p[0] & 0x0f];
+	}
+}
+
+/*
+ * Return stringified MD5 hash for list of vectors.
+ * buf must point to at least 32-bytes long buffer
+ */
+static void
+md5(char *buf, ...)
+{
+	unsigned char	hash[16];
+	const struct vec *v;
+	va_list		ap;
+	MD5_CTX	ctx;
+	int		i;
+
+	MD5Init(&ctx);
+
+	va_start(ap, buf);
+	for (i = 0; (v = va_arg(ap, const struct vec *)) != NULL; i++) {
+		assert(v->len >= 0);
+		if (v->len == 0)
+			continue;
+		if (i > 0)
+			MD5Update(&ctx, (unsigned char *) ":", 1);
+		MD5Update(&ctx,(unsigned char *)v->ptr,(unsigned int)v->len);
+	}
+	va_end(ap);
+
+	MD5Final(hash, &ctx);
+	bin2str(buf, hash, sizeof(hash));
+}
+
+/*
+ * Compare to vectors. Return 1 if they are equal
+ */
+static int
+vcmp(const struct vec *v1, const struct vec *v2)
+{
+	return (v1->len == v2->len && !memcmp(v1->ptr, v2->ptr, v1->len));
+}
+
+struct digest {
+	struct vec	user;
+	struct vec	uri;
+	struct vec	nonce;
+	struct vec	cnonce;
+	struct vec	resp;
+	struct vec	qop;
+	struct vec	nc;
+};
+
+static const struct auth_keyword {
+	size_t		offset;
+	struct vec	vec;
+} known_auth_keywords[] = {
+	{offsetof(struct digest, user),		{"username=",	9}},
+	{offsetof(struct digest, cnonce),	{"cnonce=",	7}},
+	{offsetof(struct digest, resp),		{"response=",	9}},
+	{offsetof(struct digest, uri),		{"uri=",	4}},
+	{offsetof(struct digest, qop),		{"qop=",	4}},
+	{offsetof(struct digest, nc),		{"nc=",		3}},
+	{offsetof(struct digest, nonce),	{"nonce=",	6}},
+	{0,					{NULL,		0}}
+};
+
+static void
+parse_authorization_header(const struct vec *h, struct digest *dig)
+{
+	const unsigned char	*p, *e, *s;
+	struct vec		*v, vec;
+	const struct auth_keyword *kw;
+
+	(void) memset(dig, 0, sizeof(*dig));
+	p = (unsigned char *) h->ptr + 7;
+	e = (unsigned char *) h->ptr + h->len;
+
+	while (p < e) {
+
+		/* Skip spaces */
+		while (p < e && (*p == ' ' || *p == ','))
+			p++;
+
+		/* Skip to "=" */
+		for (s = p; s < e && *s != '='; )
+			s++;
+		s++;
+
+		/* Is it known keyword ? */
+		for (kw = known_auth_keywords; kw->vec.len > 0; kw++)
+			if (kw->vec.len <= s - p &&
+			    !memcmp(p, kw->vec.ptr, kw->vec.len))
+				break;
+
+		if (kw->vec.len == 0)
+			v = &vec;		/* Dummy placeholder	*/
+		else
+			v = (struct vec *) ((char *) dig + kw->offset);
+
+		if (*s == '"') {
+			p = ++s;
+			while (p < e && *p != '"')
+				p++;
+		} else {
+			p = s;
+			while (p < e && *p != ' ' && *p != ',')
+				p++;
+		}
+
+		v->ptr = (char *) s;
+		v->len = p - s;
+
+		if (*p == '"')
+			p++;
+
+		DBG(("auth field [%.*s]", v->len, v->ptr));
+	}
+}
+
+/*
+ * Check the user's password, return 1 if OK
+ */
+static int
+check_password(int method, const struct vec *ha1, const struct digest *digest)
+{
+	char		a2[32], resp[32];
+	struct vec	vec_a2;
+
+	/* XXX  Due to a bug in MSIE, we do not compare the URI	 */
+	/* Also, we do not check for authentication timeout */
+	if (/*strcmp(dig->uri, c->ouri) != 0 || */
+	    digest->resp.len != 32 /*||
+	    now - strtoul(dig->nonce, NULL, 10) > 3600 */)
+		return (0);
+
+	md5(a2, &_shttpd_known_http_methods[method], &digest->uri, NULL);
+	vec_a2.ptr = a2;
+	vec_a2.len = sizeof(a2);
+	md5(resp, ha1, &digest->nonce, &digest->nc,
+	    &digest->cnonce, &digest->qop, &vec_a2, NULL);
+	DBG(("%s: uri [%.*s] expected_resp [%.*s] resp [%.*s]",
+	    "check_password", digest->uri.len, digest->uri.ptr,
+	    32, resp, digest->resp.len, digest->resp.ptr));
+
+	return (!memcmp(resp, digest->resp.ptr, 32));
+}
+
+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 */
+		_shttpd_snprintf(name, sizeof(name), "%s",
+		    ctx->options[OPT_AUTH_GPASSWD]);
+	} 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) _shttpd_snprintf(name, sizeof(name), "%.*s/%s",
+		    (int) (e - p), p, HTPASSWD);
+	}
+
+	if ((fd = _shttpd_open(name, O_RDONLY, 0)) == -1) {
+		DBG(("open_auth_file: open(%s)", name));
+	} else if ((fp = fdopen(fd, "r")) == NULL) {
+		DBG(("open_auth_file: fdopen(%s)", name));
+		(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 &&
+	    !_shttpd_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
+_shttpd_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;
+			if (n > (int) sizeof(protected_path) - 1)
+				n = sizeof(protected_path) - 1;
+
+			_shttpd_strlcpy(protected_path, p + 1, n);
+
+			if ((fp = fopen(protected_path, "r")) == NULL)
+				_shttpd_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
+_shttpd_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
+_shttpd_send_authorization_request(struct conn *c)
+{
+	char	buf[512];
+
+	(void) _shttpd_snprintf(buf, sizeof(buf), "Unauthorized\r\n"
+	    "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
+	    "nonce=\"%lu\"", c->ctx->options[OPT_AUTH_REALM],
+	    (unsigned long) _shttpd_current_time);
+
+	_shttpd_send_server_error(c, 401, buf);
+}
+
+/*
+ * Edit the passwords file.
+ */
+int
+_shttpd_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) _shttpd_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)
+		_shttpd_elog(E_FATAL, NULL,
+		    "Cannot open %s: %s", fname, strerror(errno));
+	else if ((fp2 = fopen(tmp, "w+")) == NULL)
+		_shttpd_elog(E_FATAL, NULL,
+		    "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) _shttpd_remove(fname);
+	(void) _shttpd_rename(tmp, fname);
+
+	return (ret);
+}
+#endif /* NO_AUTH */

+ 229 - 0
jni/shttpd/cgi.c

@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_CGI)
+struct env_block {
+	char	buf[ENV_MAX];		/* Environment buffer		*/
+	int	len;			/* Space taken			*/
+	char	*vars[CGI_ENV_VARS];	/* Point into the buffer	*/
+	int	nvars;			/* Number of variables		*/
+};
+
+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, *fname, *root = c->ctx->options[OPT_ROOT];
+	size_t			len;
+
+	blk->len = blk->nvars = 0;
+
+	/* SCRIPT_FILENAME */
+	fname = prog;
+	if ((s = strrchr(prog, '/')))
+		fname = s + 1;
+
+	/* Prepare the environment block */
+	addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
+	addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
+	addenv(blk, "%s", "REDIRECT_STATUS=200");	/* PHP */
+	addenv(blk, "SERVER_PORT=%d", c->loc_port);
+	addenv(blk, "SERVER_NAME=%s", c->ctx->options[OPT_AUTH_REALM]);
+	addenv(blk, "SERVER_ROOT=%s", root);
+	addenv(blk, "DOCUMENT_ROOT=%s", root);
+	addenv(blk, "REQUEST_METHOD=%s",
+			_shttpd_known_http_methods[c->method].ptr);
+	addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(c->sa.u.sin.sin_addr));
+	addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
+	addenv(blk, "REQUEST_URI=%s", c->uri);
+	addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
+	addenv(blk, "SCRIPT_FILENAME=%s", fname);	/* PHP */
+	addenv(blk, "PATH_TRANSLATED=%s", prog);
+
+	if (h->ct.v_vec.len > 0)
+		addenv(blk, "CONTENT_TYPE=%.*s", 
+		    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
+_shttpd_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) _shttpd_snprintf(dir, sizeof(dir), "%s", prog);
+	for (p = dir + strlen(dir) - 1; p > dir; p--)
+		if (*p == '/') {
+			*p++ = '\0';
+			break;
+		}
+	
+	if (shttpd_socketpair(pair) != 0) {
+		ret = -1;
+	} else if (_shttpd_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
+_shttpd_do_cgi(struct conn *c)
+{
+	DBG(("running CGI: [%s]", c->uri));
+	assert(c->loc.io.size > CGI_REPLY_LEN);
+	memcpy(c->loc.io.buf, CGI_REPLY, CGI_REPLY_LEN);
+	c->loc.io.head = c->loc.io.tail = c->loc.io.total = CGI_REPLY_LEN;
+	c->loc.io_class = &_shttpd_io_cgi;
+	c->loc.flags = FLAG_R;
+	if (c->method == METHOD_POST)
+		c->loc.flags |= FLAG_W;
+}
+
+#endif /* !NO_CGI */

+ 128 - 0
jni/shttpd/compat_unix.c

@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+void 
+_shttpd_set_close_on_exec(int fd)
+{
+	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+int
+_shttpd_stat(const char *path, struct stat *stp)
+{
+	return (stat(path, stp));
+}
+
+int
+_shttpd_open(const char *path, int flags, int mode)
+{
+	return (open(path, flags, mode));
+}
+
+int
+_shttpd_remove(const char *path)
+{
+	return (remove(path));
+}
+
+int
+_shttpd_rename(const char *path1, const char *path2)
+{
+	return (rename(path1, path2));
+}
+
+int
+_shttpd_mkdir(const char *path, int mode)
+{
+	return (mkdir(path, mode));
+}
+
+char *
+_shttpd_getcwd(char *buffer, int maxlen)
+{
+	return (getcwd(buffer, maxlen));
+}
+
+int
+_shttpd_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
+_shttpd_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;
+		_shttpd_elog(E_LOG, c, "redirect: fork: %s", strerror(errno));
+
+	} else if (pid == 0) {
+
+		/* Child */
+
+		(void) chdir(dir);
+		(void) dup2(sock, 0);
+		(void) dup2(sock, 1);
+		(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);
+			_shttpd_elog(E_FATAL, c, "redirect: exec(%s)", prog);
+		} else {
+			(void) execle(interp, interp, p, NULL, envp);
+			_shttpd_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 */

+ 36 - 0
jni/shttpd/compat_unix.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2004-2007 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/mman.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+
+#include <pwd.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <dlfcn.h>
+
+#if !defined(NO_THREADS)
+#include "pthread.h"
+#define	_beginthread(a, b, c) do { pthread_t tid; \
+	pthread_create(&tid, NULL, (void *(*)(void *))a, c); } while (0)
+#endif /* !NO_THREADS */
+
+//#define	SSL_LIB				"/usr/lib/x86_64-linux-gnu/libssl.so.1.0.0"
+#define	SSL_LIB				"/lib/libssl.so.1.0.0"
+#define	DIRSEP				'/'
+#define	IS_DIRSEP_CHAR(c)		((c) == '/')
+#define	O_BINARY			0
+#define	closesocket(a)			close(a)
+#define	ERRNO				errno

+ 29 - 0
jni/shttpd/config.h

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

+ 395 - 0
jni/shttpd/defs.h

@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#ifndef DEFS_HEADER_DEFINED
+#define	DEFS_HEADER_DEFINED
+
+#include "std_includes.h"
+#include "llist.h"
+#include "io.h"
+#include "md5.h"
+#include "config.h"
+#include "shttpd.h"
+
+#define	NELEMS(ar)	(sizeof(ar) / sizeof(ar[0]))
+
+#ifdef _DEBUG
+#define	DBG(x)	do { printf x ; putchar('\n'); fflush(stdout); } while (0)
+#else
+#define	DBG(x)
+#endif /* DEBUG */
+
+/*
+ * Darwin prior to 7.0 and Win32 do not have socklen_t
+ */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+
+/*
+ * For parsing. This guy represents a substring.
+ */
+struct vec {
+	const char	*ptr;
+	int		len;
+};
+
+#if !defined(FALSE)
+enum {FALSE, TRUE};
+#endif /* !FALSE */
+
+enum {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			headers_len;
+	big_int_t		content_len;
+	unsigned int		flags;
+#define	FLAG_HEADERS_PARSED	1
+#define	FLAG_SSL_ACCEPTED	2
+#define	FLAG_R			4		/* Can read in general	*/
+#define	FLAG_W			8		/* Can write in general	*/
+#define	FLAG_CLOSED		16
+#define	FLAG_DONT_CLOSE		32
+#define	FLAG_ALWAYS_READY	64		/* File, dir, user_func	*/
+#define	FLAG_SUSPEND		128
+};
+
+struct worker {
+	struct llhead	link;
+	int		num_conns;	/* Num of active connections 	*/
+	int		exit_flag;	/* Ditto - exit flag		*/
+	int		ctl[2];		/* Control socket pair		*/
+	struct shttpd_ctx *ctx;		/* Context reference		*/
+	struct llhead	connections;	/* List of connections		*/
+};
+
+struct conn {
+	struct llhead	link;		/* Connections chain		*/
+	struct worker	*worker;	/* Worker this conn belongs to	*/
+	struct shttpd_ctx *ctx;		/* Context this conn belongs to */
+	struct usa	sa;		/* Remote socket address	*/
+	time_t		birth_time;	/* Creation time		*/
+	time_t		expire_time;	/* Expiration time		*/
+
+	int		loc_port;	/* Local port			*/
+	int		status;		/* Reply status code		*/
+	int		method;		/* Request method		*/
+	char		*uri;		/* Decoded URI			*/
+	unsigned long	major_version;	/* Major HTTP version number    */
+	unsigned long	minor_version;	/* Minor HTTP version number    */
+	char		*request;	/* Request line			*/
+	char		*headers;	/* Request headers		*/
+	char		*query;		/* QUERY_STRING part of the URI	*/
+	char		*path_info;	/* PATH_INFO thing		*/
+	struct vec	mime_type;	/* Mime type			*/
+
+	struct headers	ch;		/* Parsed client headers	*/
+
+	struct stream	loc;		/* Local stream			*/
+	struct stream	rem;		/* Remote stream		*/
+
+#if !defined(NO_SSI)
+	void			*ssi;	/* SSI descriptor		*/
+#endif /* NO_SSI */
+};
+
+enum {
+	OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST,
+	OPT_CGI_EXTENSIONS, OPT_CGI_INTERPRETER, OPT_CGI_ENVIRONMENT,
+	OPT_SSI_EXTENSIONS, OPT_AUTH_REALM, OPT_AUTH_GPASSWD,
+	OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, OPT_MIME_TYPES,
+	OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_INETD, OPT_UID,
+	OPT_CFG_URI, OPT_PROTECT, OPT_SERVICE, OPT_HIDE, OPT_THREADS,
+	NUM_OPTIONS
+};
+
+/*
+ * SHTTPD context
+ */
+struct shttpd_ctx {
+	SSL_CTX		*ssl_ctx;	/* SSL context			*/
+
+	struct llhead	registered_uris;/* User urls			*/
+	struct llhead	error_handlers;	/* Embedded error handlers	*/
+	struct llhead	acl;		/* Access control list		*/
+	struct llhead	ssi_funcs;	/* SSI callback functions	*/
+	struct llhead	listeners;	/* Listening sockets		*/
+	struct llhead	workers;	/* Worker workers		*/
+
+	FILE		*access_log;	/* Access log stream		*/
+	FILE		*error_log;	/* Error log stream		*/
+
+	char	*options[NUM_OPTIONS];	/* Configurable options		*/
+#if defined(__rtems__)
+	rtems_id         mutex;
+#endif /* _WIN32 */
+};
+
+struct listener {
+	struct llhead		link;
+	struct shttpd_ctx	*ctx;	/* Context that socket belongs	*/
+	int			sock;	/* Listening socket		*/
+	int			is_ssl;	/* Should be SSL-ed		*/
+};
+
+/* Types of messages that could be sent over the control socket */
+enum {CTL_PASS_SOCKET, CTL_WAKEUP};
+
+/*
+ * In SHTTPD, list of values are represented as comma or space separated
+ * string. For example, list of CGI extensions can be represented as
+ * ".cgi,.php,.pl", or ".cgi .php .pl". The macro that follows allows to
+ * loop through the individual values in that list.
+ *
+ * A "const char *" pointer and size_t variable must be passed to the macro.
+ * Spaces or commas can be used as delimiters (macro DELIM_CHARS).
+ *
+ * In every iteration of the loop, "s" points to the current value, and
+ * "len" specifies its length. The code inside loop must not change
+ * "s" and "len" parameters.
+ */
+#define	FOR_EACH_WORD_IN_LIST(s,len)					\
+	for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0;	\
+			s += len, s+= strspn(s, DELIM_CHARS))
+
+/*
+ * IPv4 ACL entry. Specifies subnet with deny/allow flag
+ */
+struct acl {
+	struct llhead	link;
+	uint32_t	ip;		/* IP, in network byte order	*/
+	uint32_t	mask;		/* Also in network byte order	*/
+	int		flag;		/* Either '+' or '-'		*/
+};
+
+/*
+ * shttpd.c
+ */
+extern time_t	_shttpd_current_time;	/* Current UTC time		*/
+extern int	_shttpd_tz_offset;	/* Offset from GMT time zone	*/
+extern const struct vec _shttpd_known_http_methods[];
+
+extern void	_shttpd_stop_stream(struct stream *stream);
+extern int	_shttpd_url_decode(const char *, int, char *dst, int);
+extern void	_shttpd_send_server_error(struct conn *, int, const char *);
+extern int	_shttpd_get_headers_len(const char *buf, size_t buflen);
+extern void	_shttpd_parse_headers(const char *s, int, struct headers *);
+extern int	_shttpd_is_true(const char *str);
+extern int	_shttpd_socketpair(int pair[2]);
+extern void	_shttpd_get_mime_type(struct shttpd_ctx *,
+			const char *, int, struct vec *);
+
+#define	IS_TRUE(ctx, opt)	_shttpd_is_true((ctx)->options[opt])
+
+/*
+ * config.c
+ */
+extern void	_shttpd_usage(const char *prog);
+
+/*
+ * log.c
+ */
+extern void	_shttpd_elog(int flags, struct conn *c, const char *fmt, ...);
+extern void	_shttpd_log_access(FILE *fp, const struct conn *c);
+
+/*
+ * string.c
+ */
+extern void	_shttpd_strlcpy(register char *, register const char *, size_t);
+extern int	_shttpd_strncasecmp(register const char *,
+			register const char *, size_t);
+extern char	*_shttpd_strndup(const char *ptr, size_t len);
+extern char	*_shttpd_strdup(const char *str);
+extern int	_shttpd_snprintf(char *buf, size_t len, const char *fmt, ...);
+extern int	_shttpd_match_extension(const char *path, const char *ext_list);
+
+/*
+ * compat_*.c
+ */
+extern void	_shttpd_set_close_on_exec(int fd);
+extern int	_shttpd_set_non_blocking_mode(int fd);
+extern int	_shttpd_stat(const char *, struct stat *stp);
+extern int	_shttpd_open(const char *, int flags, int mode);
+extern int	_shttpd_remove(const char *);
+extern int	_shttpd_rename(const char *, const char *);
+extern int	_shttpd_mkdir(const char *, int);
+extern char *	_shttpd_getcwd(char *, int);
+extern int	_shttpd_spawn_process(struct conn *c, const char *prog,
+			char *envblk, char *envp[], int sock, const char *dir);
+
+extern int	_shttpd_set_nt_service(struct shttpd_ctx *, const char *);
+extern int	_shttpd_set_systray(struct shttpd_ctx *, const char *);
+extern void	_shttpd_try_to_run_as_nt_service(void);
+
+/*
+ * io_*.c
+ */
+extern const struct io_class	_shttpd_io_file;
+extern const struct io_class	_shttpd_io_socket;
+extern const struct io_class	_shttpd_io_ssl;
+extern const struct io_class	_shttpd_io_cgi;
+extern const struct io_class	_shttpd_io_dir;
+extern const struct io_class	_shttpd_io_embedded;
+extern const struct io_class	_shttpd_io_ssi;
+
+extern int	_shttpd_put_dir(const char *path);
+extern void	_shttpd_get_dir(struct conn *c);
+extern void	_shttpd_get_file(struct conn *c, struct stat *stp);
+extern void	_shttpd_ssl_handshake(struct stream *stream);
+extern void	_shttpd_setup_embedded_stream(struct conn *,
+			union variant, void *);
+extern struct registered_uri *_shttpd_is_registered_uri(struct shttpd_ctx *,
+			const char *uri);
+extern void	_shttpd_do_ssi(struct conn *);
+extern void	_shttpd_ssi_func_destructor(struct llhead *lp);
+
+/*
+ * auth.c
+ */
+extern int	_shttpd_check_authorization(struct conn *c, const char *path);
+extern int	_shttpd_is_authorized_for_put(struct conn *c);
+extern void	_shttpd_send_authorization_request(struct conn *c);
+extern int	_shttpd_edit_passwords(const char *fname, const char *domain,
+			const char *user, const char *pass);
+
+/*
+ * cgi.c
+ */
+extern int	_shttpd_run_cgi(struct conn *c, const char *prog);
+extern void	_shttpd_do_cgi(struct conn *c);
+
+#define CGI_REPLY	"HTTP/1.1     OK\r\n"
+#define	CGI_REPLY_LEN	(sizeof(CGI_REPLY) - 1)
+
+#endif /* DEFS_HEADER_DEFINED */

+ 97 - 0
jni/shttpd/io.h

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

+ 127 - 0
jni/shttpd/io_cgi.c

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

+ 153 - 0
jni/shttpd/io_dir.c

@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+/*
+ * For a given PUT path, create all intermediate subdirectories
+ * for given path. Return 0 if the path itself is a directory,
+ * or -1 on error, 1 if OK.
+ */
+int
+_shttpd_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 (_shttpd_stat(buf, &st) == -1 &&
+		    _shttpd_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)
+{
+	static const char footer[] = "</table></body></html>\n";
+
+	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)
+			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) _shttpd_snprintf(file, sizeof(file),
+		    "%s%s%s", stream->chan.dir.path, slash, dp->d_name);
+		(void) _shttpd_stat(file, &st);
+		if (S_ISDIR(st.st_mode)) {
+			_shttpd_snprintf(size,sizeof(size),"%s","&lt;DIR&gt;");
+		} else {
+			if (st.st_size < 1024)
+				(void) _shttpd_snprintf(size, sizeof(size),
+				    "%lu", (unsigned long) st.st_size);
+			else if (st.st_size < 1024 * 1024)
+				(void) _shttpd_snprintf(size,
+				    sizeof(size), "%luk",
+				    (unsigned long) (st.st_size >> 10)  + 1);
+			else
+				(void) _shttpd_snprintf(size, sizeof(size),
+				    "%.1fM", (float) st.st_size / 1048576);
+		}
+		(void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
+			localtime(&st.st_mtime));
+
+		n = _shttpd_snprintf(line, sizeof(line),
+		    "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
+		    "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+		    c->uri, slash, dp->d_name, dp->d_name,
+		    S_ISDIR(st.st_mode) ? "/" : "", mod, size);
+		(void) memcpy(buf, line, n);
+		buf = (char *) buf + n;
+		nwritten += n;
+		len -= n;
+	} while (dp != NULL);
+
+	/* Append proper HTML footer for the page */
+	if (dp == NULL && len >= sizeof(footer)) {
+		(void) memcpy(buf, footer, sizeof(footer));
+		nwritten += sizeof(footer);
+		stream->flags |= FLAG_CLOSED;
+	}
+
+	return (nwritten);
+}
+
+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
+_shttpd_get_dir(struct conn *c)
+{
+	if ((c->loc.chan.dir.dirp = opendir(c->loc.chan.dir.path)) == NULL) {
+		(void) free(c->loc.chan.dir.path);
+		_shttpd_send_server_error(c, 500, "Cannot open directory");
+	} else {
+		c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
+		    "HTTP/1.1 200 OK\r\n"
+		    "Connection: close\r\n"
+		    "Content-Type: text/html; charset=utf-8\r\n\r\n"
+		    "<html><head><title>Index of %s</title>"
+		    "<style>th {text-align: left;}</style></head>"
+		    "<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 = &_shttpd_io_dir;
+		c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
+	}
+}
+
+const struct io_class	_shttpd_io_dir =  {
+	"dir",
+	read_dir,
+	NULL,
+	close_dir
+};

+ 313 - 0
jni/shttpd/io_emb.c

@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+const char *
+shttpd_version(void)
+{
+	return (VERSION);
+}
+
+static void
+call_user(struct conn *c, struct shttpd_arg *arg, shttpd_callback_t func)
+{
+	arg->priv		= c;
+	arg->state		= c->loc.chan.emb.state;
+	arg->out.buf		= io_space(&c->loc.io);
+	arg->out.len		= io_space_len(&c->loc.io);
+	arg->out.num_bytes	= 0;
+	arg->in.buf		= io_data(&c->rem.io);;
+	arg->in.len		= io_data_len(&c->rem.io);
+	arg->in.num_bytes	= 0;
+
+	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;
+
+	if (arg->flags & SHTTPD_SUSPEND)
+		c->loc.flags |= FLAG_SUSPEND;
+}
+
+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, ...)
+{
+	char		*buf = arg->out.buf + arg->out.num_bytes;
+	int		buflen = arg->out.len - arg->out.num_bytes, len = 0;
+	va_list		ap;
+
+	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 (_shttpd_strncasecmp(header_name, p, len) == 0)
+			return (p + len + 2);
+
+		p += strlen(p) + 1;
+	}
+
+	return (NULL);
+}
+
+const char *
+shttpd_get_boundary(struct shttpd_arg *arg, int *boundary_length) {
+  const char* content_type = shttpd_get_header(arg, "Content-Type");
+  if (content_type == NULL) {
+    return NULL;
+  }
+
+  const char* flag = "boundary=";
+  const char* begin = strstr(content_type, flag);
+  if (begin == NULL) {
+    *boundary_length = 0;
+    return NULL;
+  }
+  begin += strlen(flag);
+  int len = 0;
+  while (!(begin[len] == '\r' || begin[len] == '\0' || begin[len] == ';')) {
+    ++len;
+  }
+  *boundary_length = len;
+  return begin;
+}
+
+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 (_shttpd_known_http_methods[c->method].ptr);
+	} else if (strcmp(env_name, "REQUEST_URI") == 0) {
+		return (c->uri);
+	} else if (strcmp(env_name, "QUERY_STRING") == 0) {
+		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			= _shttpd_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] == '=' &&
+		    !_shttpd_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 (_shttpd_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 *
+_shttpd_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
+_shttpd_setup_embedded_stream(struct conn *c, union variant func, void *data)
+{
+	c->loc.chan.emb.state = NULL;
+	c->loc.chan.emb.func = func;
+	c->loc.chan.emb.data = data;
+	c->loc.io_class = &_shttpd_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);
+	}
+}
+
+void
+shttpd_wakeup(const void *priv)
+{
+	const struct conn	*conn = priv;
+	char			buf[sizeof(int) + sizeof(void *)];
+	int			cmd = CTL_WAKEUP;
+
+#if 0
+	conn->flags &= ~SHTTPD_SUSPEND;
+#endif
+	(void) memcpy(buf, &cmd, sizeof(cmd));
+	(void) memcpy(buf + sizeof(cmd), conn, sizeof(conn));
+
+	(void) send(conn->worker->ctl[1], buf, sizeof(buf), 0);
+}
+
+const struct io_class	_shttpd_io_embedded =  {
+	"embedded",
+	do_embedded,
+	(int (*)(struct stream *, const void *, size_t)) do_embedded,
+	close_embedded
+};

+ 157 - 0
jni/shttpd/io_file.c

@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int
+write_file(struct stream *stream, const void *buf, size_t len)
+{
+	struct stat	st;
+	struct stream	*rem = &stream->conn->rem;
+	int		n, fd = stream->chan.fd;
+
+	assert(fd != -1);
+	n = write(fd, buf, len);
+
+	DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
+
+	if (n <= 0 || (rem->io.total >= (big_int_t) rem->content_len)) {
+		(void) fstat(fd, &st);
+		stream->io.head = stream->headers_len =
+		    _shttpd_snprintf(stream->io.buf,
+		    stream->io.size, "HTTP/1.1 %d OK\r\n"
+		    "Content-Length: %lu\r\nConnection: close\r\n\r\n",
+		    stream->conn->status, st.st_size);
+		_shttpd_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
+_shttpd_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)
+		_shttpd_get_mime_type(c->ctx, c->uri,
+		    strlen(c->uri), &c->mime_type); 
+	cl = (big_int_t) stp->st_size;
+
+	/* If Range: header specified, act accordingly */
+	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) _shttpd_snprintf(range, sizeof(range),
+		    "Content-Range: bytes %lu-%lu/%lu\r\n",
+		    r1, r1 + cl - 1, (unsigned long) stp->st_size);
+		msg = "Partial Content";
+	}
+
+	/* Prepare Etag, Date, Last-Modified headers */
+	(void) strftime(date, sizeof(date),
+	    fmt, localtime(&_shttpd_current_time));
+	(void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
+	(void) _shttpd_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 = _shttpd_snprintf(c->loc.io.buf,
+	    c->loc.io.size,
+	    "HTTP/1.1 %d %s\r\n"
+	    "Date: %s\r\n"
+	    "Last-Modified: %s\r\n"
+	    "Etag: \"%s\"\r\n"
+	    "Content-Type: %.*s\r\n"
+	    "Content-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 = &_shttpd_io_file;
+	c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
+
+	if (c->method == METHOD_HEAD)
+		_shttpd_stop_stream(&c->loc);
+}
+
+const struct io_class	_shttpd_io_file =  {
+	"file",
+	read_file,
+	write_file,
+	close_file
+};

+ 39 - 0
jni/shttpd/io_socket.c

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

+ 488 - 0
jni/shttpd/io_ssi.c

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

+ 85 - 0
jni/shttpd/io_ssl.c

@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+#if !defined(NO_SSL)
+struct ssl_func	ssl_sw[] = {
+	{"SSL_free",			{0}},
+	{"SSL_accept",			{0}},
+	{"SSL_connect",			{0}},
+	{"SSL_read",			{0}},
+	{"SSL_write",			{0}},
+	{"SSL_get_error",		{0}},
+	{"SSL_set_fd",			{0}},
+	{"SSL_new",			{0}},
+	{"SSL_CTX_new",			{0}},
+	{"SSLv23_server_method",	{0}},
+	{"SSL_library_init",		{0}},
+	{"SSL_CTX_use_PrivateKey_file",	{0}},
+	{"SSL_CTX_use_certificate_file",{0}},
+	{NULL,				{0}}
+};
+
+void
+_shttpd_ssl_handshake(struct stream *stream)
+{
+	int	n;
+
+	if ((n = SSL_accept(stream->chan.ssl.ssl)) == 1) {
+		DBG(("handshake: SSL accepted"));
+		stream->flags |= FLAG_SSL_ACCEPTED;
+	} else {
+		n = SSL_get_error(stream->chan.ssl.ssl, n);
+		if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
+			stream->flags |= FLAG_CLOSED;
+		DBG(("SSL_accept error %d", n));
+	}
+}
+
+static int
+read_ssl(struct stream *stream, void *buf, size_t len)
+{
+	int	nread = -1;
+
+	assert(stream->chan.ssl.ssl != NULL);
+
+	if (!(stream->flags & FLAG_SSL_ACCEPTED))
+		_shttpd_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	_shttpd_io_ssl =  {
+	"ssl",
+	read_ssl,
+	write_ssl,
+	close_ssl
+};
+#endif /* !NO_SSL */

+ 59 - 0
jni/shttpd/llist.h

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

+ 93 - 0
jni/shttpd/log.c

@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+/*
+ * Log function
+ */
+void
+_shttpd_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(&_shttpd_current_time));
+
+	len = _shttpd_snprintf(buf, sizeof(buf),
+	    "[%s] [error] [client %s] \"%s\" ",
+	    date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
+	    c && c->request ? c->request : "-");
+
+	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
+_shttpd_log_access(FILE *fp, const struct conn *c)
+{
+	static const struct vec	dash = {"-", 1};
+
+	const struct vec	*user = &c->ch.user.v_vec;
+	const struct vec	*referer = &c->ch.referer.v_vec;
+	const struct vec	*user_agent = &c->ch.useragent.v_vec;
+	char			date[64], buf[URI_MAX], *q1 = "\"", *q2 = "\"";
+
+	if (user->len == 0)
+		user = &dash;
+
+	if (referer->len == 0) {
+		referer = &dash;
+		q1 = "";
+	}
+
+	if (user_agent->len == 0) {
+		user_agent = &dash;
+		q2 = "";
+	}
+
+	(void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
+			localtime(&c->birth_time));
+
+	(void) _shttpd_snprintf(buf, sizeof(buf),
+	    "%s - %.*s [%s %+05d] \"%s\" %d %lu %s%.*s%s %s%.*s%s",
+	    inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr,
+	    date, _shttpd_tz_offset, c->request ? c->request : "-",
+	    c->status, (unsigned long) c->loc.io.total,
+	    q1, referer->len, referer->ptr, q1,
+	    q2, user_agent->len, user_agent->ptr, q2);
+
+	if (fp != NULL) {
+		(void) fprintf(fp, "%s\n", buf);
+		(void) fflush(fp);
+	}
+}

+ 249 - 0
jni/shttpd/md5.c

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

+ 24 - 0
jni/shttpd/md5.h

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

+ 641 - 0
jni/shttpd/multipart_parser.c

@@ -0,0 +1,641 @@
+/**
+ *  Copyright (c) 2019 Trevor Herselman. All rights reserved.
+ *
+ *  MIT License
+ *
+ *  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.
+ */
+
+#include "multipart_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#include <stdio.h>
+
+#include "utils/Log.h"
+
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define SET_ERRNO(e)                                                 \
+do {                                                                 \
+  parser->multipart_errno = (e);                                     \
+} while(0)
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
+#ifndef UNREACHABLE
+# ifdef _MSC_VER
+#  define UNREACHABLE __assume(0)
+# else	/* GCC, Clang & Intel C++ */
+#  define UNREACHABLE __builtin_unreachable()
+# endif
+#endif
+
+#ifndef FALLTHROUGH
+# if defined(__GNUC__) || defined(__clang__)
+#  define FALLTHROUGH  __attribute__ ((fallthrough))
+# else
+#  define FALLTHROUGH ((void)0)
+# endif
+#endif
+
+
+enum state
+  { s_start
+  , s_start_dash
+  , s_boundary
+  , s_boundary_cr
+  , s_boundary_almost_done
+  , s_header_field_start
+  , s_header_field
+  , s_header_value_discard_ws
+  , s_header_value
+  , s_header_value_lws
+  , s_header_almost_done
+
+  , s_headers_almost_done
+  , s_headers_done
+
+  , s_body_part_start
+  , s_body_part
+
+  , s_body_part_boundary_cr
+  , s_body_part_boundary_cr_lf
+  , s_body_part_boundary_cr_lf_dash
+  , s_body_part_boundary_cr_lf_dash_dash
+
+  , s_end
+  };
+
+/* Macros for character classes */
+#define CR                  '\r'
+#define LF                  '\n'
+#define LOWER(c)            (unsigned char)(c | 0x20)
+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+
+
+void multipart_parser_init(multipart_parser *parser)
+{
+  parser->state = s_start;
+}
+
+void multipart_parser_settings_init(multipart_parser_settings *settings)
+{
+  memset(settings, 0, sizeof(*settings));
+}
+
+int multipart_parser_execute(multipart_parser *parser,
+                             const multipart_parser_settings *settings,
+                             const char *data,
+                             size_t len)
+{
+  const char* buf_end = &data[len];
+  const char* p = data;
+
+  const char* body_start = NULL;
+
+  for (; p < buf_end; ++p)
+  {
+    const char ch = *p;
+
+    switch (parser->state)
+    {
+      case s_start:
+        if (LIKELY(ch == '-')) {
+          parser->state = s_start_dash;
+        }
+        continue;
+
+      case s_start_dash:
+        if (LIKELY(ch == '-')) {
+          parser->nread = 0;
+          parser->state = s_boundary;
+          continue;
+        }
+        return -1;
+
+      case s_boundary:
+        if (LIKELY(parser->nread < parser->boundary_len)) {
+          if (LIKELY(ch == parser->boundary[parser->nread++])) {
+            continue;
+          }
+        } else {
+          if (LIKELY(ch == '\r')) {
+            parser->state = s_boundary_cr;
+            continue;
+          } else if (ch == '-') {
+            parser->state = s_boundary_almost_done;
+            continue;
+          }
+        }
+        return -1;
+
+      case s_boundary_cr:
+        if (LIKELY(ch == '\n')) {
+          if (LIKELY(settings->on_boundary_begin(parser) == 0)) {
+            parser->state = s_header_field_start;
+            continue;
+          }
+        }
+        return -1;
+
+      case s_boundary_almost_done:
+        if (LIKELY(ch == '-')) {
+          parser->state = s_end;
+          return settings->on_body_parts_complete(parser);
+        }
+        return -1;
+
+      case s_headers_almost_done:
+        if (ch == '\r') {
+          parser->state = s_headers_done;
+          continue;
+        }
+        FALLTHROUGH;
+
+      case s_header_field_start:
+        if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
+          parser->nread = 1;
+          memset(parser->header_field, 0, sizeof(parser->header_field));
+          parser->header_field[0] = ch;
+          parser->state = s_header_field;
+          continue;
+        }
+        return -1;
+
+      case s_header_field:
+        if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '-') {
+          if (parser->nread < sizeof(parser->header_field)) {
+            parser->header_field[parser->nread] = ch;
+          }
+          parser->nread++;
+          continue;
+        } else if (ch == ':') {
+          if (settings->on_header_field(
+                parser,
+                /*p - parser->nread*/ parser->header_field,
+                /*parser->nread*/ strlen(parser->header_field)) == 0) {
+            parser->state = s_header_value_discard_ws;
+            continue;
+          }
+        }
+        return -1;
+
+      case s_header_value_discard_ws:
+        if (ch > ' ') {
+          parser->nread = 1;
+          memset(parser->header_value, 0, sizeof(parser->header_value));
+          parser->header_value[0] = ch;
+          parser->state = s_header_value;
+          continue;
+        } if (ch == ' ') {
+          continue;
+        }
+        return -1;
+
+      case s_header_value:
+        if (ch != '\r') {
+          if (parser->nread < sizeof(parser->header_value)) {
+            parser->header_value[parser->nread] = ch;
+          }
+          parser->nread++;
+          continue;
+        }
+        if (settings->on_header_value(
+              parser,
+              /*p - parser->nread*/ parser->header_value,
+              /*parser->nread*/ strlen(parser->header_value)) == 0) {
+          parser->state = s_header_almost_done;
+          continue;
+        }
+        return -1;
+
+      case s_header_almost_done:
+        if (ch == '\n') {
+          parser->state = s_headers_almost_done;
+          continue;
+        }
+
+      case s_header_value_lws:
+        return -1;
+
+      case s_headers_done:
+        if (ch == '\n') {
+          if (LIKELY( settings->on_headers_complete(parser) == 0)) {
+            parser->state = s_body_part_start;
+            continue;
+          }
+        }
+        return -1;
+
+      case s_body_part_start:
+        body_start = p;
+        parser->state = s_body_part;
+        FALLTHROUGH;
+
+      case s_body_part:
+        if (LIKELY(ch != '\r')) {
+          continue;
+        }
+
+        if (body_start == NULL) {
+          settings->on_body(parser, data, p - data);
+        } else {
+          settings->on_body(parser, body_start, p - body_start);
+        }
+        parser->state = s_body_part_boundary_cr;
+        continue;
+
+      case s_body_part_boundary_cr:
+        if (ch == '\n') {
+          parser->state = s_body_part_boundary_cr_lf;
+          continue;
+        }
+        settings->on_body(parser, "\r", 1);
+        if (ch == '\r') {
+          continue;
+        }
+        body_start = p;
+        parser->state = s_body_part;
+        continue;
+      case s_body_part_boundary_cr_lf:
+        if (ch == '-') {
+          parser->state = s_body_part_boundary_cr_lf_dash;
+          continue;
+        }
+
+        settings->on_body(parser, "\r\n", 2);
+
+        if (ch == '\r') {
+          parser->state = s_body_part_boundary_cr;
+          continue;
+        }
+
+        body_start = p;
+        parser->state = s_body_part;
+        continue;
+
+      case s_body_part_boundary_cr_lf_dash:
+        if (ch == '-') {
+          parser->nread = 0;
+          parser->state = s_body_part_boundary_cr_lf_dash_dash;
+          continue;
+        }
+
+        settings->on_body(parser, "\r\n-", 3);
+        if (ch == '\r') {
+          parser->state = s_body_part_boundary_cr;
+          continue;
+        }
+        body_start = p;
+        parser->state = s_body_part;
+        continue;
+
+      case s_body_part_boundary_cr_lf_dash_dash:
+        if (LIKELY(parser->nread < parser->boundary_len)) {
+          if (LIKELY(ch == parser->boundary[parser->nread++])) {
+            continue;
+          }
+
+          settings->on_body(parser, "\r\n--", 4);
+          if (parser->nread > 0) {
+            settings->on_body(parser, parser->boundary, parser->nread - 1);
+          }
+          if (ch == '\r') {
+            parser->state = s_body_part_boundary_cr;
+            continue;
+          }
+
+          body_start = p;
+          parser->state = s_body_part;
+          continue;
+        } else {
+            if (LIKELY(ch == '\r')) {
+              parser->state = s_boundary_cr;
+              continue;
+            }
+
+            if (ch == '-') {
+              parser->state = s_boundary_almost_done;
+              continue;
+            }
+        }
+        return -1;
+      case s_end:
+        return 0;
+      default:
+        UNREACHABLE;
+    }
+    UNREACHABLE;
+  }
+
+  switch (parser->state) {
+    case s_body_part_start:
+      if ((body_start != NULL) ) {
+        settings->on_body(parser, body_start, buf_end - body_start);
+      }
+      break;
+    case s_body_part:
+      if (body_start == NULL ) {
+        settings->on_body(parser, data, len);
+      } else {
+        settings->on_body(parser, body_start, buf_end - body_start);
+      }
+      break;
+    default:
+      break;
+  }
+
+  return 0;
+}
+
+const char* multipart_get_name(const char* str, size_t len,
+                               size_t* value_len)
+{
+  const char* str_end = &str[len];
+  const char* p = str;
+
+  const char* value_start;
+
+  typedef enum
+  { s_seek
+  , s_N
+  , s_NA
+  , s_NAM
+  , s_NAME
+  , s_NAME_EQ
+  , s_NAME_EQ_QUOT
+  , s_value_start
+  , s_value
+  , s_value_end
+  } e_state;
+
+  for (e_state state = s_seek; p < str_end; ++p)
+  {
+    const char ch = *p;
+
+    switch (state)
+    {
+      case s_seek:
+      _reset:
+        if (UNLIKELY(LOWER(ch) == 'n')) {
+          state = s_N;
+        }
+        continue;
+
+      case s_N:
+        if (LIKELY(ch == 'a' || ch == 'A')) {
+          state = s_NA;
+        } else {
+          state = s_seek;
+          goto _reset;
+        }
+        continue;
+
+      case s_NA:
+        if (LIKELY(ch == 'm' || ch == 'M')) {
+          state = s_NAM;
+        } else {
+          state = s_seek;
+          goto _reset;
+        }
+        continue;
+
+      case s_NAM:
+        if (LIKELY(ch == 'e' || ch == 'E')) {
+          state = s_NAME;
+        } else {
+          state = s_seek;
+          goto _reset;
+        }
+        continue;
+
+      case s_NAME:
+        if (LIKELY(ch == '=')) {
+          state = s_NAME_EQ;
+        } else {
+          if (ch == ' ') {                                /*  Skip whitespace */
+            continue;
+          }
+
+          state = s_seek;
+          goto _reset;
+        }
+        continue;
+
+      case s_NAME_EQ:
+        if (LIKELY(ch == '"')) {
+          state = s_value_start;
+        } else {
+          if (ch == ' ') {                                /*  Skip whitespace */
+            continue;
+          }
+
+          state = s_seek;
+          goto _reset;
+        }
+        continue;
+
+      case s_value_start:
+        value_start = p;
+
+        if (LIKELY(ch != '"')) {
+          state = s_value;
+        } else {
+          *value_len = 0;                          /* detected an empty value */
+          return value_start;
+        }
+        continue;
+
+      case s_value:
+        if (LIKELY(ch != '"')) {
+          continue;
+        } else {
+          *value_len = p - value_start;
+          return value_start;
+        }
+
+      default:
+        UNREACHABLE;
+    }
+  }
+
+  return NULL;
+}
+
+const char* multipart_get_filename(const char* str, size_t len,
+                                   size_t* value_len)
+{
+  const char* str_end = &str[len];
+  const char* p = str;
+
+  const char* value_start;
+
+  typedef enum
+    { s_F
+    , s_FI
+    , s_FIL
+    , s_FILE
+    , s_FILEN
+    , s_FILENA
+    , s_FILENAM
+    , s_FILENAME
+    , s_FILENAME_EQ
+    , s_FILENAME_EQ_QUOT
+    , s_value_start
+    , s_value
+  } e_state;
+
+  for (e_state state = s_F; p < str_end; ++p)
+  {
+    const char ch = *p;
+
+    switch (state)
+    {
+      case s_F:
+      _reset:
+        if (UNLIKELY(LOWER(ch) == 'f')) {
+          state = s_FI;
+        }
+        continue;
+
+      case s_FI:
+        if (LIKELY(ch == 'i') || ch == 'I') {
+          state = s_FIL;
+        } else {
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FIL:
+        if (LIKELY(ch == 'l') || ch == 'L') {
+          state = s_FILE;
+        } else {
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FILE:
+        if (LIKELY(ch == 'e') || ch == 'E') {
+          state = s_FILEN;
+        } else {
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FILEN:
+        if (LIKELY(ch == 'n') || ch == 'N') {
+          state = s_FILENA;
+        } else {
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FILENA:
+        if (LIKELY(ch == 'a') || ch == 'A') {
+          state = s_FILENAM;
+        } else {
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FILENAM:
+        if (LIKELY(ch == 'm') || ch == 'M') {
+          state = s_FILENAME;
+        } else {
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FILENAME:
+        if (LIKELY(ch == 'e') || ch == 'E') {
+          state = s_FILENAME_EQ;
+        } else {
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FILENAME_EQ:
+        if (LIKELY(ch == '=')) {
+          state = s_FILENAME_EQ_QUOT;
+        } else {
+          if (ch == ' ') {                                /*  Skip whitespace */
+            continue;
+          }
+
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_FILENAME_EQ_QUOT:
+        if (LIKELY(ch == '"')) {
+          state = s_value_start;
+        } else {
+          if (ch == ' ') {                                /*  Skip whitespace */
+            continue;
+          }
+
+          state = s_F;
+          goto _reset;
+        }
+        continue;
+
+      case s_value_start:
+        value_start = p;
+        state = s_value;
+        FALLTHROUGH;
+
+      case s_value:
+        if (LIKELY(ch != '"')) {
+          continue;
+        } else {
+          *value_len = p - value_start;
+          return value_start;
+        }
+
+      default:
+        UNREACHABLE;
+    }
+  }
+
+  return NULL;
+}

+ 120 - 0
jni/shttpd/multipart_parser.h

@@ -0,0 +1,120 @@
+/**
+ *  Copyright (c) 2019 Trevor Herselman. All rights reserved.
+ *
+ *  MIT License
+ *
+ *  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.
+ */
+
+#ifndef multipart_parser_h
+#define multipart_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#if defined(_WIN32) && !defined(__MINGW32__) && \
+  (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
+#include <BaseTsd.h>
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+typedef struct multipart_parser multipart_parser;
+typedef struct multipart_parser_settings multipart_parser_settings;
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ */
+typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length);
+typedef int (*multipart_cb) (multipart_parser*);
+
+struct multipart_parser_settings {
+  multipart_cb      on_boundary_begin;
+  multipart_data_cb on_header_field;
+  multipart_data_cb on_header_value;
+  multipart_cb      on_headers_complete;
+  multipart_data_cb on_body;
+  multipart_cb      on_body_parts_complete;
+};
+
+struct multipart_parser {
+  /** PRIVATE **/
+  unsigned char state;                       /* enum state from http_parser.c */
+
+  uint64_t nread;                        /* # bytes read in various scenarios */
+
+  /** READ-ONLY **/
+  unsigned char multipart_errno;
+
+  /** PUBLIC **/
+  void *data; /* A pointer to get hook to the "connection" or "socket" object */
+
+  const char* boundary;   /* set this to a boundary string taken from headers */
+  size_t boundary_len;
+
+  char header_field[64];
+  char header_value[128];
+};
+
+void multipart_parser_init(multipart_parser *parser);
+
+/* Initialize multipart_parser_settings members to 0
+ */
+void multipart_parser_settings_init(multipart_parser_settings *settings);
+
+/* `return -1` on error
+ * Sets `parser->multipart_errno` on error.
+ */
+int multipart_parser_execute(multipart_parser *parser,
+                             const multipart_parser_settings *settings,
+                             const char *data,
+                             size_t len);
+
+/* Helper method to get the boundary string from Content-Type header */
+//const char* get_boundary(const char* str, size_t str_len, size_t* boundary_len);
+
+/**
+ */
+const char* multipart_get_name(const char* str, size_t len, size_t* value_len);
+
+/**
+ * Helper function to get the `filename` value from a header string value
+ * eg. `form-data; name="file1"; filename="我a私aaaa\"xxx\"abc"`
+ * returns: `我a私aaaa"xxx"abc`
+ * Extracts and decodes the `filename` string into buffer,
+ *   performs URL decoding, and slash string decoding!
+ * Firefox will encode a double quote `"` character into \"
+ * Chrome will use %22
+ */
+const char* multipart_get_filename(const char* str, size_t len,
+                                   size_t* value_len);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 94 - 0
jni/shttpd/multipart_reader.c

@@ -0,0 +1,94 @@
+#include "multipart_reader.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+int multipart_parser_on_boundary_begin(multipart_parser *mp) {
+//  LOGD("on_boundary_begin");
+  return 0;
+}
+int multipart_parser_on_header_field(multipart_parser* parser, const char *at, size_t length) {
+
+  return 0;
+}
+
+
+#define multipart_parser_strcpy(dest, source, source_len) \
+  memcpy(dest, source, (source_len < sizeof(dest)) ? source_len : (sizeof(dest) - 1));\
+  dest[(source_len < sizeof(dest)) ? source_len : (sizeof(dest) - 1)] = 0;
+
+
+int multipart_parser_on_header_value(multipart_parser* parser, const char *at, size_t length) {
+  if (strcmp("Content-Disposition", parser->header_field) == 0) {
+    multipart_reader* reader = (multipart_reader*)parser->data;
+    int value_len = 0;
+    int len = 0;
+    const char* name = multipart_get_name(
+         parser->header_value, sizeof(parser->header_value), &value_len);
+    if (name != NULL) {
+      multipart_parser_strcpy(reader->form_data_name, name, value_len);
+    }
+    const char* filename = multipart_get_filename(
+         parser->header_value, sizeof(parser->header_value), &value_len);
+    if (filename != NULL) {
+      multipart_parser_strcpy(reader->form_data_filename, filename, value_len);
+    }
+  }
+  return 0;
+}
+int multipart_parser_on_headers_complete(multipart_parser *mp) {
+  return 0;
+}
+
+int multipart_parser_on_body(multipart_parser* parser, const char *at, size_t length) {
+  const multipart_reader* reader = (multipart_reader*)parser->data;
+  if (!reader->on_receive) {
+    return 0;
+  }
+  reader->on_receive(reader, at, length);
+  return 0;
+}
+int multipart_parser_on_body_parts_complete(multipart_parser *parser) {
+  return 0;
+}
+
+multipart_reader* multipart_reader_create(const char* boundary, multipart_reader_callback on_receive, void* user_data) {
+  multipart_reader* reader = (multipart_reader*)calloc(1, sizeof(multipart_reader));
+  reader->on_receive = on_receive;
+  reader->user_data = user_data;
+
+
+  multipart_parser_init(&reader->parser);
+  reader->parser.data = reader;
+  strncpy(reader->boundary, boundary, sizeof(reader->boundary));
+  multipart_parser_strcpy(reader->boundary, boundary, strlen(boundary));
+  reader->parser.boundary = reader->boundary;
+  reader->parser.boundary_len = strlen(reader->boundary);
+
+  multipart_parser_settings_init(&reader->parser_settings);
+  reader->parser_settings.on_boundary_begin = multipart_parser_on_boundary_begin;
+  reader->parser_settings.on_header_field = multipart_parser_on_header_field;
+  reader->parser_settings.on_header_value = multipart_parser_on_header_value;
+  reader->parser_settings.on_headers_complete = multipart_parser_on_headers_complete;
+  reader->parser_settings.on_body = multipart_parser_on_body;
+  reader->parser_settings.on_body_parts_complete = multipart_parser_on_body_parts_complete;
+
+  return reader;
+}
+
+int multipart_reader_execute(multipart_reader *reader, const void *data, uint64_t length) {
+  if (reader->parser.multipart_errno != 0) {
+    return reader->parser.multipart_errno;
+  }
+  int r = multipart_parser_execute(&reader->parser, &reader->parser_settings, data, length);
+  if (r != 0) {
+    reader->parser.multipart_errno = r;
+  }
+  return r;
+}
+void multipart_reader_destroy(multipart_reader *receiver) {
+  if (!receiver) {
+    return;
+  }
+  free(receiver);
+}

+ 32 - 0
jni/shttpd/multipart_reader.h

@@ -0,0 +1,32 @@
+#pragma once
+
+#include <stdint.h>
+#include "multipart_parser.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct multipart_reader;
+typedef struct multipart_reader multipart_reader;
+
+typedef void (*multipart_reader_callback)(const multipart_reader* reader, const char* data, uint64_t data_length);
+
+struct multipart_reader {
+  multipart_reader_callback on_receive;
+  void* user_data;
+  char form_data_name[64];
+  char form_data_filename[128];
+
+  char boundary[64];
+  multipart_parser parser;
+  multipart_parser_settings parser_settings;
+};
+
+multipart_reader* multipart_reader_create(const char* boundary, multipart_reader_callback on_receive, void* user_data);
+int multipart_reader_execute(multipart_reader *reader, const void *data, uint64_t length);
+void multipart_reader_destroy(multipart_reader *reader);
+
+#ifdef __cplusplus
+}
+#endif

File diff suppressed because it is too large
+ 1903 - 0
jni/shttpd/shttpd.c


+ 112 - 0
jni/shttpd/shttpd.h

@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2004-2008 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ *
+ * $Id: shttpd.h,v 1.18 2008/08/23 08:34:50 drozd Exp $
+ */
+
+#ifndef SHTTPD_HEADER_INCLUDED
+#define	SHTTPD_HEADER_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct ubuf {
+	char		*buf;		/* Buffer pointer		*/
+	int		len;		/* Size of a buffer		*/
+	int		num_bytes;	/* Bytes processed by callback	*/
+};
+
+/*
+ * This structure is passed to the user callback function
+ */
+struct shttpd_arg {
+	void		*priv;		/* Private! Do not touch!	*/
+	void		*state;		/* User state			*/
+	void		*user_data;	/* Data from register_uri()	*/
+	struct ubuf	in;		/* Input is here, POST data	*/
+	struct ubuf	out;		/* Output goes here		*/
+
+	unsigned int	flags;
+#define	SHTTPD_END_OF_OUTPUT	1	/* No more data do send		*/
+#define	SHTTPD_CONNECTION_ERROR	2	/* Server closed the connection	*/
+#define	SHTTPD_MORE_POST_DATA	4	/* arg->in has incomplete data	*/
+#define	SHTTPD_POST_BUFFER_FULL	8	/* arg->in has max data		*/
+#define	SHTTPD_SSI_EVAL_TRUE	16	/* SSI eval callback must set it*/
+#define	SHTTPD_SUSPEND		32	/* User wants to suspend output	*/
+};
+
+/*
+ * User callback function. Called when certain registered URLs have been
+ * requested. These are the requirements to the callback function:
+ *
+ * 1. It must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
+ *	and record how many bytes are copied, into 'out.num_bytes'
+ * 2. It must not call any blocking functions
+ * 3. It must set SHTTPD_END_OF_OUTPUT flag when there is no more data to send
+ * 4. For POST requests, it must process the incoming data (in.buf) of length
+ *	'in.len', and set 'in.num_bytes', which is how many bytes of POST
+ *	data was processed and can be discarded by SHTTPD.
+ * 5. If callback allocates arg->state, to keep state, it must deallocate it
+ *    at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
+ * 6. If callback function wants to suspend until some event, it must store
+ *	arg->priv pointer elsewhere, set SHTTPD_SUSPEND flag and return. When
+ *	the event happens, user code should call shttpd_wakeup(priv).
+ *	It is safe to call shttpd_wakeup() from any thread. User code must
+ *	not call shttpd_wakeup once the connection is closed.
+ */
+typedef void (*shttpd_callback_t)(struct shttpd_arg *);
+
+/*
+ * shttpd_init		Initialize shttpd context
+ * shttpd_fini		Dealocate the context, close all connections
+ * shttpd_set_option	Set new value for option
+ * shttpd_register_uri	Setup the callback function for specified URL
+ * shttpd_poll		Do connections processing
+ * shttpd_version	return string with SHTTPD version
+ * shttpd_get_var	Fetch POST/GET variable value by name. Return value len
+ * shttpd_get_header	return value of the specified HTTP header
+ * shttpd_get_env	return values for the following	pseudo-variables:
+ 			"REQUEST_METHOD", "REQUEST_URI",
+ *			"REMOTE_USER" and "REMOTE_ADDR"
+ * shttpd_printf	helper function to output data
+ * shttpd_handle_error	register custom HTTP error handler
+ * shttpd_wakeup	clear SHTTPD_SUSPEND state for the connection
+ */
+
+struct shttpd_ctx;
+
+struct shttpd_ctx *shttpd_init(int argc, char *argv[]);
+int shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
+void shttpd_fini(struct shttpd_ctx *);
+void shttpd_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_boundary(struct shttpd_arg *, int *boundary_length);
+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);
+void shttpd_wakeup(const void *priv);
+int shttpd_join(struct shttpd_ctx *, fd_set *, fd_set *, int *max_fd);
+int  shttpd_socketpair(int sp[2]);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SHTTPD_HEADER_INCLUDED */

+ 52 - 0
jni/shttpd/ssl.h

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

+ 72 - 0
jni/shttpd/standalone.c

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include "defs.h"
+
+static int	exit_flag;	/* Program termination flag	*/
+
+static void
+signal_handler(int sig_num)
+{
+	switch (sig_num) {
+#ifndef _WIN32
+	case SIGCHLD:
+		while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
+		break;
+#endif /* !_WIN32 */
+	default:
+		exit_flag = sig_num;
+		break;
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct shttpd_ctx	*ctx;
+
+#if !defined(NO_AUTH)
+	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
+		if (argc != 6)
+			_shttpd_usage(argv[0]);
+		exit(_shttpd_edit_passwords(argv[2],argv[3],argv[4],argv[5]));
+	}
+#endif /* NO_AUTH */
+
+	if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
+		_shttpd_usage(argv[0]);
+
+#if defined(_WIN32)
+	try_to_run_as_nt_service();
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+	(void) signal(SIGCHLD, signal_handler);
+	(void) signal(SIGPIPE, SIG_IGN);
+#endif /* _WIN32 */
+
+	(void) signal(SIGTERM, signal_handler);
+	(void) signal(SIGINT, signal_handler);
+
+	if ((ctx = shttpd_init(argc, argv)) == NULL)
+		_shttpd_elog(E_FATAL, NULL, "%s",
+		    "Cannot initialize SHTTPD context");
+
+	_shttpd_elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
+	    VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
+
+	while (exit_flag == 0)
+		shttpd_poll(ctx, 10 * 1000);
+
+	_shttpd_elog(E_LOG, NULL, "Exit on signal %d", exit_flag);
+	shttpd_fini(ctx);
+
+	return (EXIT_SUCCESS);
+}

+ 40 - 0
jni/shttpd/std_includes.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ * All rights reserved
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#ifndef STD_HEADERS_INCLUDED
+#define	STD_HEADERS_INCLUDED
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* _WIN32_WCE */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#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 */

+ 95 - 0
jni/shttpd/string.c

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

BIN
libs/armeabi/libzkgui.so


BIN
obj/activity/DeviceUpdateActivity.o


BIN
obj/activity/callActivity.o


BIN
obj/activity/mainActivity.o


BIN
obj/activity/sipTestActivity.o


BIN
obj/activity/startActivity.o


BIN
obj/activity/statusbar.o


BIN
obj/activity/ui3Activity.o


BIN
obj/activity/warnActivity.o


BIN
obj/core/update_assistant.o


BIN
obj/net/tcp_client.o


BIN
obj/server/http_server.o


BIN
obj/service/BusinessConfig.o


BIN
obj/service/time.o


BIN
obj/shttpd/auth.o


BIN
obj/shttpd/cgi.o


BIN
obj/shttpd/compat_unix.o


BIN
obj/shttpd/io_cgi.o


BIN
obj/shttpd/io_dir.o


BIN
obj/shttpd/io_emb.o


BIN
obj/shttpd/io_file.o


BIN
obj/shttpd/io_socket.o


BIN
obj/shttpd/io_ssi.o


BIN
obj/shttpd/io_ssl.o


BIN
obj/shttpd/log.o


BIN
obj/shttpd/md5.o


BIN
obj/shttpd/multipart_parser.o


BIN
obj/shttpd/multipart_reader.o


BIN
obj/shttpd/shttpd.o


BIN
obj/shttpd/standalone.o


BIN
obj/shttpd/string.o


+ 155 - 0
resources/html/index.html

@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"> 
+<title>WDKL NET BRIDGE</title> 
+</head>
+<style>
+  fieldset{
+    margin-top: 30px;
+    border-radius: 15px;
+    border: 1px solid #ccc;
+  }
+  label{
+    display: inline-block;
+    width: 100px;
+    text-align: right;
+    margin-right: 30px;
+  }
+  input{
+    margin-top: 10px;
+  }
+  input[type=submit] {
+    margin-left: 10%;
+  width: 200px;
+  background-color: #4CAF50;
+  color: white;
+  padding: 14px 20px;
+  margin: 8px 0;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+input[type=submit]:hover {
+  background-color: #45a049;
+}
+</style>
+<body>
+  <div>
+    <h1>WDKL NET BRIDGE</h1>
+    <p>mac : %s</p>
+  </div>
+
+  <form action="/setting" method="POST" onsubmit="return validate();">
+    <fieldset>
+      <legend>DEVICE</legend>
+      <label>DHCP?</label>
+      YES<input onclick="ipSetting(false);" type="radio" name="dhcp" value="1" style="margin-right: 1rem;" checked>
+      NO<input onclick="ipSetting(true);" type="radio" name="dhcp" value="0">
+      <br>
+      <div id="ip_panel" style="display: none;">
+        <label>IP</label>
+        <input type="text" name="ip" id="ip" value="%s">
+        <br>
+        <label>NETMASK</label>
+        <input type="text" name="netmask" id="netmask" value="%s">
+        <br>
+        <label>GATEWAY</label>
+        <input type="text" name="gateway" id="gateway" value="%s">
+        <br>
+        <label>DNS1</label>
+        <input type="text" name="dns1" id="dns1" value="%s">
+        <br>
+        <label>DNS2</label>
+        <input type="text" name="dns2" id="dns2" value="%s">
+      </div>
+    </fieldset>
+
+    <fieldset>
+      <legend>SERVER</legend>
+      <label>SERVER IP</label>
+      <input type="text" name="server_ip" id="server_ip" value="%s">
+    </fieldset>
+    
+    <input type="submit" value="submit">
+  </form>
+  <script>
+    function ipSetting(show){
+      console.log("xxx" + show);
+      document.getElementById("ip_panel").style.display = show?"block":"none";
+    }
+    function validate() {
+      console.log(document.getElementById("ip_panel").style.display);
+      var regexIP = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]).){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
+      var regexNetmask = /^(254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)$/;
+
+      var ip = document.getElementById("ip").value;
+      var netmask = document.getElementById("netmask").value;
+      var gateway = document.getElementById("gateway").value;
+      var dns1 = document.getElementById("dns1").value;
+      var dns2 = document.getElementById("dns2").value;
+      var server_ip = document.getElementById("server_ip").value;
+
+      if (document.getElementById("ip_panel").style.display == "none"){
+        if (server_ip == ""){
+          alert("server ip must input");
+          document.getElementById("server_ip").focus();
+          return false;
+        } else if (!regexIP.test(server_ip)){
+          alert("server ip wrong input");
+          document.getElementById("server_ip").focus();
+          return false;
+        }
+        return true;
+      } else {
+        if (ip == ""){
+          alert("ip must input");
+          document.getElementById("ip").focus();
+          return false;
+        } else if (netmask == ""){
+          alert("netmask must input");
+          document.getElementById("netmask").focus();
+          return false;
+        } else if (gateway == ""){
+          alert("gateway must input");
+          document.getElementById("gateway").focus();
+          return false;
+        } else if (server_ip == ""){
+          alert("server_ip must input");
+          document.getElementById("server_ip").focus();
+          return false;
+        }
+
+        if (!regexIP.test(ip)){
+          alert("ip wrong input");
+          document.getElementById("ip").focus();
+          return false;
+        } else if (!regexNetmask.test(netmask)){
+          alert("netmask wrong input");
+          document.getElementById("netmask").focus();
+          return false;
+        } else if (!regexIP.test(gateway)){
+          alert("gateway wrong input");
+          document.getElementById("gateway").focus();
+          return false;
+        } else if (!regexIP.test(server_ip)){
+          alert("server_ip wrong input");
+          document.getElementById("server_ip").focus();
+          return false;
+        } else if (dns1!="" && !regexIP.test(dns1)){
+          alert("dns1 wrong input");
+          document.getElementById("dns1").focus();
+          return false;
+        } else if (dns2!="" && !regexIP.test(dns2)){
+          alert("dns2 wrong input");
+          document.getElementById("dns2").focus();
+          return false;
+        }
+
+        return true;
+      }
+    }
+  </script>
+</body>
+</html>

+ 11 - 0
resources/html/setting_result.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"> 
+<title>WDKL NET BRIDGE</title> 
+</head>
+<body>
+  <h3>%s</h3>
+  <a href="/">back to main</a>  
+</body>
+</html>

BIN
ui/DeviceUpdate.ftu