http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/civetweb.c
----------------------------------------------------------------------
diff --git a/thirdparty/civetweb-1.9.1/src/civetweb.c 
b/thirdparty/civetweb-1.9.1/src/civetweb.c
new file mode 100644
index 0000000..da491b6
--- /dev/null
+++ b/thirdparty/civetweb-1.9.1/src/civetweb.c
@@ -0,0 +1,14894 @@
+/* Copyright (c) 2013-2017 the Civetweb developers
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if defined(_WIN32)
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
+#endif
+#ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */
+#define _WIN32_WINNT 0x0501
+#endif
+#else
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE /* for setgroups() */
+#endif
+#if defined(__linux__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
+#endif
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
+#endif
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
+#endif
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
+#endif
+#ifdef __sun
+#define __EXTENSIONS__  /* to expose flockfile and friends in stdio.h */
+#define __inline inline /* not recognized on older compiler versions */
+#endif
+#endif
+
+#if defined(USE_LUA)
+#define USE_TIMERS
+#endif
+
+#if defined(_MSC_VER)
+/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */
+#pragma warning(disable : 4306)
+/* conditional expression is constant: introduced by FD_SET(..) */
+#pragma warning(disable : 4127)
+/* non-constant aggregate initializer: issued due to missing C99 support */
+#pragma warning(disable : 4204)
+/* padding added after data member */
+#pragma warning(disable : 4820)
+/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
+#pragma warning(disable : 4668)
+/* no function prototype given: converting '()' to '(void)' */
+#pragma warning(disable : 4255)
+/* function has been selected for automatic inline expansion */
+#pragma warning(disable : 4711)
+#endif
+
+
+/* This code uses static_assert to check some conditions.
+ * Unfortunately some compilers still do not support it, so we have a
+ * replacement function here. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define mg_static_assert static_assert
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+#define mg_static_assert static_assert
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#define mg_static_assert _Static_assert
+#else
+char static_assert_replacement[1];
+#define mg_static_assert(cond, txt)                                            
\
+       extern char static_assert_replacement[(cond) ? 1 : -1]
+#endif
+
+mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
+                 "int data type size check");
+mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
+                 "pointer data type size check");
+mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
+
+
+/* DTL -- including winsock2.h works better if lean and mean */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#if defined(__SYMBIAN32__)
+#define NO_SSL /* SSL is not supported */
+#define NO_CGI /* CGI is not supported */
+#define PATH_MAX FILENAME_MAX
+#endif /* __SYMBIAN32__ */
+
+
+/* Include the header file here, so the CivetWeb interface is defined for the
+ * entire implementation, including the following forward definitions. */
+#include "civetweb.h"
+
+
+#ifndef IGNORE_UNUSED_RESULT
+#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
+#endif
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* !_WIN32_WCE */
+
+
+#ifdef __clang__
+/* When using -Weverything, clang does not accept it's own headers
+ * in a release build configuration. Disable what is too much in
+ * -Weverything. */
+#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
+#endif
+
+
+#ifdef __MACH__ /* Apple OSX section */
+
+#ifdef __clang__
+/* Avoid warnings for Xopen 7.00 and higher */
+#pragma clang diagnostic ignored "-Wno-reserved-id-macro"
+#pragma clang diagnostic ignored "-Wno-keyword-macro"
+#endif
+
+#define CLOCK_MONOTONIC (1)
+#define CLOCK_REALTIME (2)
+
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <assert.h>
+
+/* clock_gettime is not implemented on OSX prior to 10.12 */
+static int
+_civet_clock_gettime(int clk_id, struct timespec *t)
+{
+       memset(t, 0, sizeof(*t));
+       if (clk_id == CLOCK_REALTIME) {
+               struct timeval now;
+               int rv = gettimeofday(&now, NULL);
+               if (rv) {
+                       return rv;
+               }
+               t->tv_sec = now.tv_sec;
+               t->tv_nsec = now.tv_usec * 1000;
+               return 0;
+
+       } else if (clk_id == CLOCK_MONOTONIC) {
+               static uint64_t clock_start_time = 0;
+               static mach_timebase_info_data_t timebase_ifo = {0, 0};
+
+               uint64_t now = mach_absolute_time();
+
+               if (clock_start_time == 0) {
+                       kern_return_t mach_status = 
mach_timebase_info(&timebase_ifo);
+#if defined(DEBUG)
+                       assert(mach_status == KERN_SUCCESS);
+#else
+                       /* appease "unused variable" warning for release builds 
*/
+                       (void)mach_status;
+#endif
+                       clock_start_time = now;
+               }
+
+               now = (uint64_t)((double)(now - clock_start_time)
+                                * (double)timebase_ifo.numer
+                                / (double)timebase_ifo.denom);
+
+               t->tv_sec = now / 1000000000;
+               t->tv_nsec = now % 1000000000;
+               return 0;
+       }
+       return -1; /* EINVAL - Clock ID is unknown */
+}
+
+/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */
+#ifdef __CLOCK_AVAILABILITY
+/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be
+ * declared but it may be NULL at runtime. So we need to check before using
+ * it. */
+static int
+_civet_safe_clock_gettime(int clk_id, struct timespec *t)
+{
+       if (clock_gettime) {
+               return clock_gettime(clk_id, t);
+       }
+       return _civet_clock_gettime(clk_id, t);
+}
+#define clock_gettime _civet_safe_clock_gettime
+#else
+#define clock_gettime _civet_clock_gettime
+#endif
+
+#endif
+
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#ifndef INT64_MAX
+#define INT64_MAX (9223372036854775807)
+#endif
+
+
+#ifndef MAX_WORKER_THREADS
+#define MAX_WORKER_THREADS (1024 * 64)
+#endif
+
+#ifndef SOCKET_TIMEOUT_QUANTUM /* in ms */
+#define SOCKET_TIMEOUT_QUANTUM (2000)
+#endif
+
+#define SHUTDOWN_RD (0)
+#define SHUTDOWN_WR (1)
+#define SHUTDOWN_BOTH (2)
+
+mg_static_assert(MAX_WORKER_THREADS >= 1,
+                 "worker threads must be a positive number");
+
+mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
+                 "size_t data type size check");
+
+#if defined(_WIN32)                                                            
\
+    && !defined(__SYMBIAN32__) /* WINDOWS / UNIX include block */
+#include <windows.h>
+#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
+#include <ws2tcpip.h>
+
+typedef const char *SOCK_OPT_TYPE;
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (MAX_PATH)
+#endif
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (4096)
+#endif
+
+mg_static_assert(PATH_MAX >= 1, "path length must be a positive number");
+
+#ifndef _IN_PORT_T
+#ifndef in_port_t
+#define in_port_t u_short
+#endif
+#endif
+
+#ifndef _WIN32_WCE
+#include <process.h>
+#include <direct.h>
+#include <io.h>
+#else            /* _WIN32_WCE */
+#define NO_CGI   /* WinCE has no pipes */
+#define NO_POPEN /* WinCE has no popen */
+
+typedef long off_t;
+
+#define errno ((int)(GetLastError()))
+#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10))
+#endif /* _WIN32_WCE */
+
+#define MAKEUQUAD(lo, hi)                                                      
\
+       ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
+#define RATE_DIFF (10000000) /* 100 nsecs */
+#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
+#define SYS2UNIX_TIME(lo, hi)                                                  
\
+       ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
+
+/* Visual Studio 6 does not know __func__ or __FUNCTION__
+ * The rest of MS compilers use __FUNCTION__, not C99 __func__
+ * Also use _strtoui64 on modern M$ compilers */
+#if defined(_MSC_VER)
+#if (_MSC_VER < 1300)
+#define STRX(x) #x
+#define STR(x) STRX(x)
+#define __func__ __FILE__ ":" STR(__LINE__)
+#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
+#define strtoll(x, y, z) (_atoi64(x))
+#else
+#define __func__ __FUNCTION__
+#define strtoull(x, y, z) (_strtoui64(x, y, z))
+#define strtoll(x, y, z) (_strtoi64(x, y, z))
+#endif
+#endif /* _MSC_VER */
+
+#define ERRNO ((int)(GetLastError()))
+#define NO_SOCKLEN_T
+
+#if defined(_WIN64) || defined(__MINGW64__)
+#define SSL_LIB "ssleay64.dll"
+#define CRYPTO_LIB "libeay64.dll"
+#else
+#define SSL_LIB "ssleay32.dll"
+#define CRYPTO_LIB "libeay32.dll"
+#endif
+
+#define O_NONBLOCK (0)
+#ifndef W_OK
+#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
+#endif
+#if !defined(EWOULDBLOCK)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif /* !EWOULDBLOCK */
+#define _POSIX_
+#define INT64_FMT "I64d"
+#define UINT64_FMT "I64u"
+
+#define WINCDECL __cdecl
+#define vsnprintf_impl _vsnprintf
+#define access _access
+#define mg_sleep(x) (Sleep(x))
+
+#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
+#ifndef popen
+#define popen(x, y) (_popen(x, y))
+#endif
+#ifndef pclose
+#define pclose(x) (_pclose(x))
+#endif
+#define close(x) (_close(x))
+#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
+#define RTLD_LAZY (0)
+#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
+#define fdopen(x, y) (_fdopen((x), (y)))
+#define write(x, y, z) (_write((x), (y), (unsigned)z))
+#define read(x, y, z) (_read((x), (y), (unsigned)z))
+#define flockfile(x) (EnterCriticalSection(&global_log_file_lock))
+#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock))
+#define sleep(x) (Sleep((x)*1000))
+#define rmdir(x) (_rmdir(x))
+#define timegm(x) (_mkgmtime(x))
+
+#if !defined(fileno)
+#define fileno(x) (_fileno(x))
+#endif /* !fileno MINGW #defines fileno */
+
+typedef HANDLE pthread_mutex_t;
+typedef DWORD pthread_key_t;
+typedef HANDLE pthread_t;
+typedef struct {
+       CRITICAL_SECTION threadIdSec;
+       struct mg_workerTLS *waiting_thread; /* The chain of threads */
+} pthread_cond_t;
+
+#ifndef __clockid_t_defined
+typedef DWORD clockid_t;
+#endif
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC (1)
+#endif
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME (2)
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+#define _TIMESPEC_DEFINED
+#endif
+#ifndef _TIMESPEC_DEFINED
+struct timespec {
+       time_t tv_sec; /* seconds */
+       long tv_nsec;  /* nanoseconds */
+};
+#endif
+
+#if !defined(WIN_PTHREADS_TIME_H)
+#define MUST_IMPLEMENT_CLOCK_GETTIME
+#endif
+
+#ifdef MUST_IMPLEMENT_CLOCK_GETTIME
+#define clock_gettime mg_clock_gettime
+static int
+clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+       FILETIME ft;
+       ULARGE_INTEGER li;
+       BOOL ok = FALSE;
+       double d;
+       static double perfcnt_per_sec = 0.0;
+
+       if (tp) {
+               memset(tp, 0, sizeof(*tp));
+               if (clk_id == CLOCK_REALTIME) {
+                       GetSystemTimeAsFileTime(&ft);
+                       li.LowPart = ft.dwLowDateTime;
+                       li.HighPart = ft.dwHighDateTime;
+                       li.QuadPart -= 116444736000000000; /* 1.1.1970 in 
filedate */
+                       tp->tv_sec = (time_t)(li.QuadPart / 10000000);
+                       tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
+                       ok = TRUE;
+               } else if (clk_id == CLOCK_MONOTONIC) {
+                       if (perfcnt_per_sec == 0.0) {
+                               QueryPerformanceFrequency((LARGE_INTEGER *)&li);
+                               perfcnt_per_sec = 1.0 / li.QuadPart;
+                       }
+                       if (perfcnt_per_sec != 0.0) {
+                               QueryPerformanceCounter((LARGE_INTEGER *)&li);
+                               d = li.QuadPart * perfcnt_per_sec;
+                               tp->tv_sec = (time_t)d;
+                               d -= tp->tv_sec;
+                               tp->tv_nsec = (long)(d * 1.0E9);
+                               ok = TRUE;
+                       }
+               }
+       }
+
+       return ok ? 0 : -1;
+}
+#endif
+
+
+#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
+
+static int pthread_mutex_lock(pthread_mutex_t *);
+static int pthread_mutex_unlock(pthread_mutex_t *);
+static void path_to_unicode(const struct mg_connection *conn,
+                            const char *path,
+                            wchar_t *wbuf,
+                            size_t wbuf_len);
+
+/* All file operations need to be rewritten to solve #246. */
+
+#include "file_ops.inl"
+
+struct mg_file;
+
+static const char *
+mg_fgets(char *buf, size_t size, struct mg_file *filep, char **p);
+
+
+/* POSIX dirent interface */
+struct dirent {
+       char d_name[PATH_MAX];
+};
+
+typedef struct DIR {
+       HANDLE handle;
+       WIN32_FIND_DATAW info;
+       struct dirent result;
+} DIR;
+
+#if defined(_WIN32) && !defined(POLLIN)
+#ifndef HAVE_POLL
+struct pollfd {
+       SOCKET fd;
+       short events;
+       short revents;
+};
+#define POLLIN (0x0300)
+#endif
+#endif
+
+/* Mark required libraries */
+#if defined(_MSC_VER)
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+#else /* defined(_WIN32) && !defined(__SYMBIAN32__) -                          
\
+         WINDOWS / UNIX include block */
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+typedef const void *SOCK_OPT_TYPE;
+
+#if defined(ANDROID)
+typedef unsigned short int in_port_t;
+#endif
+
+#include <pwd.h>
+#include <unistd.h>
+#include <grp.h>
+#include <dirent.h>
+#define vsnprintf_impl vsnprintf
+
+#if !defined(NO_SSL_DL) && !defined(NO_SSL)
+#include <dlfcn.h>
+#endif
+#include <pthread.h>
+#if defined(__MACH__)
+#define SSL_LIB "libssl.dylib"
+#define CRYPTO_LIB "libcrypto.dylib"
+#else
+#if !defined(SSL_LIB)
+#define SSL_LIB "libssl.so"
+#endif
+#if !defined(CRYPTO_LIB)
+#define CRYPTO_LIB "libcrypto.so"
+#endif
+#endif
+#ifndef O_BINARY
+#define O_BINARY (0)
+#endif /* O_BINARY */
+#define closesocket(a) (close(a))
+#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
+#define mg_remove(conn, x) (remove(x))
+#define mg_sleep(x) (usleep((x)*1000))
+#define mg_opendir(conn, x) (opendir(x))
+#define mg_closedir(x) (closedir(x))
+#define mg_readdir(x) (readdir(x))
+#define ERRNO (errno)
+#define INVALID_SOCKET (-1)
+#define INT64_FMT PRId64
+#define UINT64_FMT PRIu64
+typedef int SOCKET;
+#define WINCDECL
+
+#if defined(__hpux)
+/* HPUX 11 does not have monotonic, fall back to realtime */
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
+ * Itanium.  Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
+ * the prototypes use int* rather than socklen_t* which matches the
+ * actual library expectation.  When called with the wrong size arg
+ * accept() returns a zero client inet addr and check_acl() always
+ * fails.  Since socklen_t is widely used below, just force replace
+ * their typedef with int. - DTL
+ */
+#define socklen_t int
+#endif /* hpux */
+
+#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) -                         
\
+          WINDOWS / UNIX include block */
+
+/* va_copy should always be a macro, C99 and C++11 - DTL */
+#ifndef va_copy
+#define va_copy(x, y) ((x) = (y))
+#endif
+
+#ifdef _WIN32
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static CRITICAL_SECTION global_log_file_lock;
+static DWORD
+pthread_self(void)
+{
+       return GetCurrentThreadId();
+}
+
+
+static int
+pthread_key_create(
+    pthread_key_t *key,
+    void (*_ignored)(void *) /* destructor not supported for Windows */
+    )
+{
+       (void)_ignored;
+
+       if ((key != 0)) {
+               *key = TlsAlloc();
+               return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
+       }
+       return -2;
+}
+
+
+static int
+pthread_key_delete(pthread_key_t key)
+{
+       return TlsFree(key) ? 0 : 1;
+}
+
+
+static int
+pthread_setspecific(pthread_key_t key, void *value)
+{
+       return TlsSetValue(key, value) ? 0 : 1;
+}
+
+
+static void *
+pthread_getspecific(pthread_key_t key)
+{
+       return TlsGetValue(key);
+}
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
+#else
+static pthread_mutexattr_t pthread_mutex_attr;
+#endif /* _WIN32 */
+
+
+#define PASSWORDS_FILE_NAME ".htpasswd"
+#define CGI_ENVIRONMENT_SIZE (4096)
+#define MAX_CGI_ENVIR_VARS (256)
+#define MG_BUF_LEN (8192)
+
+#ifndef MAX_REQUEST_SIZE
+#define MAX_REQUEST_SIZE (16384)
+#endif
+
+mg_static_assert(MAX_REQUEST_SIZE >= 256,
+                 "request size length must be a positive number");
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+
+#if defined(_WIN32_WCE)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static time_t
+time(time_t *ptime)
+{
+       time_t t;
+       SYSTEMTIME st;
+       FILETIME ft;
+
+       GetSystemTime(&st);
+       SystemTimeToFileTime(&st, &ft);
+       t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
+
+       if (ptime != NULL) {
+               *ptime = t;
+       }
+
+       return t;
+}
+
+
+static struct tm *
+localtime_s(const time_t *ptime, struct tm *ptm)
+{
+       int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
+       FILETIME ft, lft;
+       SYSTEMTIME st;
+       TIME_ZONE_INFORMATION tzinfo;
+
+       if (ptm == NULL) {
+               return NULL;
+       }
+
+       *(int64_t *)&ft = t;
+       FileTimeToLocalFileTime(&ft, &lft);
+       FileTimeToSystemTime(&lft, &st);
+       ptm->tm_year = st.wYear - 1900;
+       ptm->tm_mon = st.wMonth - 1;
+       ptm->tm_wday = st.wDayOfWeek;
+       ptm->tm_mday = st.wDay;
+       ptm->tm_hour = st.wHour;
+       ptm->tm_min = st.wMinute;
+       ptm->tm_sec = st.wSecond;
+       ptm->tm_yday = 0; /* hope nobody uses this */
+       ptm->tm_isdst =
+           (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT) ? 1 : 0;
+
+       return ptm;
+}
+
+
+static struct tm *
+gmtime_s(const time_t *ptime, struct tm *ptm)
+{
+       /* FIXME(lsm): fix this. */
+       return localtime_s(ptime, ptm);
+}
+
+
+static int mg_atomic_inc(volatile int *addr);
+static struct tm tm_array[MAX_WORKER_THREADS];
+static int tm_index = 0;
+
+
+static struct tm *
+localtime(const time_t *ptime)
+{
+       int i = mg_atomic_inc(&tm_index) % (sizeof(tm_array) / 
sizeof(tm_array[0]));
+       return localtime_s(ptime, tm_array + i);
+}
+
+
+static struct tm *
+gmtime(const time_t *ptime)
+{
+       int i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
+       return gmtime_s(ptime, tm_array + i);
+}
+
+
+static size_t
+strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm)
+{
+       /* TODO: (void)mg_snprintf(NULL, dst, dst_size, "implement strftime()
+        * for WinCE"); */
+       return 0;
+}
+
+#define _beginthreadex(psec, stack, func, prm, flags, ptid)                    
\
+       (uintptr_t) CreateThread(psec, stack, func, prm, flags, ptid)
+
+#define remove(f) mg_remove(NULL, f)
+
+static int
+rename(const char *a, const char *b)
+{
+       wchar_t wa[PATH_MAX];
+       wchar_t wb[PATH_MAX];
+       path_to_unicode(NULL, a, wa, ARRAY_SIZE(wa));
+       path_to_unicode(NULL, b, wb, ARRAY_SIZE(wb));
+
+       return MoveFileW(wa, wb) ? 0 : -1;
+}
+
+struct stat {
+       int64_t st_size;
+       time_t st_mtime;
+};
+
+static int
+stat(const char *name, struct stat *st)
+{
+       wchar_t wbuf[PATH_MAX];
+       WIN32_FILE_ATTRIBUTE_DATA attr;
+       time_t creation_time, write_time;
+
+       path_to_unicode(NULL, name, wbuf, ARRAY_SIZE(wbuf));
+       memset(&attr, 0, sizeof(attr));
+
+       GetFileAttributesExW(wbuf, GetFileExInfoStandard, &attr);
+       st->st_size =
+           (((int64_t)attr.nFileSizeHigh) << 32) + (int64_t)attr.nFileSizeLow;
+
+       write_time = SYS2UNIX_TIME(attr.ftLastWriteTime.dwLowDateTime,
+                                  attr.ftLastWriteTime.dwHighDateTime);
+       creation_time = SYS2UNIX_TIME(attr.ftCreationTime.dwLowDateTime,
+                                     attr.ftCreationTime.dwHighDateTime);
+
+       if (creation_time > write_time) {
+               st->st_mtime = creation_time;
+       } else {
+               st->st_mtime = write_time;
+       }
+       return 0;
+}
+
+#define access(x, a) 1 /* not required anyway */
+
+/* WinCE-TODO: define stat, remove, rename, _rmdir, _lseeki64 */
+/* Values from errno.h in Windows SDK (Visual Studio). */
+#define EEXIST 17
+#define EACCES 13
+#define ENOENT 2
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+#endif /* defined(_WIN32_WCE) */
+
+
+static int
+mg_atomic_inc(volatile int *addr)
+{
+       int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       /* Depending on the SDK, this function uses either
+        * (volatile unsigned int *) or (volatile LONG *),
+        * so whatever you use, the other SDK is likely to raise a warning. */
+       ret = InterlockedIncrement((volatile long *)addr);
+#elif defined(__GNUC__)                                                        
\
+    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+       ret = __sync_add_and_fetch(addr, 1);
+#else
+       ret = (++(*addr));
+#endif
+       return ret;
+}
+
+
+static int
+mg_atomic_dec(volatile int *addr)
+{
+       int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       /* Depending on the SDK, this function uses either
+        * (volatile unsigned int *) or (volatile LONG *),
+        * so whatever you use, the other SDK is likely to raise a warning. */
+       ret = InterlockedDecrement((volatile long *)addr);
+#elif defined(__GNUC__)                                                        
\
+    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+       ret = __sync_sub_and_fetch(addr, 1);
+#else
+       ret = (--(*addr));
+#endif
+       return ret;
+}
+
+
+#if defined(MEMORY_DEBUGGING)
+static unsigned long mg_memory_debug_blockCount = 0;
+static unsigned long mg_memory_debug_totalMemUsed = 0;
+
+
+static void *
+mg_malloc_ex(size_t size, const char *file, unsigned line)
+{
+       void *data = malloc(size + sizeof(size_t));
+       void *memory = 0;
+       char mallocStr[256];
+
+       if (data) {
+               *(size_t *)data = size;
+               mg_memory_debug_totalMemUsed += size;
+               mg_memory_debug_blockCount++;
+               memory = (void *)(((char *)data) + sizeof(size_t));
+       }
+
+       sprintf(mallocStr,
+               "MEM: %p %5lu alloc   %7lu %4lu --- %s:%u\n",
+               memory,
+               (unsigned long)size,
+               mg_memory_debug_totalMemUsed,
+               mg_memory_debug_blockCount,
+               file,
+               line);
+#if defined(_WIN32)
+       OutputDebugStringA(mallocStr);
+#else
+       DEBUG_TRACE("%s", mallocStr);
+#endif
+
+       return memory;
+}
+
+
+static void *
+mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line)
+{
+       void *data = mg_malloc_ex(size * count, file, line);
+       if (data) {
+               memset(data, 0, size * count);
+       }
+       return data;
+}
+
+
+static void
+mg_free_ex(void *memory, const char *file, unsigned line)
+{
+       char mallocStr[256];
+       void *data = (void *)(((char *)memory) - sizeof(size_t));
+       size_t size;
+
+       if (memory) {
+               size = *(size_t *)data;
+               mg_memory_debug_totalMemUsed -= size;
+               mg_memory_debug_blockCount--;
+               sprintf(mallocStr,
+                       "MEM: %p %5lu free    %7lu %4lu --- %s:%u\n",
+                       memory,
+                       (unsigned long)size,
+                       mg_memory_debug_totalMemUsed,
+                       mg_memory_debug_blockCount,
+                       file,
+                       line);
+#if defined(_WIN32)
+               OutputDebugStringA(mallocStr);
+#else
+               DEBUG_TRACE("%s", mallocStr);
+#endif
+
+               free(data);
+       }
+}
+
+
+static void *
+mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
+{
+       char mallocStr[256];
+       void *data;
+       void *_realloc;
+       size_t oldsize;
+
+       if (newsize) {
+               if (memory) {
+                       data = (void *)(((char *)memory) - sizeof(size_t));
+                       oldsize = *(size_t *)data;
+                       _realloc = realloc(data, newsize + sizeof(size_t));
+                       if (_realloc) {
+                               data = _realloc;
+                               mg_memory_debug_totalMemUsed -= oldsize;
+                               sprintf(mallocStr,
+                                       "MEM: %p %5lu r-free  %7lu %4lu --- 
%s:%u\n",
+                                       memory,
+                                       (unsigned long)oldsize,
+                                       mg_memory_debug_totalMemUsed,
+                                       mg_memory_debug_blockCount,
+                                       file,
+                                       line);
+#if defined(_WIN32)
+                               OutputDebugStringA(mallocStr);
+#else
+                               DEBUG_TRACE("%s", mallocStr);
+#endif
+                               mg_memory_debug_totalMemUsed += newsize;
+                               sprintf(mallocStr,
+                                       "MEM: %p %5lu r-alloc %7lu %4lu --- 
%s:%u\n",
+                                       memory,
+                                       (unsigned long)newsize,
+                                       mg_memory_debug_totalMemUsed,
+                                       mg_memory_debug_blockCount,
+                                       file,
+                                       line);
+#if defined(_WIN32)
+                               OutputDebugStringA(mallocStr);
+#else
+                               DEBUG_TRACE("%s", mallocStr);
+#endif
+                               *(size_t *)data = newsize;
+                               data = (void *)(((char *)data) + 
sizeof(size_t));
+                       } else {
+#if defined(_WIN32)
+                               OutputDebugStringA("MEM: realloc failed\n");
+#else
+                               DEBUG_TRACE("%s", "MEM: realloc failed\n");
+#endif
+                               return _realloc;
+                       }
+               } else {
+                       data = mg_malloc_ex(newsize, file, line);
+               }
+       } else {
+               data = 0;
+               mg_free_ex(memory, file, line);
+       }
+
+       return data;
+}
+
+#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__)
+#define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__)
+#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__)
+#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
+
+#else
+
+static __inline void *
+mg_malloc(size_t a)
+{
+       return malloc(a);
+}
+
+static __inline void *
+mg_calloc(size_t a, size_t b)
+{
+       return calloc(a, b);
+}
+
+static __inline void *
+mg_realloc(void *a, size_t b)
+{
+       return realloc(a, b);
+}
+
+static __inline void
+mg_free(void *a)
+{
+       free(a);
+}
+
+#endif
+
+
+static void mg_vsnprintf(const struct mg_connection *conn,
+                         int *truncated,
+                         char *buf,
+                         size_t buflen,
+                         const char *fmt,
+                         va_list ap);
+
+static void mg_snprintf(const struct mg_connection *conn,
+                        int *truncated,
+                        char *buf,
+                        size_t buflen,
+                        PRINTF_FORMAT_STRING(const char *fmt),
+                        ...) PRINTF_ARGS(5, 6);
+
+/* This following lines are just meant as a reminder to use the mg-functions
+ * for memory management */
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef calloc
+#undef calloc
+#endif
+#ifdef realloc
+#undef realloc
+#endif
+#ifdef free
+#undef free
+#endif
+#ifdef snprintf
+#undef snprintf
+#endif
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
+#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
+#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
+#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
+#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
+#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
+#ifdef _WIN32 /* vsnprintf must not be used in any system, * \ \ \             
\
+               * but this define only works well for Windows. */
+#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
+#endif
+
+
+static pthread_key_t sTlsKey; /* Thread local storage index */
+static int sTlsInit = 0;
+static int thread_idx_max = 0;
+
+
+struct mg_workerTLS {
+       int is_master;
+       unsigned long thread_idx;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+       HANDLE pthread_cond_helper_mutex;
+       struct mg_workerTLS *next_waiting_thread;
+#endif
+};
+
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+
+/* Get a unique thread ID as unsigned long, independent from the data type
+ * of thread IDs defined by the operating system API.
+ * If two calls to mg_current_thread_id  return the same value, they calls
+ * are done from the same thread. If they return different values, they are
+ * done from different threads. (Provided this function is used in the same
+ * process context and threads are not repeatedly created and deleted, but
+ * CivetWeb does not do that).
+ * This function must match the signature required for SSL id callbacks:
+ * CRYPTO_set_id_callback
+ */
+static unsigned long
+mg_current_thread_id(void)
+{
+#ifdef _WIN32
+       return GetCurrentThreadId();
+#else
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+/* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
+ * or not, so one of the two conditions will be unreachable by construction.
+ * Unfortunately the C standard does not define a way to check this at
+ * compile time, since the #if preprocessor conditions can not use the sizeof
+ * operator as an argument. */
+#endif
+
+       if (sizeof(pthread_t) > sizeof(unsigned long)) {
+               /* This is the problematic case for CRYPTO_set_id_callback:
+                * The OS pthread_t can not be cast to unsigned long. */
+               struct mg_workerTLS *tls =
+                   (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+               if (tls == NULL) {
+                       /* SSL called from an unknown thread: Create some 
thread index.
+                        */
+                       tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct 
mg_workerTLS));
+                       tls->is_master = -2; /* -2 means "3rd party thread" */
+                       tls->thread_idx = 
(unsigned)mg_atomic_inc(&thread_idx_max);
+                       pthread_setspecific(sTlsKey, tls);
+               }
+               return tls->thread_idx;
+       } else {
+               /* pthread_t may be any data type, so a simple cast to unsigned 
long
+                * can rise a warning/error, depending on the platform.
+                * Here memcpy is used as an anything-to-anything cast. */
+               unsigned long ret = 0;
+               pthread_t t = pthread_self();
+               memcpy(&ret, &t, sizeof(pthread_t));
+               return ret;
+       }
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+}
+
+
+#if defined(__GNUC__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic pop
+#endif
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic pop
+#endif
+
+
+#if !defined(DEBUG_TRACE)
+#if defined(DEBUG)
+static void DEBUG_TRACE_FUNC(const char *func,
+                             unsigned line,
+                             PRINTF_FORMAT_STRING(const char *fmt),
+                             ...) PRINTF_ARGS(3, 4);
+
+static void
+DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
+{
+       va_list args;
+       struct timespec tsnow;
+       uint64_t nsnow;
+       static uint64_t nslast;
+
+       /* Get some operating system independent thread id */
+       unsigned long thread_id = mg_current_thread_id();
+
+       clock_gettime(CLOCK_REALTIME, &tsnow);
+       nsnow = (((uint64_t)tsnow.tv_sec) * 1000000000) + 
(uint64_t)tsnow.tv_nsec;
+
+       flockfile(stdout);
+       printf("*** %lu.%09lu %12" INT64_FMT " %lu %s:%u: ",
+              (unsigned long)tsnow.tv_sec,
+              (unsigned long)tsnow.tv_nsec,
+              nsnow - nslast,
+              thread_id,
+              func,
+              line);
+       va_start(args, fmt);
+       vprintf(fmt, args);
+       va_end(args);
+       putchar('\n');
+       fflush(stdout);
+       funlockfile(stdout);
+       nslast = nsnow;
+}
+
+#define DEBUG_TRACE(fmt, ...)                                                  
\
+       DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
+
+#else
+#define DEBUG_TRACE(fmt, ...)                                                  
\
+       do {                                                                    
   \
+       } while (0)
+#endif /* DEBUG */
+#endif /* DEBUG_TRACE */
+
+
+#define MD5_STATIC static
+#include "md5.inl"
+
+/* Darwin prior to 7.0 and Win32 do not have socklen_t */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+#define _DARWIN_UNLIMITED_SELECT
+
+#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
+
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL (0)
+#endif
+
+#if !defined(SOMAXCONN)
+#define SOMAXCONN (100)
+#endif
+
+/* Size of the accepted socket queue */
+#if !defined(MGSQLEN)
+#define MGSQLEN (20)
+#endif
+
+
+#if defined(NO_SSL)
+typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
+typedef struct SSL_CTX SSL_CTX;
+#else
+#if defined(NO_SSL_DL)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/engine.h>
+#include <openssl/conf.h>
+#include <openssl/dh.h>
+#else
+/* SSL loaded dynamically from DLL.
+ * I put the prototypes here to be independent from OpenSSL source
+ * installation. */
+
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+typedef struct x509_name X509_NAME;
+typedef struct asn1_integer ASN1_INTEGER;
+typedef struct evp_md EVP_MD;
+typedef struct x509 X509;
+
+
+#define SSL_CTRL_OPTIONS (32)
+#define SSL_CTRL_CLEAR_OPTIONS (77)
+#define SSL_CTRL_SET_ECDH_AUTO (94)
+
+#define SSL_VERIFY_NONE (0)
+#define SSL_VERIFY_PEER (1)
+#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2)
+#define SSL_VERIFY_CLIENT_ONCE (4)
+#define SSL_OP_ALL ((long)(0x80000BFFUL))
+#define SSL_OP_NO_SSLv2 (0x01000000L)
+#define SSL_OP_NO_SSLv3 (0x02000000L)
+#define SSL_OP_NO_TLSv1 (0x04000000L)
+#define SSL_OP_NO_TLSv1_2 (0x08000000L)
+#define SSL_OP_NO_TLSv1_1 (0x10000000L)
+#define SSL_OP_SINGLE_DH_USE (0x00100000L)
+#define SSL_OP_CIPHER_SERVER_PREFERENCE (0x00400000L)
+#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (0x00010000L)
+
+#define SSL_ERROR_NONE (0)
+#define SSL_ERROR_SSL (1)
+#define SSL_ERROR_WANT_READ (2)
+#define SSL_ERROR_WANT_WRITE (3)
+#define SSL_ERROR_WANT_X509_LOOKUP (4)
+#define SSL_ERROR_SYSCALL (5) /* see errno */
+#define SSL_ERROR_ZERO_RETURN (6)
+#define SSL_ERROR_WANT_CONNECT (7)
+#define SSL_ERROR_WANT_ACCEPT (8)
+
+
+struct ssl_func {
+       const char *name;  /* SSL function name */
+       void (*ptr)(void); /* Function pointer */
+};
+
+#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
+#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
+#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
+#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
+#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
+#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
+#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
+#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
+#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
+#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
+#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr)
+#define SSL_CTX_use_PrivateKey_file                                            
\
+       (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
+#define SSL_CTX_use_certificate_file                                           
\
+       (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
+#define SSL_CTX_set_default_passwd_cb                                          
\
+       (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
+#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
+#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr)
+#define SSL_CTX_use_certificate_chain_file                                     
\
+       (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr)
+#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr)
+#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
+#define SSL_CTX_set_verify                                                     
\
+       (*(void (*)(SSL_CTX *,                                                  
   \
+                   int,                                                        
   \
+                   int (*verify_callback)(int, X509_STORE_CTX 
*)))ssl_sw[19].ptr)
+#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
+#define SSL_CTX_load_verify_locations                                          
\
+       (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
+#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
+#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
+#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr)
+#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr)
+#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr)
+#define SSL_CIPHER_get_name                                                    
\
+       (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr)
+#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr)
+#define SSL_CTX_set_session_id_context                                         
\
+       (*(int (*)(SSL_CTX *, const unsigned char *, unsigned 
int))ssl_sw[29].ptr)
+#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr)
+
+
+#define SSL_CTX_set_cipher_list                                                
\
+       (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr)
+#define SSL_CTX_set_options(ctx, op)                                           
\
+       SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
+#define SSL_CTX_clear_options(ctx, op)                                         
\
+       SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
+#define SSL_CTX_set_ecdh_auto(ctx, onoff)                                      
\
+       SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
+
+#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
+#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
+
+
+#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
+#define CRYPTO_set_locking_callback                                            
\
+       (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr)
+#define CRYPTO_set_id_callback                                                 
\
+       (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr)
+#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr)
+#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr)
+#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr)
+#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr)
+#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr)
+#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr)
+#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr)
+#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr)
+#define X509_free (*(void (*)(X509 *))crypto_sw[11].ptr)
+#define X509_get_subject_name (*(X509_NAME * (*)(X509 *))crypto_sw[12].ptr)
+#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *))crypto_sw[13].ptr)
+#define X509_NAME_oneline                                                      
\
+       (*(char *(*)(X509_NAME *, char *, int))crypto_sw[14].ptr)
+#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *))crypto_sw[15].ptr)
+#define i2c_ASN1_INTEGER                                                       
\
+       (*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr)
+#define EVP_get_digestbyname                                                   
\
+       (*(const EVP_MD *(*)(const char *))crypto_sw[17].ptr)
+#define ASN1_digest                                                            
\
+       (*(int (*)(int (*)(),                                                   
   \
+                  const EVP_MD *,                                              
   \
+                  char *,                                                      
   \
+                  unsigned char *,                                             
   \
+                  unsigned int *))crypto_sw[18].ptr)
+#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[19].ptr)
+
+
+/* set_ssl_option() function updates this array.
+ * It loads SSL library dynamically and changes NULLs to the actual addresses
+ * of respective functions. The macros above (like SSL_connect()) are really
+ * just calling these functions indirectly via the pointer. */
+static struct ssl_func ssl_sw[] = {{"SSL_free", NULL},
+                                   {"SSL_accept", NULL},
+                                   {"SSL_connect", NULL},
+                                   {"SSL_read", NULL},
+                                   {"SSL_write", NULL},
+                                   {"SSL_get_error", NULL},
+                                   {"SSL_set_fd", NULL},
+                                   {"SSL_new", NULL},
+                                   {"SSL_CTX_new", NULL},
+                                   {"SSLv23_server_method", NULL},
+                                   {"SSL_library_init", NULL},
+                                   {"SSL_CTX_use_PrivateKey_file", NULL},
+                                   {"SSL_CTX_use_certificate_file", NULL},
+                                   {"SSL_CTX_set_default_passwd_cb", NULL},
+                                   {"SSL_CTX_free", NULL},
+                                   {"SSL_load_error_strings", NULL},
+                                   {"SSL_CTX_use_certificate_chain_file", 
NULL},
+                                   {"SSLv23_client_method", NULL},
+                                   {"SSL_pending", NULL},
+                                   {"SSL_CTX_set_verify", NULL},
+                                   {"SSL_shutdown", NULL},
+                                   {"SSL_CTX_load_verify_locations", NULL},
+                                   {"SSL_CTX_set_default_verify_paths", NULL},
+                                   {"SSL_CTX_set_verify_depth", NULL},
+                                   {"SSL_get_peer_certificate", NULL},
+                                   {"SSL_get_version", NULL},
+                                   {"SSL_get_current_cipher", NULL},
+                                   {"SSL_CIPHER_get_name", NULL},
+                                   {"SSL_CTX_check_private_key", NULL},
+                                   {"SSL_CTX_set_session_id_context", NULL},
+                                   {"SSL_CTX_ctrl", NULL},
+                                   {"SSL_CTX_set_cipher_list", NULL},
+                                   {NULL, NULL}};
+
+
+/* Similar array as ssl_sw. These functions could be located in different
+ * lib. */
+static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL},
+                                      {"CRYPTO_set_locking_callback", NULL},
+                                      {"CRYPTO_set_id_callback", NULL},
+                                      {"ERR_get_error", NULL},
+                                      {"ERR_error_string", NULL},
+                                      {"ERR_remove_state", NULL},
+                                      {"ERR_free_strings", NULL},
+                                      {"ENGINE_cleanup", NULL},
+                                      {"CONF_modules_unload", NULL},
+                                      {"CRYPTO_cleanup_all_ex_data", NULL},
+                                      {"EVP_cleanup", NULL},
+                                      {"X509_free", NULL},
+                                      {"X509_get_subject_name", NULL},
+                                      {"X509_get_issuer_name", NULL},
+                                      {"X509_NAME_oneline", NULL},
+                                      {"X509_get_serialNumber", NULL},
+                                      {"i2c_ASN1_INTEGER", NULL},
+                                      {"EVP_get_digestbyname", NULL},
+                                      {"ASN1_digest", NULL},
+                                      {"i2d_X509", NULL},
+                                      {NULL, NULL}};
+#endif /* NO_SSL_DL */
+#endif /* NO_SSL */
+
+
+#if !defined(NO_CACHING)
+static const char *month_names[] = {"Jan",
+                                    "Feb",
+                                    "Mar",
+                                    "Apr",
+                                    "May",
+                                    "Jun",
+                                    "Jul",
+                                    "Aug",
+                                    "Sep",
+                                    "Oct",
+                                    "Nov",
+                                    "Dec"};
+#endif /* !NO_CACHING */
+
+/* Unified socket address. For IPv6 support, add IPv6 address structure in the
+ * union u. */
+union usa {
+       struct sockaddr sa;
+       struct sockaddr_in sin;
+#if defined(USE_IPV6)
+       struct sockaddr_in6 sin6;
+#endif
+};
+
+/* Describes a string (chunk of memory). */
+struct vec {
+       const char *ptr;
+       size_t len;
+};
+
+struct mg_file_stat {
+       /* File properties filled by mg_stat: */
+       uint64_t size;
+       time_t last_modified;
+       int is_directory; /* Set to 1 if mg_stat is called for a directory */
+       int is_gzipped;   /* Set to 1 if the content is gzipped, in which
+                          * case we need a "Content-Eencoding: gzip" header */
+       int location;     /* 0 = nowhere, 1 = on disk, 2 = in memory */
+};
+
+struct mg_file_in_memory {
+       char *p;
+       uint32_t pos;
+       char mode;
+};
+
+struct mg_file_access {
+       /* File properties filled by mg_fopen: */
+       FILE *fp;
+       /* TODO (low): Replace "membuf" implementation by a "file in memory"
+        * support library. Use some struct mg_file_in_memory *mf; instead of
+        * membuf char pointer. */
+       const char *membuf;
+};
+
+struct mg_file {
+       struct mg_file_stat stat;
+       struct mg_file_access access;
+};
+
+#define STRUCT_FILE_INITIALIZER                                                
\
+       {                                                                       
   \
+               {                                                               
       \
+                       (uint64_t)0, (time_t)0, 0, 0, 0                         
           \
+               }                                                               
       \
+               ,                                                               
       \
+               {                                                               
       \
+                       (FILE *) NULL, (const char *)NULL                       
           \
+               }                                                               
       \
+       }
+
+/* Describes listening socket, or socket which was accept()-ed by the master
+ * thread and queued for future handling by the worker thread. */
+struct socket {
+       SOCKET sock;             /* Listening socket */
+       union usa lsa;           /* Local socket address */
+       union usa rsa;           /* Remote socket address */
+       unsigned char is_ssl;    /* Is port SSL-ed */
+       unsigned char ssl_redir; /* Is port supposed to redirect everything to 
SSL
+                                 * port */
+       unsigned char in_use;    /* Is valid */
+};
+
+/* NOTE(lsm): this enum shoulds be in sync with the config_options below. */
+enum {
+       CGI_EXTENSIONS,
+       CGI_ENVIRONMENT,
+       PUT_DELETE_PASSWORDS_FILE,
+       CGI_INTERPRETER,
+       PROTECT_URI,
+       AUTHENTICATION_DOMAIN,
+       SSI_EXTENSIONS,
+       THROTTLE,
+       ACCESS_LOG_FILE,
+       ENABLE_DIRECTORY_LISTING,
+       ERROR_LOG_FILE,
+       GLOBAL_PASSWORDS_FILE,
+       INDEX_FILES,
+       ENABLE_KEEP_ALIVE,
+       ACCESS_CONTROL_LIST,
+       EXTRA_MIME_TYPES,
+       LISTENING_PORTS,
+       DOCUMENT_ROOT,
+       SSL_CERTIFICATE,
+       NUM_THREADS,
+       RUN_AS_USER,
+       REWRITE,
+       HIDE_FILES,
+       REQUEST_TIMEOUT,
+       KEEP_ALIVE_TIMEOUT,
+       LINGER_TIMEOUT,
+       SSL_DO_VERIFY_PEER,
+       SSL_CA_PATH,
+       SSL_CA_FILE,
+       SSL_VERIFY_DEPTH,
+       SSL_DEFAULT_VERIFY_PATHS,
+       SSL_CIPHER_LIST,
+       SSL_PROTOCOL_VERSION,
+       SSL_SHORT_TRUST,
+
+#if defined(USE_WEBSOCKET)
+       WEBSOCKET_TIMEOUT,
+#endif
+
+       DECODE_URL,
+
+#if defined(USE_LUA)
+       LUA_PRELOAD_FILE,
+       LUA_SCRIPT_EXTENSIONS,
+       LUA_SERVER_PAGE_EXTENSIONS,
+#endif
+#if defined(USE_DUKTAPE)
+       DUKTAPE_SCRIPT_EXTENSIONS,
+#endif
+
+#if defined(USE_WEBSOCKET)
+       WEBSOCKET_ROOT,
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       LUA_WEBSOCKET_EXTENSIONS,
+#endif
+
+       ACCESS_CONTROL_ALLOW_ORIGIN,
+       ERROR_PAGES,
+       CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
+                         * socket option typedef TCP_NODELAY. */
+#if !defined(NO_CACHING)
+       STATIC_FILE_MAX_AGE,
+#endif
+#if defined(__linux__)
+       ALLOW_SENDFILE_CALL,
+#endif
+#if defined(_WIN32)
+       CASE_SENSITIVE_FILES,
+#endif
+#if defined(USE_LUA)
+       LUA_BACKGROUND_SCRIPT,
+#endif
+
+       NUM_OPTIONS
+};
+
+
+/* Config option name, config types, default value */
+static struct mg_option config_options[] = {
+    {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
+    {"cgi_environment", CONFIG_TYPE_STRING, NULL},
+    {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL},
+    {"cgi_interpreter", CONFIG_TYPE_FILE, NULL},
+    {"protect_uri", CONFIG_TYPE_STRING, NULL},
+    {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"},
+    {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
+    {"throttle", CONFIG_TYPE_STRING, NULL},
+    {"access_log_file", CONFIG_TYPE_FILE, NULL},
+    {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"},
+    {"error_log_file", CONFIG_TYPE_FILE, NULL},
+    {"global_auth_file", CONFIG_TYPE_FILE, NULL},
+    {"index_files",
+     CONFIG_TYPE_STRING,
+#ifdef USE_LUA
+     "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,"
+     "index.shtml,index.php"},
+#else
+     "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
+#endif
+    {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"},
+    {"access_control_list", CONFIG_TYPE_STRING, NULL},
+    {"extra_mime_types", CONFIG_TYPE_STRING, NULL},
+    {"listening_ports", CONFIG_TYPE_STRING, "8080"},
+    {"document_root", CONFIG_TYPE_DIRECTORY, NULL},
+    {"ssl_certificate", CONFIG_TYPE_FILE, NULL},
+    {"num_threads", CONFIG_TYPE_NUMBER, "50"},
+    {"run_as_user", CONFIG_TYPE_STRING, NULL},
+    {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL},
+    {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL},
+    {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+    {"keep_alive_timeout_ms", CONFIG_TYPE_NUMBER, "500"},
+    {"linger_timeout_ms", CONFIG_TYPE_NUMBER, NULL},
+    {"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"},
+    {"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL},
+    {"ssl_ca_file", CONFIG_TYPE_FILE, NULL},
+    {"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"},
+    {"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"},
+    {"ssl_cipher_list", CONFIG_TYPE_STRING, NULL},
+    {"ssl_protocol_version", CONFIG_TYPE_NUMBER, "0"},
+    {"ssl_short_trust", CONFIG_TYPE_BOOLEAN, "no"},
+#if defined(USE_WEBSOCKET)
+    {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+#endif
+    {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"},
+
+#if defined(USE_LUA)
+    {"lua_preload_file", CONFIG_TYPE_FILE, NULL},
+    {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+    {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
+#endif
+#if defined(USE_DUKTAPE)
+    /* The support for duktape is still in alpha version state.
+     * The name of this config option might change. */
+    {"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
+#endif
+
+#if defined(USE_WEBSOCKET)
+    {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL},
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+    {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+#endif
+    {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"},
+    {"error_pages", CONFIG_TYPE_DIRECTORY, NULL},
+    {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
+#if !defined(NO_CACHING)
+    {"static_file_max_age", CONFIG_TYPE_NUMBER, "3600"},
+#endif
+#if defined(__linux__)
+    {"allow_sendfile_call", CONFIG_TYPE_BOOLEAN, "yes"},
+#endif
+#if defined(_WIN32)
+    {"case_sensitive", CONFIG_TYPE_BOOLEAN, "no"},
+#endif
+#if defined(USE_LUA)
+    {"lua_background_script", CONFIG_TYPE_FILE, NULL},
+#endif
+
+    {NULL, CONFIG_TYPE_UNKNOWN, NULL}};
+
+/* Check if the config_options and the corresponding enum have compatible
+ * sizes. */
+mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
+                     == (NUM_OPTIONS + 1),
+                 "config_options and enum not sync");
+
+enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
+
+struct mg_handler_info {
+       /* Name/Pattern of the URI. */
+       char *uri;
+       size_t uri_len;
+
+       /* handler type */
+       int handler_type;
+
+       /* Handler for http/https or authorization requests. */
+       mg_request_handler handler;
+
+       /* Handler for ws/wss (websocket) requests. */
+       mg_websocket_connect_handler connect_handler;
+       mg_websocket_ready_handler ready_handler;
+       mg_websocket_data_handler data_handler;
+       mg_websocket_close_handler close_handler;
+
+       /* accepted subprotocols for ws/wss requests. */
+       struct mg_websocket_subprotocols *subprotocols;
+
+       /* Handler for authorization requests */
+       mg_authorization_handler auth_handler;
+
+       /* User supplied argument for the handler function. */
+       void *cbdata;
+
+       /* next handler in a linked list */
+       struct mg_handler_info *next;
+};
+
+struct mg_context {
+       volatile int stop_flag;        /* Should we stop event loop */
+       SSL_CTX *ssl_ctx;              /* SSL context */
+       char *config[NUM_OPTIONS];     /* Civetweb configuration parameters */
+       struct mg_callbacks callbacks; /* User-defined callback function */
+       void *user_data;               /* User-defined data */
+       int context_type;              /* 1 = server context,
+                                       * 2 = ws/wss client context,
+                                       */
+
+       struct socket *listening_sockets;
+       struct pollfd *listening_socket_fds;
+       unsigned int num_listening_sockets;
+
+       pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */
+
+#ifdef ALTERNATIVE_QUEUE
+       struct socket *client_socks;
+       void **client_wait_events;
+#else
+       struct socket queue[MGSQLEN]; /* Accepted sockets */
+       volatile int sq_head;         /* Head of the socket queue */
+       volatile int sq_tail;         /* Tail of the socket queue */
+       pthread_cond_t sq_full;       /* Signaled when socket is produced */
+       pthread_cond_t sq_empty;      /* Signaled when socket is consumed */
+#endif
+
+       pthread_t masterthreadid; /* The master thread ID */
+       unsigned int
+           cfg_worker_threads;      /* The number of configured worker 
threads. */
+       pthread_t *worker_threadids; /* The worker thread IDs */
+       struct mg_connection *worker_connections; /* The connection struct, pre-
+                                                  * allocated for each worker 
*/
+
+       time_t start_time;        /* Server start time, used for authentication 
*/
+       uint64_t auth_nonce_mask; /* Mask for all nonce values */
+       pthread_mutex_t nonce_mutex; /* Protects nonce_count */
+       unsigned long nonce_count;   /* Used nonces, used for authentication */
+
+       char *systemName; /* What operating system is running */
+
+       /* linked list of uri handlers */
+       struct mg_handler_info *handlers;
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       /* linked list of shared lua websockets */
+       struct mg_shared_lua_websocket_list *shared_lua_websockets;
+#endif
+
+#if defined(USE_TIMERS)
+       struct ttimers *timers;
+#endif
+
+#if defined(USE_LUA)
+       void *lua_background_state;
+#endif
+};
+
+
+struct mg_connection {
+       struct mg_request_info request_info;
+       struct mg_context *ctx;
+       SSL *ssl;                 /* SSL descriptor */
+       SSL_CTX *client_ssl_ctx;  /* SSL context for client connections */
+       struct socket client;     /* Connected client */
+       time_t conn_birth_time;   /* Time (wall clock) when connection was
+                                  * established */
+       struct timespec req_time; /* Time (since system start) when the request
+                                  * was received */
+       int64_t num_bytes_sent;   /* Total bytes sent to client */
+       int64_t content_len;      /* Content-Length header value */
+       int64_t consumed_content; /* How many bytes of content have been read */
+       int is_chunked;           /* Transfer-Encoding is chunked: 0=no, 1=yes:
+                                  * data available, 2: all data read */
+       size_t chunk_remainder;   /* Unread data from the last chunk */
+       char *buf;                /* Buffer for received data */
+       char *path_info;          /* PATH_INFO part of the URL */
+
+       int must_close;       /* 1 if connection must be closed */
+       int in_error_handler; /* 1 if in handler for user defined error
+                              * pages */
+       int handled_requests; /* Number of requests handled by this connection 
*/
+       int buf_size;         /* Buffer size */
+       int request_len;      /* Size of the request + headers in a buffer */
+       int data_len;         /* Total size of data in a buffer */
+       int status_code;      /* HTTP reply status code, e.g. 200 */
+       int throttle;         /* Throttling, bytes/sec. <= 0 means no
+                              * throttle */
+       time_t last_throttle_time;   /* Last time throttled data was sent */
+       int64_t last_throttle_bytes; /* Bytes sent this second */
+       pthread_mutex_t mutex;       /* Used by mg_(un)lock_connection to ensure
+                                     * atomic transmissions for websockets */
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+       void *lua_websocket_state; /* Lua_State for a websocket connection */
+#endif
+
+       int thread_index; /* Thread index within ctx */
+};
+
+
+/* Directory entry */
+struct de {
+       struct mg_connection *conn;
+       char *file_name;
+       struct mg_file_stat file;
+};
+
+
+#if defined(USE_WEBSOCKET)
+static int is_websocket_protocol(const struct mg_connection *conn);
+#else
+#define is_websocket_protocol(conn) (0)
+#endif
+
+
+#if !defined(NO_THREAD_NAME)
+#if defined(_WIN32) && defined(_MSC_VER)
+/* Set the thread name for debugging purposes in Visual Studio
+ * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+ */
+#pragma pack(push, 8)
+typedef struct tagTHREADNAME_INFO {
+       DWORD dwType;     /* Must be 0x1000. */
+       LPCSTR szName;    /* Pointer to name (in user addr space). */
+       DWORD dwThreadID; /* Thread ID (-1=caller thread). */
+       DWORD dwFlags;    /* Reserved for future use, must be zero. */
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+#elif defined(__linux__)
+
+#include <sys/prctl.h>
+#include <sys/sendfile.h>
+#include <sys/eventfd.h>
+
+
+#if defined(ALTERNATIVE_QUEUE)
+
+static void *
+event_create(void)
+{
+       int ret = eventfd(0, EFD_CLOEXEC);
+       if (ret == -1) {
+               /* Linux uses -1 on error, Windows NULL. */
+               /* However, Linux does not return 0 on success either. */
+               return 0;
+       }
+       return (void *)ret;
+}
+
+
+static int
+event_wait(void *eventhdl)
+{
+       uint64_t u;
+       int s = (int)read((int)eventhdl, &u, sizeof(u));
+       if (s != sizeof(uint64_t)) {
+               /* error */
+               return 0;
+       }
+       (void)u; /* the value is not required */
+       return 1;
+}
+
+
+static int
+event_signal(void *eventhdl)
+{
+       uint64_t u = 1;
+       int s = (int)write((int)eventhdl, &u, sizeof(u));
+       if (s != sizeof(uint64_t)) {
+               /* error */
+               return 0;
+       }
+       return 1;
+}
+
+
+static void
+event_destroy(void *eventhdl)
+{
+       close((int)eventhdl);
+}
+#endif
+
+#endif
+
+
+#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
+
+struct posix_event {
+       pthread_mutex_t mutex;
+       pthread_cond_t cond;
+};
+
+
+static void *
+event_create(void)
+{
+       struct posix_event *ret = mg_malloc(sizeof(struct posix_event));
+       if (ret == 0) {
+               /* out of memory */
+               return 0;
+       }
+       if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
+               /* pthread mutex not available */
+               mg_free(ret);
+               return 0;
+       }
+       if (0 != pthread_cond_init(&(ret->cond), NULL)) {
+               /* pthread cond not available */
+               pthread_mutex_destroy(&(ret->mutex));
+               mg_free(ret);
+               return 0;
+       }
+       return (void *)ret;
+}
+
+
+static int
+event_wait(void *eventhdl)
+{
+       struct posix_event *ev = (struct posix_event *)eventhdl;
+       pthread_mutex_lock(&(ev->mutex));
+       pthread_cond_wait(&(ev->cond), &(ev->mutex));
+       pthread_mutex_unlock(&(ev->mutex));
+       return 1;
+}
+
+
+static int
+event_signal(void *eventhdl)
+{
+       struct posix_event *ev = (struct posix_event *)eventhdl;
+       pthread_mutex_lock(&(ev->mutex));
+       pthread_cond_signal(&(ev->cond));
+       pthread_mutex_unlock(&(ev->mutex));
+       return 1;
+}
+
+
+static void
+event_destroy(void *eventhdl)
+{
+       struct posix_event *ev = (struct posix_event *)eventhdl;
+       pthread_cond_destroy(&(ev->cond));
+       pthread_mutex_destroy(&(ev->mutex));
+       mg_free(ev);
+}
+#endif
+
+
+static void
+mg_set_thread_name(const char *name)
+{
+       char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
+
+       mg_snprintf(
+           NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
+
+#if defined(_WIN32)
+#if defined(_MSC_VER)
+       /* Windows and Visual Studio Compiler */
+       __try
+       {
+               THREADNAME_INFO info;
+               info.dwType = 0x1000;
+               info.szName = threadName;
+               info.dwThreadID = ~0U;
+               info.dwFlags = 0;
+
+               RaiseException(0x406D1388,
+                              0,
+                              sizeof(info) / sizeof(ULONG_PTR),
+                              (ULONG_PTR *)&info);
+       }
+       __except(EXCEPTION_EXECUTE_HANDLER)
+       {
+       }
+#elif defined(__MINGW32__)
+/* No option known to set thread name for MinGW */
+#endif
+#elif defined(__GLIBC__)                                                       
\
+    && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
+       /* pthread_setname_np first appeared in glibc in version 2.12*/
+       (void)pthread_setname_np(pthread_self(), threadName);
+#elif defined(__linux__)
+       /* on linux we can use the old prctl function */
+       (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
+#endif
+}
+#else /* !defined(NO_THREAD_NAME) */
+void
+mg_set_thread_name(const char *threadName)
+{
+}
+#endif
+
+
+#if defined(MG_LEGACY_INTERFACE)
+const char **
+mg_get_valid_option_names(void)
+{
+       /* This function is deprecated. Use mg_get_valid_options instead. */
+       static const char *
+           data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0};
+       int i;
+
+       for (i = 0; config_options[i].name != NULL; i++) {
+               data[i * 2] = config_options[i].name;
+               data[i * 2 + 1] = config_options[i].default_value;
+       }
+
+       return data;
+}
+#endif
+
+
+const struct mg_option *
+mg_get_valid_options(void)
+{
+       return config_options;
+}
+
+
+/* Do not open file (used in is_file_in_memory) */
+#define MG_FOPEN_MODE_NONE (0)
+
+/* Open file for read only access */
+#define MG_FOPEN_MODE_READ (1)
+
+/* Open file for writing, create and overwrite */
+#define MG_FOPEN_MODE_WRITE (2)
+
+/* Open file for writing, create and append */
+#define MG_FOPEN_MODE_APPEND (4)
+
+
+/* If a file is in memory, set all "stat" members and the membuf pointer of
+ * output filep and return 1, otherwise return 0 and don't modify anything. */
+static int
+open_file_in_memory(const struct mg_connection *conn,
+                    const char *path,
+                    struct mg_file *filep,
+                    int mode)
+{
+       size_t size = 0;
+       const char *buf = NULL;
+       if (!conn) {
+               return 0;
+       }
+
+       if ((mode != MG_FOPEN_MODE_NONE) && (mode != MG_FOPEN_MODE_READ)) {
+               return 0;
+       }
+
+       if (conn->ctx->callbacks.open_file) {
+               buf = conn->ctx->callbacks.open_file(conn, path, &size);
+               if (buf != NULL) {
+                       if (filep == NULL) {
+                               /* This is a file in memory, but we cannot 
store the properties
+                                * now.
+                                * Called from "is_file_in_memory" function. */
+                               return 1;
+                       }
+
+                       /* NOTE: override filep->size only on success. 
Otherwise, it might
+                        * break constructs like if (!mg_stat() || !mg_fopen()) 
... */
+                       filep->access.membuf = buf;
+                       filep->access.fp = NULL;
+
+                       /* Size was set by the callback */
+                       filep->stat.size = size;
+
+                       /* Assume the data may change during runtime by setting
+                        * last_modified = now */
+                       filep->stat.last_modified = time(NULL);
+
+                       filep->stat.is_directory = 0;
+                       filep->stat.is_gzipped = 0;
+               }
+       }
+
+       return (buf != NULL);
+}
+
+
+static int
+is_file_in_memory(const struct mg_connection *conn, const char *path)
+{
+       return open_file_in_memory(conn, path, NULL, MG_FOPEN_MODE_NONE);
+}
+
+
+static int
+is_file_opened(const struct mg_file_access *fileacc)
+{
+       if (!fileacc) {
+               return 0;
+       }
+       return (fileacc->membuf != NULL) || (fileacc->fp != NULL);
+}
+
+
+static int mg_stat(const struct mg_connection *conn,
+                   const char *path,
+                   struct mg_file_stat *filep);
+
+
+/* mg_fopen will open a file either in memory or on the disk.
+ * The input parameter path is a string in UTF-8 encoding.
+ * The input parameter mode is MG_FOPEN_MODE_*
+ * On success, either fp or membuf will be set in the output
+ * struct file. All status members will also be set.
+ * The function returns 1 on success, 0 on error. */
+static int
+mg_fopen(const struct mg_connection *conn,
+         const char *path,
+         int mode,
+         struct mg_file *filep)
+{
+       int found;
+
+       if (!filep) {
+               return 0;
+       }
+       filep->access.fp = NULL;
+       filep->access.membuf = NULL;
+
+       if (!is_file_in_memory(conn, path)) {
+
+               /* filep is initialized in mg_stat: all fields with memset to,
+               * some fields like size and modification date with values */
+               found = mg_stat(conn, path, &(filep->stat));
+
+               if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
+                       /* file does not exist and will not be created */
+                       return 0;
+               }
+
+#ifdef _WIN32
+               {
+                       wchar_t wbuf[PATH_MAX];
+                       path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+                       switch (mode) {
+                       case MG_FOPEN_MODE_READ:
+                               filep->access.fp = _wfopen(wbuf, L"rb");
+                               break;
+                       case MG_FOPEN_MODE_WRITE:
+                               filep->access.fp = _wfopen(wbuf, L"wb");
+                               break;
+                       case MG_FOPEN_MODE_APPEND:
+                               filep->access.fp = _wfopen(wbuf, L"ab");
+                               break;
+                       }
+               }
+#else
+               /* Linux et al already use unicode. No need to convert. */
+               switch (mode) {
+               case MG_FOPEN_MODE_READ:
+                       filep->access.fp = fopen(path, "r");
+                       break;
+               case MG_FOPEN_MODE_WRITE:
+                       filep->access.fp = fopen(path, "w");
+                       break;
+               case MG_FOPEN_MODE_APPEND:
+                       filep->access.fp = fopen(path, "a");
+                       break;
+               }
+
+#endif
+               if (!found) {
+                       /* File did not exist before fopen was called.
+                        * Maybe it has been created now. Get stat info
+                        * like creation time now. */
+                       found = mg_stat(conn, path, &(filep->stat));
+                       (void)found;
+               }
+
+               /* file is on disk */
+               return (filep->access.fp != NULL);
+
+       } else {
+               /* is_file_in_memory returned true */
+               if (open_file_in_memory(conn, path, filep, mode)) {
+                       /* file is in memory */
+                       return (filep->access.membuf != NULL);
+               }
+       }
+
+       /* Open failed */
+       return 0;
+}
+
+
+/* return 0 on success, just like fclose */
+static int
+mg_fclose(struct mg_file_access *fileacc)
+{
+       int ret = -1;
+       if (fileacc != NULL) {
+               if (fileacc->fp != NULL) {
+                       ret = fclose(fileacc->fp);
+               } else if (fileacc->membuf != NULL) {
+                       ret = 0;
+               }
+               /* reset all members of fileacc */
+               memset(fileacc, 0, sizeof(*fileacc));
+       }
+       return ret;
+}
+
+
+static void
+mg_strlcpy(register char *dst, register const char *src, size_t n)
+{
+       for (; *src != '\0' && n > 1; n--) {
+               *dst++ = *src++;
+       }
+       *dst = '\0';
+}
+
+
+static int
+lowercase(const char *s)
+{
+       return tolower(*(const unsigned char *)s);
+}
+
+
+int
+mg_strncasecmp(const char *s1, const char *s2, size_t len)
+{
+       int diff = 0;
+
+       if (len > 0) {
+               do {
+                       diff = lowercase(s1++) - lowercase(s2++);
+               } while (diff == 0 && s1[-1] != '\0' && --len > 0);
+       }
+
+       return diff;
+}
+
+
+int
+mg_strcasecmp(const char *s1, const char *s2)
+{
+       int diff;
+
+       do {
+               diff = lowercase(s1++) - lowercase(s2++);
+       } while (diff == 0 && s1[-1] != '\0');
+
+       return diff;
+}
+
+
+static char *
+mg_strndup(const char *ptr, size_t len)
+{
+       char *p;
+
+       if ((p = (char *)mg_malloc(len + 1)) != NULL) {
+               mg_strlcpy(p, ptr, len + 1);
+       }
+
+       return p;
+}
+
+
+static char *
+mg_strdup(const char *str)
+{
+       return mg_strndup(str, strlen(str));
+}
+
+
+static const char *
+mg_strcasestr(const char *big_str, const char *small_str)
+{
+       size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
+
+       if (big_len >= small_len) {
+               for (i = 0; i <= (big_len - small_len); i++) {
+                       if (mg_strncasecmp(big_str + i, small_str, small_len) 
== 0) {
+                               return big_str + i;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+
+/* Return null terminated string of given maximum length.
+ * Report errors if length is exceeded. */
+static void
+mg_vsnprintf(const struct mg_connection *conn,
+             int *truncated,
+             char *buf,
+             size_t buflen,
+             const char *fmt,
+             va_list ap)
+{
+       int n, ok;
+
+       if (buflen == 0) {
+               return;
+       }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+/* Using fmt as a non-literal is intended here, since it is mostly called
+ * indirectly by mg_snprintf */
+#endif
+
+       n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
+       ok = (n >= 0) && ((size_t)n < buflen);
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+       if (ok) {
+               if (truncated) {
+                       *truncated = 0;
+               }
+       } else {
+               if (truncated) {
+                       *truncated = 1;
+               }
+               mg_cry(conn,
+                      "truncating vsnprintf buffer: [%.*s]",
+                      (int)((buflen > 200) ? 200 : (buflen - 1)),
+                      buf);
+               n = (int)buflen - 1;
+       }
+       buf[n] = '\0';
+}
+
+
+static void
+mg_snprintf(const struct mg_connection *conn,
+            int *truncated,
+            char *buf,
+            size_t buflen,
+            const char *fmt,
+            ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
+       va_end(ap);
+}
+
+
+static int
+get_option_index(const char *name)
+{
+       int i;
+
+       for (i = 0; config_options[i].name != NULL; i++) {
+               if (strcmp(config_options[i].name, name) == 0) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+
+const char *
+mg_get_option(const struct mg_context *ctx, const char *name)
+{
+       int i;
+       if ((i = get_option_index(name)) == -1) {
+               return NULL;
+       } else if (!ctx || ctx->config[i] == NULL) {
+               return "";
+       } else {
+               return ctx->config[i];
+       }
+}
+
+
+struct mg_context *
+mg_get_context(const struct mg_connection *conn)
+{
+       return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx);
+}
+
+
+void *
+mg_get_user_data(const struct mg_context *ctx)
+{
+       return (ctx == NULL) ? NULL : ctx->user_data;
+}
+
+
+void
+mg_set_user_connection_data(struct mg_connection *conn, void *data)
+{
+       if (conn != NULL) {
+               conn->request_info.conn_data = data;
+       }
+}
+
+
+void *
+mg_get_user_connection_data(const struct mg_connection *conn)
+{
+       if (conn != NULL) {
+               return conn->request_info.conn_data;
+       }
+       return NULL;
+}
+
+
+size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
+{
+       size_t i;
+       if (!ctx) {
+               return 0;
+       }
+       for (i = 0; i < size && i < ctx->num_listening_sockets; i++) {
+               ssl[i] = ctx->listening_sockets[i].is_ssl;
+               ports[i] =
+#if defined(USE_IPV6)
+                   (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
+                       ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
+                       :
+#endif
+                       ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
+       }
+       return i;
+}
+
+
+int
+mg_get_server_ports(const struct mg_context *ctx,
+                    int size,
+                    struct mg_server_ports *ports)
+{
+       int i, cnt = 0;
+
+       if (size <= 0) {
+               return -1;
+       }
+       memset(ports, 0, sizeof(*ports) * (size_t)size);
+       if (!ctx) {
+               return -1;
+       }
+       if (!ctx->listening_sockets) {
+               return -1;
+       }
+
+       for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
+
+               ports[cnt].port =
+#if defined(USE_IPV6)
+                   (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
+                       ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
+                       :
+#endif
+                       ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
+               ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
+               ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
+
+               if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
+                       /* IPv4 */
+                       ports[cnt].protocol = 1;
+                       cnt++;
+               } else if (ctx->listening_sockets[i].lsa.sa.sa_family == 
AF_INET6) {
+                       /* IPv6 */
+                       ports[cnt].protocol = 3;
+                       cnt++;
+               }
+       }
+
+       return cnt;
+}
+
+
+static void
+sockaddr_to_string(char *buf, size_t len, const union usa *usa)
+{
+       buf[0] = '\0';
+
+       if (!usa) {
+               return;
+       }
+
+       if (usa->sa.sa_family == AF_INET) {
+               getnameinfo(&usa->sa,
+                           sizeof(usa->sin),
+                           buf,
+                           (unsigned)len,
+                           NULL,
+                           0,
+                           NI_NUMERICHOST);
+       }
+#if defined(USE_IPV6)
+       else if (usa->sa.sa_family == AF_INET6) {
+               getnameinfo(&usa->sa,
+                           sizeof(usa->sin6),
+                           buf,
+                           (unsigned)len,
+                           NULL,
+                           0,
+                           NI_NUMERICHOST);
+       }
+#endif
+}
+
+
+/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
+ * included in all responses other than 100, 101, 5xx. */
+static void
+gmt_time_string(char *buf, size_t buf_len, time_t *t)
+{
+       struct tm *tm;
+
+       tm = ((t != NULL) ? gmtime(t) : NULL);
+       if (tm != NULL) {
+               strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
+       } else {
+               mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
+               buf[buf_len - 1] = '\0';
+       }
+}
+
+
+/* difftime for struct timespec. Return value is in seconds. */
+static double
+mg_difftimespec(const struct timespec *ts_now, const struct timespec 
*ts_before)
+{
+       return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
+              + (double)(ts_now->tv_sec - ts_before->tv_sec);
+}
+
+
+/* Print error message to the opened error log stream. */
+void
+mg_cry(const struct mg_connection *conn, const char *fmt, ...)
+{
+       char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
+       va_list ap;
+       struct mg_file fi;
+       time_t timestamp;
+
+       va_start(ap, fmt);
+       IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
+       va_end(ap);
+       buf[sizeof(buf) - 1] = 0;
+
+       if (!conn) {
+               puts(buf);
+               return;
+       }
+
+       /* Do not lock when getting the callback value, here and below.
+        * I suppose this is fine, since function cannot disappear in the
+        * same way string option can. */
+       if ((conn->ctx->callbacks.log_message == NULL)
+           || (conn->ctx->callbacks.log_message(conn, buf) == 0)) {
+
+               if (conn->ctx->config[ERROR_LOG_FILE] != NULL) {
+                       if (mg_fopen(conn,
+                                    conn->ctx->config[ERROR_LOG_FILE],
+                                    MG_FOPEN_MODE_APPEND,
+                                    &fi) == 0) {
+                               fi.access.fp = NULL;
+                       }
+               } else {
+                       fi.access.fp = NULL;
+               }
+
+               if (fi.access.fp != NULL) {
+                       flockfile(fi.access.fp);
+                       timestamp = time(NULL);
+
+                       sockaddr_to_string(src_addr, sizeof(src_addr), 
&conn->client.rsa);
+                       fprintf(fi.access.fp,
+                               "[%010lu] [error] [client %s] ",
+                               (unsigned long)timestamp,
+                               src_addr);
+
+                       if (conn->request_info.request_method != NULL) {
+                               fprintf(fi.access.fp,
+                                       "%s %s: ",
+                                       conn->request_info.request_method,
+                                       conn->request_info.request_uri);
+                       }
+
+                       fprintf(fi.access.fp, "%s", buf);
+                       fputc('\n', fi.access.fp);
+                       fflush(fi.access.fp);
+                       funlockfile(fi.access.fp);
+                       (void)mg_fclose(&fi.access); /* Ignore errors. We can't 
call
+                                                     * mg_cry here anyway ;-) 
*/
+               }
+       }
+}
+
+
+/* Return fake connection structure. Used for logging, if connection
+ * is not applicable at the moment of logging. */
+static struct mg_connection *
+fc(struct mg_context *ctx)
+{
+       static struct mg_connection fake_connection;
+       fake_connection.ctx = ctx;
+       return &fake_connection;
+}
+
+
+const char *
+mg_version(void)
+{
+       return CIVETWEB_VERSION;
+}
+
+
+const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *conn)
+{
+       if (!conn) {
+               return NULL;
+       }
+       return &conn->request_info;
+}
+
+
+/* Skip the characters until one of the delimiters characters found.
+ * 0-terminate resulting word. Skip the delimiter and following whitespaces.
+ * Advance pointer to buffer to the next word. Return found 0-terminated word.
+ * Delimiters can be quoted with quotechar. */
+static char *
+skip_quoted(char **buf,
+            const char *delimiters,
+            const char *whitespace,
+            char quotechar)
+{
+       char *p, *begin_word, *end_word, *end_whitespace;
+
+       begin_word = *buf;
+       end_word = begin_word + strcspn(begin_word, delimiters);
+
+       /* Check for quotechar */
+       if (end_word > begin_word) {
+               p = end_word - 1;
+               while (*p == quotechar) {
+                       /* While the delimiter is quoted, look for the next 
delimiter. */
+                       /* This happens, e.g., in calls from parse_auth_header,
+                        * if the user name contains a " character. */
+
+                       /* If there is anything beyond end_word, copy it. */
+                       if (*end_word != '\0') {
+                               size_t end_off = strcspn(end_word + 1, 
delimiters);
+                               memmove(p, end_word, end_off + 1);
+                               p += end_off; /* p must correspond to end_word 
- 1 */
+                               end_word += end_off + 1;
+                       } else {
+                               *p = '\0';
+                               break;
+                       }
+               }
+               for (p++; p < end_word; p++) {
+                       *p = '\0';
+               }
+       }
+
+       if (*end_word == '\0') {
+               *buf = end_word;
+       } else {
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Disable spurious conversion warning for GCC */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+               end_whitespace = end_word + strspn(&end_word[1], whitespace) + 
1;
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+#pragma GCC diagnostic pop
+#endif
+
+               for (p = end_word; p < end_whitespace; p++) {
+                       *p = '\0';
+               }
+
+               *buf = end_whitespace;
+       }
+
+       return begin_word;
+}
+
+
+/* Simplified version of skip_quoted without quote char
+ * and whitespace == delimiters */
+static char *
+skip(char **buf, const char *delimiters)
+{
+       return skip_quoted(buf, delimiters, delimiters, 0);
+}
+
+
+/* Return HTTP header value, or NULL if not found. */
+static const char *
+get_header(const struct mg_request_info *ri, const char *name)
+{
+       int i;
+       if (ri) {
+               for (i = 0; i < ri->num_headers; i++) {
+                       if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
+                               return ri->http_headers[i].value;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+
+const char *
+mg_get_header(const struct mg_connection *conn, const char *name)
+{
+       if (!conn) {
+               return NULL;
+       }
+
+       return get_header(&conn->request_info, name);
+}
+
+
+/* A helper function for traversing a comma separated list of values.
+ * It returns a list pointer shifted to the next value, or NULL if the end
+ * of the list found.
+ * Value is stored in val vector. If value has form "x=y", then eq_val
+ * vector is initialized to point to the "y" part, and val vector length
+ * is adjusted to point only to "x". */
+static const char *
+next_option(const char *list, struct vec *val, struct vec *eq_val)
+{
+       int end;
+
+reparse:
+       if (val == NULL || list == NULL || *list == '\0') {
+               /* End of the list */
+               list = NULL;
+       } else {
+               /* Skip over leading LWS */
+               while (*list == ' ' || *list == '\t')
+                       list++;
+
+               val->ptr = list;
+               if ((list = strchr(val->ptr, ',')) != NULL) {
+                       /* Comma found. Store length and shift the list ptr */
+                       val->len = ((size_t)(list - val->ptr));
+                       list++;
+               } else {
+                       /* This value is the last one */
+                       list = val->ptr + strlen(val->ptr);
+                       val->len = ((size_t)(list - val->ptr));
+               }
+
+               /* Adjust length for trailing LWS */
+               end = (int)val->len - 1;
+               while (end >= 0 && (val->ptr[end] == ' ' || val->ptr[end] == 
'\t'))
+                       end--;
+               val->len = (size_t)(end + 1);
+
+               if (val->len == 0) {
+                       /* Ignore any empty entries. */
+                       goto reparse;
+               }
+
+               if (eq_val != NULL) {
+                       /* Value has form "x=y", adjust pointers and lengths
+                        * so that val points to "x", and eq_val points to "y". 
*/
+                       eq_val->len = 0;
+                       eq_val->ptr = (const char *)memchr(val->ptr, '=', 
val->len);
+                       if (eq_val->ptr != NULL) {
+                               eq_val->ptr++; /* Skip over '=' character */
+                               eq_val->len = ((size_t)(val->ptr - 
eq_val->ptr)) + val->len;
+                               val->len = ((size_t)(eq_val->ptr - val->ptr)) - 
1;
+                       }
+               }
+       }
+
+       return list;
+}
+
+/* A helper function for checking if a comma separated list of values contains
+ * the given option (case insensitvely).
+ * 'header' can be NULL, in which case false is returned. */
+static int
+header_has_option(const char *header, const char *option)
+{
+       struct vec opt_vec;
+       struct vec eq_vec;
+
+       assert(option != NULL);
+       assert(option[0] != '\0');
+
+       while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
+               if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* Perform case-insensitive match of string against pattern */
+static int
+match_prefix(const char *pattern, size_t pattern_len, const char *str)
+{
+       const char *or_str;
+       size_t i;
+       int j, len, res;
+
+       if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) 
{
+               res = match_prefix(pattern, (size_t)(or_str - pattern), str);
+               return (res > 0) ? res : match_prefix(or_str + 1,
+                                                     (size_t)((pattern + 
pattern_len)
+                                                              - (or_str + 1)),
+                                                     str);
+       }
+
+       for (i = 0, j = 0; i < pattern_len; i++, j++) {
+               if (pattern[i] == '?' && str[j] != '\0') {
+                       continue;
+               } else if (pattern[i] == '$') {
+                       return (str[j] == '\0') ? j : -1;
+               } else if (pattern[i] == '*') {
+                       i++;
+                       if (pattern[i] == '*') {
+                               i++;
+                               len = (int)strlen(str + j);
+                       } else {
+                               len = (int)strcspn(str + j, "/");
+                       }
+                       if (i == pattern_len) {
+                               return j + len;
+                       }
+                       do {
+                               res = match_prefix(pattern + i, pattern_len - 
i, str + j + len);
+                       } while (res == -1 && len-- > 0);
+                       return (res == -1) ? -1 : j + res + len;
+               } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
+                       return -1;
+               }
+       }
+       return j;
+}
+
+
+/* HTTP 1.1 assumes keep alive if "Connection:" header is not set
+ * This function must tolerate situations when connection info is not
+ * set up, for example if request parsing failed. */
+static int
+should_keep_alive(const struct mg_connection *conn)
+{
+       if (conn != NULL) {
+               const char *http_version = conn->request_info.http_version;
+               const char *header = mg_get_header(conn, "Connection");
+               if (conn->must_close || conn->status_code == 401
+                   || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], 
"yes") != 0
+                   || (header != NULL && !header_has_option(header, 
"keep-alive"))
+                   || (header == NULL && http_version
+                       && 0 != strcmp(http_version, "1.1"))) {
+                       return 0;
+               }
+               return 1;
+       }
+       return 0;
+}
+
+
+static int
+should_decode_url(const struct mg_connection *conn)
+{
+       if (!conn || !conn->ctx) {
+               return 0;
+       }
+
+       return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0);
+}
+
+
+static const char *
+suggest_connection_header(const struct mg_connection *conn)
+{
+       return should_keep_alive(conn) ? "keep-alive" : "close";
+}
+
+
+static int
+send_no_cache_header(struct mg_connection *conn)
+{
+       /* Send all current and obsolete cache opt-out directives. */
+       return mg_printf(conn,
+                        "Cache-Control: no-cache, no-store, "
+                        "must-revalidate, private, max-age=0\r\n"
+                        "Pragma: no-cache\r\n"
+                        "Expires: 0\r\n");
+}
+
+
+static int
+send_static_cache_header(struct mg_connection *conn)
+{
+#if !defined(NO_CACHING)
+       /* Read the server config to check how long a file may be cached.
+        * The configuration is in seconds. */
+       int max_age = atoi(conn->ctx->config[STATIC_FILE_MAX_AGE]);
+       if (max_age <= 0) {
+               /* 0 means "do not cache". All values <0 are reserved
+                * and may be used differently in the future. */
+               /* If a file should not be cached, do not only send
+                * max-age=0, but also pragmas and Expires headers. */
+               return send_no_cache_header(conn);
+       }
+
+       /* Use "Cache-Control: max-age" instead of "Expires" header.
+        * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
+       /* See also https://www.mnot.net/cache_docs/ */
+       /* According to RFC 2616, Section 14.21, caching times should not exceed
+        * one year. A year with 365 days corresponds to 31536000 seconds, a 
leap
+        * year to 31622400 seconds. For the moment, we just send whatever has
+        * been configured, still the behavior for >1 year should be considered
+        * as undefined. */
+       return mg_printf(conn, "Cache-Control: max-age=%u\r\n", 
(unsigned)max_age);
+#else  /* NO_CACHING */
+       return send_no_cache_header(conn);
+#endif /* !NO_CACHING */
+}
+
+
+static void handle_file_based_request(struct mg_connection *conn,
+                                      const char *path,
+                                      struct mg_file *filep);
+
+
+const char *
+mg_get_response_code_text(struct mg_connection *conn, int response_code)
+{
+       /* See IANA HTTP status code assignment:
+        * 
http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+        */
+
+       switch (response_code) {
+       /* RFC2616 Section 10.1 - Informational 1xx */
+       case 100:
+               return "Continue"; /* RFC2616 Section 10.1.1 */
+       case 101:
+               return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
+       case 102:
+               return "Processing"; /* RFC2518 Section 10.1 */
+
+       /* RFC2616 Section 10.2 - Successful 2xx */
+       case 200:
+               return "OK"; /* RFC2616 Section 10.2.1 */
+       case 201:
+               return "Created"; /* RFC2616 Section 10.2.2 */
+       case 202:
+               return "Accepted"; /* RFC2616 Section 10.2.3 */
+       case 203:
+               return "Non-Authoritative Information"; /* RFC2616 Section 
10.2.4 */
+       case 204:
+               return "No Content"; /* RFC2616 Section 10.2.5 */
+       case 205:
+               return "Reset Content"; /* RFC2616 Section 10.2.6 */
+       case 206:
+               return "Partial Content"; /* RFC2616 Section 10.2.7 */
+       case 207:
+               return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 
11.1 */
+       case 208:
+               return "Already Reported"; /* RFC5842 Section 7.1 */
+
+       case 226:
+               return "IM used"; /* RFC3229 Section 10.4.1 */
+
+       /* RFC2616 Section 10.3 - Redirection 3xx */
+       case 300:
+               return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
+       case 301:
+               return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
+       case 302:
+               return "Found"; /* RFC2616 Section 10.3.3 */
+       case 303:
+               return "See Other"; /* RFC2616 Section 10.3.4 */
+       case 304:
+               return "Not Modified"; /* RFC2616 Section 10.3.5 */
+       case 305:
+               return "Use Proxy"; /* RFC2616 Section 10.3.6 */
+       case 307:
+               return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
+       case 308:
+               return "Permanent Redirect"; /* RFC7238 Section 3 */
+
+       /* RFC2616 Section 10.4 - Client Error 4xx */
+       case 400:
+               return "Bad Request"; /* RFC2616 Section 10.4.1 */
+       case 401:
+               return "Unauthorized"; /* RFC2616 Section 10.4.2 */
+       case 402:
+               return "Payment Required"; /* RFC2616 Section 10.4.3 */
+       case 403:
+               return "Forbidden"; /* RFC2616 Section 10.4.4 */
+       case 404:
+               return "Not Found"; /* RFC2616 Section 10.4.5 */
+       case 405:
+               return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
+       case 406:
+               return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
+       case 407:
+               return "Proxy Authentication Required"; /* RFC2616 Section 
10.4.8 */
+       case 408:
+               return "Request Time-out"; /* RFC2616 Section 10.4.9 */
+       case 409:
+               return "Conflict"; /* RFC2616 Section 10.4.10 */
+       case 410:
+               return "Gone"; /* RFC2616 Section 10.4.11 */
+       case 411:
+               return "Length Required"; /* RFC2616 Section 10.4.12 */
+       case 412:
+               return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
+       case 413:
+               return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
+       case 414:
+               return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
+       case 415:
+               return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
+       case 416:
+               return "Requested range not satisfiable"; /* RFC2616 Section 
10.4.17 */
+       case 417:
+               return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
+
+       case 421:
+               return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
+       case 422:
+               return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
+                                                * Sect

<TRUNCATED>

Reply via email to