From 4216cf2ac1ec67df378918d96a087cce125e8cf5 Mon Sep 17 00:00:00 2001
From: Alexander Alashkin <alexander.alashkin@cesanta.com>
Date: Mon, 17 Oct 2016 12:03:59 +0300
Subject: [PATCH] Enable FS for WinCE

PUBLISHED_FROM=ec3ed105e29251bdd9b8ad08e8e56c0873ced8ae
---
 mongoose.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++-------
 mongoose.h | 53 ++++++++++++++++++++---------
 2 files changed, 123 insertions(+), 27 deletions(-)

diff --git a/mongoose.c b/mongoose.c
index 4f0913588..b854081f5 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -733,8 +733,23 @@ double cs_time(void) {
   if (gettimeofday(&tv, NULL /* tz */) != 0) return 0;
   now = (double) tv.tv_sec + (((double) tv.tv_usec) / 1000000.0);
 #else
-  now = GetTickCount() / 1000.0;
-#endif
+  SYSTEMTIME sysnow;
+  FILETIME ftime;
+  GetLocalTime(&sysnow);
+  SystemTimeToFileTime(&sysnow, &ftime);
+  /*
+   * 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64
+   * This should not cause a problems in this (21th) century
+   * 2. Windows FILETIME is a number of 100-nanosecond intervals since January
+   * 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC,
+   * thus, we need to convert to seconds and adjust amount (subtract 11644473600
+   * seconds)
+   */
+  now = (double) (((int64_t) ftime.dwLowDateTime +
+                   ((int64_t) ftime.dwHighDateTime << 32)) /
+                  10000000.0) -
+        11644473600;
+#endif /* _WIN32 */
   return now;
 }
 #ifdef MG_MODULE_LINES
@@ -5064,9 +5079,15 @@ static void mg_http_construct_etag(char *buf, size_t buf_len,
   snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", (unsigned long) st->st_mtime,
            (int64_t) st->st_size);
 }
+
+#ifndef WINCE
 static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) {
   strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
 }
+#else
+/* Look wince_lib.c for WindowsCE implementation */
+static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t);
+#endif
 
 static int mg_http_parse_range_header(const struct mg_str *header, int64_t *a,
                                       int64_t *b) {
@@ -5105,7 +5126,7 @@ void mg_http_serve_file(struct mg_connection *nc, struct http_message *hm,
     mg_http_send_error(nc, code, "Open failed");
   } else {
     char etag[50], current_time[50], last_modified[50], range[70];
-    time_t t = time(NULL);
+    time_t t = (time_t) mg_time();
     int64_t r1 = 0, r2 = 0, cl = st.st_size;
     struct mg_str *range_hdr = mg_get_http_header(hm, "Range");
     int n, status_code = 200;
@@ -5398,7 +5419,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
   static const size_t one = 1;
   char ha1[33], resp[33], cnonce[40];
 
-  snprintf(cnonce, sizeof(cnonce), "%x", (unsigned int) time(NULL));
+  snprintf(cnonce, sizeof(cnonce), "%x", (unsigned int) mg_time());
   cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
          (size_t) strlen(auth_domain), colon, one, passwd,
          (size_t) strlen(passwd), NULL);
@@ -5419,7 +5440,7 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
  * Assumption: nonce is a hexadecimal number of seconds since 1970.
  */
 static int mg_check_nonce(const char *nonce) {
-  unsigned long now = (unsigned long) time(NULL);
+  unsigned long now = (unsigned long) mg_time();
   unsigned long val = (unsigned long) strtoul(nonce, NULL, 16);
   return now < val || now - val < 3600;
 }
@@ -5946,7 +5967,7 @@ static void mg_http_send_digest_auth_request(struct mg_connection *c,
             "WWW-Authenticate: Digest qop=\"auth\", "
             "realm=\"%s\", nonce=\"%lu\"\r\n"
             "Content-Length: 0\r\n\r\n",
-            domain, (unsigned long) time(NULL));
+            domain, (unsigned long) mg_time());
 }
 
 static void mg_http_send_options(struct mg_connection *nc) {
@@ -6864,7 +6885,7 @@ static void mg_do_ssi_include(struct mg_connection *nc, struct http_message *hm,
     mg_printf(nc, "SSI include error: fopen(%s): %s", path,
               strerror(mg_get_errno()));
   } else {
-    mg_set_close_on_exec(fileno(fp));
+    mg_set_close_on_exec((sock_t) fileno(fp));
     if (mg_match_prefix(opts->ssi_pattern, strlen(opts->ssi_pattern), path) >
         0) {
       mg_send_ssi_file(nc, hm, path, fp, include_level + 1, opts);
@@ -6987,7 +7008,7 @@ MG_INTERNAL void mg_handle_ssi_request(struct mg_connection *nc,
   if ((fp = fopen(path, "rb")) == NULL) {
     mg_http_send_error(nc, 404, NULL);
   } else {
-    mg_set_close_on_exec(fileno(fp));
+    mg_set_close_on_exec((sock_t) fileno(fp));
 
     mime_type = mg_get_mime_type(path, "text/plain", opts);
     mg_send_response_line(nc, 200, opts->extra_headers);
@@ -7126,7 +7147,7 @@ MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path) {
       "</D:activelock>\n"
       "</D:lockdiscovery>"
       "</d:multistatus>\n";
-  mg_printf(nc, reply, path, (unsigned int) time(NULL));
+  mg_printf(nc, reply, path, (unsigned int) mg_time());
   nc->flags |= MG_F_SEND_AND_CLOSE;
 }
 #endif
@@ -7257,7 +7278,7 @@ MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path,
     const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range");
     int64_t r1 = 0, r2 = 0;
     pd->file.type = DATA_PUT;
-    mg_set_close_on_exec(fileno(pd->file.fp));
+    mg_set_close_on_exec((sock_t) fileno(pd->file.fp));
     pd->file.cl = to64(cl_hdr->p);
     if (range_hdr != NULL &&
         mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) {
@@ -7710,7 +7731,7 @@ FILE *mg_fopen(const char *path, const char *mode) {
 }
 
 int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(WINCE)
   wchar_t wpath[MAX_PATH_SIZE];
   to_wchar(path, wpath, ARRAY_SIZE(wpath));
   return _wopen(wpath, flag, mode);
@@ -12113,4 +12134,58 @@ const char *strerror(int err) {
   return buf;
 }
 
+int open(const char *filename, int oflag, int pmode) {
+  /*
+   * TODO(alashkin): mg_open function is not used in mongoose
+   * but exists in documentation as utility function
+   * Shall we delete it at all or implement for WinCE as well?
+   */
+   DebugBreak();
+   return 0; /* for compiler */
+}
+
+int _wstati64(const wchar_t *path, cs_stat_t *st) {
+  DWORD fa = GetFileAttributesW(path);
+  if (fa == INVALID_FILE_ATTRIBUTES) {
+    return -1;
+  }
+  memset(st, 0, sizeof(*st));
+  if ((fa & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+    HANDLE h;
+    FILETIME ftime;
+    st->st_mode |= _S_IFREG;
+    h = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+    FILE_ATTRIBUTE_NORMAL, NULL);
+    if (h == INVALID_HANDLE_VALUE) {
+      return -1;
+    }
+    st->st_size = GetFileSize(h, NULL);
+    GetFileTime(h, NULL, NULL, &ftime);
+    st->st_mtime = (uint32_t)((((uint64_t)ftime.dwLowDateTime +
+        ((uint64_t)ftime.dwHighDateTime << 32)) / 10000000.0) - 11644473600);
+    CloseHandle(h);
+  } else {
+    st->st_mode |= _S_IFDIR;
+  }
+  return 0;
+}
+
+/* Windows CE doesn't have neither gmtime nor strftime */
+static void mg_gmt_time_string(char *buf, size_t buf_len, time_t *t) {
+  FILETIME ft;
+  SYSTEMTIME systime;
+  if (t != NULL) {
+    uint64_t filetime = (*t + 11644473600) * 10000000;
+    ft.dwLowDateTime = filetime & 0xFFFFFFFF;
+    ft.dwHighDateTime = (filetime & 0xFFFFFFFF00000000) >> 32;
+    FileTimeToSystemTime(&ft, &systime);
+  } else {
+    GetSystemTime(&systime);
+  }
+  /* There is no PRIu16 in WinCE SDK */
+  snprintf(buf, buf_len, "%d.%d.%d %d:%d:%d GMT", (int)systime.wYear,
+           (int)systime.wMonth, (int)systime.wDay, (int)systime.wHour,
+           (int)systime.wMinute, (int)systime.wSecond);
+}
+
 #endif
diff --git a/mongoose.h b/mongoose.h
index 2352562a2..0a8ecdd52 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -126,8 +126,13 @@
 #pragma warning(disable : 4204) /* missing c99 support */
 #endif
 
+#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
 #define _WINSOCK_DEPRECATED_NO_WARNINGS 1
+#endif
+
+#ifndef _CRT_SECURE_NO_WARNINGS
 #define _CRT_SECURE_NO_WARNINGS
+#endif
 
 #include <assert.h>
 #include <direct.h>
@@ -981,15 +986,6 @@ typedef uint32_t in_addr_t;
 #define INT64_X_FMT "I64x"
 /* TODO(alashkin): check if this is correct */
 #define SIZE_T_FMT "u"
-typedef struct _stati64 cs_stat_t;
-
-#ifndef S_ISDIR
-#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
-#endif
-
-#ifndef S_ISREG
-#define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG)
-#endif
 
 #define DIRSEP '\\'
 
@@ -1024,16 +1020,41 @@ typedef unsigned int* uintptr_t;
 #define BUFSIZ 4096
 #define ENOMEM ERROR_NOT_ENOUGH_MEMORY
 #endif
+/*
+ * Explicitly disabling MG_ENABLE_THREADS for WinCE
+ * because they are enabled for _WIN32 by default
+ */
+#ifndef MG_ENABLE_THREADS
+#define MG_ENABLE_THREADS 0
+#endif
 
-const char *strerror();
+#ifndef MG_ENABLE_FILESYSTEM
+#define MG_ENABLE_FILESYSTEM 1
+#endif
 
-#define MG_ENABLE_FILESYSTEM 0
+typedef struct _stati64 {
+  uint32_t st_mtime;
+  uint32_t st_size;
+  uint32_t st_mode;
+} cs_stat_t;
 
-/*
- * WinCE lacks a lot of used in CGI API functions
- * TODO(alaskin): look for wce_xxxx alternatives
- */
-#define MG_ENABLE_HTTP_CGI 0
+#define ENOENT ERROR_PATH_NOT_FOUND
+#define EACCES ERROR_ACCESS_DENIED
+
+#define _S_IFREG 2
+#define _S_IFDIR 4
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) (((x) & _S_IFDIR) != 0)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(x) (((x) & _S_IFREG) != 0)
+#endif
+
+int open(const char *filename, int oflag, int pmode);
+int _wstati64(const wchar_t *path, cs_stat_t *st);
+const char *strerror();
 
 #endif /* CS_PLATFORM == CS_P_WINCE */
 #endif /* CS_COMMON_PLATFORMS_PLATFORM_WINCE_H_ */
-- 
GitLab