summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinhard Tartler <siretart@tauware.de>2018-06-15 12:21:15 -0400
committerReinhard Tartler <siretart@tauware.de>2018-06-18 15:05:48 -0400
commit64b588e5d7ffae27265b3e95e59dbf577fbfb0db (patch)
tree60f01b0b3344696b0e81fd654f49110256a3b54a
parentd1f3aaeda717076c1761f0aebb56681a5c4ce435 (diff)
Re-introduce patch that disables the chromcast functionality
Also drop the updated mongoose-to-6.11 patch. We will pick up that code on the next upstream release. This reverts commit 5d780999b6ee7a84d737fdb5dbc07ea9a25e4cde.
-rw-r--r--debian/patches/03-update-mongoose-to-6.11.patch12281
-rw-r--r--debian/patches/07-disable-chromecast.patch46
-rw-r--r--debian/patches/series2
3 files changed, 47 insertions, 12282 deletions
diff --git a/debian/patches/03-update-mongoose-to-6.11.patch b/debian/patches/03-update-mongoose-to-6.11.patch
deleted file mode 100644
index aa1d6c4..0000000
--- a/debian/patches/03-update-mongoose-to-6.11.patch
+++ /dev/null
@@ -1,12281 +0,0 @@
-Description: <short summary of the patch>
- TODO: Put a short summary on the line above and replace this paragraph
- with a longer explanation of this change. Complete the meta-information
- with other relevant fields (see below for details). To make it easier, the
- information below has been extracted from the changelog. Adjust it or drop
- it.
- .
- smplayer (18.4.0~ds0-1) UNRELEASED; urgency=medium
- .
- * New upstream release.
-Author: Mateusz Łukasik <mati75@linuxmint.pl>
-
----
-The information above should follow the Patch Tagging Guidelines, please
-checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
-are templates for supplementary fields that you might want to add:
-
-Origin: <vendor|upstream|other>, <url of original patch>
-Bug: <url in upstream bugtracker>
-Bug-Debian: https://bugs.debian.org/<bugnumber>
-Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
-Forwarded: <no|not-needed|url proving that it has been forwarded>
-Reviewed-By: <name and email of someone who approved the patch>
-Last-Update: 2018-05-17
-
---- smplayer-18.4.0~ds0.orig/webserver/mongoose.c
-+++ smplayer-18.4.0~ds0/webserver/mongoose.c
-@@ -1,6 +1,6 @@
- #include "mongoose.h"
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/internal.h"
-+#line 1 "mongoose/src/mg_internal.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -10,21 +10,7 @@
- #ifndef CS_MONGOOSE_SRC_INTERNAL_H_
- #define CS_MONGOOSE_SRC_INTERNAL_H_
-
--#ifndef MG_MALLOC
--#define MG_MALLOC malloc
--#endif
--
--#ifndef MG_CALLOC
--#define MG_CALLOC calloc
--#endif
--
--#ifndef MG_REALLOC
--#define MG_REALLOC realloc
--#endif
--
--#ifndef MG_FREE
--#define MG_FREE free
--#endif
-+/* Amalgamated: #include "common/mg_mem.h" */
-
- #ifndef MBUF_REALLOC
- #define MBUF_REALLOC MG_REALLOC
-@@ -48,9 +34,9 @@
- #define MG_DISABLE_PFS
- #endif
-
--/* Amalgamated: #include "mongoose/src/net.h" */
--/* Amalgamated: #include "mongoose/src/http.h" */
- /* Amalgamated: #include "common/cs_dbg.h" */
-+/* Amalgamated: #include "mg_http.h" */
-+/* Amalgamated: #include "mg_net.h" */
-
- #define MG_CTL_MSG_MESSAGE_SIZE 8192
-
-@@ -62,7 +48,8 @@ MG_INTERNAL struct mg_connection *mg_do_
- MG_INTERNAL int mg_parse_address(const char *str, union socket_address *sa,
- int *proto, char *host, size_t host_len);
- MG_INTERNAL void mg_call(struct mg_connection *nc,
-- mg_event_handler_t ev_handler, int ev, void *ev_data);
-+ mg_event_handler_t ev_handler, void *user_data, int ev,
-+ void *ev_data);
- void mg_forward(struct mg_connection *from, struct mg_connection *to);
- MG_INTERNAL void mg_add_conn(struct mg_mgr *mgr, struct mg_connection *c);
- MG_INTERNAL void mg_remove_conn(struct mg_connection *c);
-@@ -81,6 +68,10 @@ struct ctl_msg {
-
- #if MG_ENABLE_MQTT
- struct mg_mqtt_message;
-+
-+#define MG_MQTT_ERROR_INCOMPLETE_MSG -1
-+#define MG_MQTT_ERROR_MALFORMED_MSG -2
-+
- MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm);
- #endif
-
-@@ -110,11 +101,6 @@ MG_INTERNAL size_t mg_handle_chunked(str
- struct http_message *hm, char *buf,
- size_t blen);
-
--MG_INTERNAL int mg_http_common_url_parse(const char *url, const char *schema,
-- const char *schema_tls, int *use_ssl,
-- char **user, char **pass, char **addr,
-- int *port_i, const char **path);
--
- #if MG_ENABLE_FILESYSTEM
- MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
- const struct mg_serve_http_opts *opts,
-@@ -155,9 +141,11 @@ MG_INTERNAL void mg_handle_put(struct mg
- struct http_message *hm);
- #endif
- #if MG_ENABLE_HTTP_WEBSOCKET
--MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev, void *ev_data);
-+MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data));
- MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc,
-- const struct mg_str *key);
-+ const struct mg_str *key,
-+ struct http_message *);
- #endif
- #endif /* MG_ENABLE_HTTP */
-
-@@ -165,11 +153,6 @@ MG_INTERNAL int mg_get_errno(void);
-
- MG_INTERNAL void mg_close_conn(struct mg_connection *conn);
-
--MG_INTERNAL int mg_http_common_url_parse(const char *url, const char *schema,
-- const char *schema_tls, int *use_ssl,
-- char **user, char **pass, char **addr,
-- int *port_i, const char **path);
--
- #if MG_ENABLE_SNTP
- MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len,
- struct mg_sntp_message *msg);
-@@ -177,160 +160,43 @@ MG_INTERNAL int mg_sntp_parse_reply(cons
-
- #endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "common/cs_dbg.h"
-+#line 1 "common/mg_mem.h"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
-
--#ifndef CS_COMMON_CS_DBG_H_
--#define CS_COMMON_CS_DBG_H_
--
--/* Amalgamated: #include "common/platform.h" */
--
--#if CS_ENABLE_STDIO
--#include <stdio.h>
--#endif
--
--#ifndef CS_ENABLE_DEBUG
--#define CS_ENABLE_DEBUG 0
--#endif
--
--#ifndef CS_LOG_ENABLE_TS_DIFF
--#define CS_LOG_ENABLE_TS_DIFF 0
--#endif
-+#ifndef CS_COMMON_MG_MEM_H_
-+#define CS_COMMON_MG_MEM_H_
-
- #ifdef __cplusplus
- extern "C" {
--#endif /* __cplusplus */
--
--enum cs_log_level {
-- LL_NONE = -1,
-- LL_ERROR = 0,
-- LL_WARN = 1,
-- LL_INFO = 2,
-- LL_DEBUG = 3,
-- LL_VERBOSE_DEBUG = 4,
--
-- _LL_MIN = -2,
-- _LL_MAX = 5,
--};
--
--void cs_log_set_level(enum cs_log_level level);
--
--#if CS_ENABLE_STDIO
--
--void cs_log_set_file(FILE *file);
--extern enum cs_log_level cs_log_level;
--void cs_log_print_prefix(const char *func);
--void cs_log_printf(const char *fmt, ...);
--
--#define LOG(l, x) \
-- do { \
-- if (cs_log_level >= l) { \
-- cs_log_print_prefix(__func__); \
-- cs_log_printf x; \
-- } \
-- } while (0)
--
--#ifndef CS_NDEBUG
--
--#define DBG(x) \
-- do { \
-- if (cs_log_level >= LL_VERBOSE_DEBUG) { \
-- cs_log_print_prefix(__func__); \
-- cs_log_printf x; \
-- } \
-- } while (0)
--
--#else /* NDEBUG */
--
--#define DBG(x)
--
- #endif
-
--#else /* CS_ENABLE_STDIO */
--
--#define LOG(l, x)
--#define DBG(x)
--
--#endif
--
--#ifdef __cplusplus
--}
--#endif /* __cplusplus */
--
--#endif /* CS_COMMON_CS_DBG_H_ */
--#ifdef MG_MODULE_LINES
--#line 1 "common/cs_dbg.c"
-+#ifndef MG_MALLOC
-+#define MG_MALLOC malloc
- #endif
--/*
-- * Copyright (c) 2014-2016 Cesanta Software Limited
-- * All rights reserved
-- */
--
--/* Amalgamated: #include "common/cs_dbg.h" */
--
--#include <stdarg.h>
--#include <stdio.h>
--
--/* Amalgamated: #include "common/cs_time.h" */
-
--enum cs_log_level cs_log_level WEAK =
--#if CS_ENABLE_DEBUG
-- LL_VERBOSE_DEBUG;
--#else
-- LL_ERROR;
-+#ifndef MG_CALLOC
-+#define MG_CALLOC calloc
- #endif
-
--#if CS_ENABLE_STDIO
--
--FILE *cs_log_file WEAK = NULL;
--
--#if CS_LOG_ENABLE_TS_DIFF
--double cs_log_ts WEAK;
-+#ifndef MG_REALLOC
-+#define MG_REALLOC realloc
- #endif
-
--void cs_log_print_prefix(const char *func) WEAK;
--void cs_log_print_prefix(const char *func) {
-- if (cs_log_file == NULL) cs_log_file = stderr;
-- fprintf(cs_log_file, "%-20s ", func);
--#if CS_LOG_ENABLE_TS_DIFF
-- {
-- double now = cs_time();
-- fprintf(cs_log_file, "%7u ", (unsigned int) ((now - cs_log_ts) * 1000000));
-- cs_log_ts = now;
-- }
-+#ifndef MG_FREE
-+#define MG_FREE free
- #endif
--}
--
--void cs_log_printf(const char *fmt, ...) WEAK;
--void cs_log_printf(const char *fmt, ...) {
-- va_list ap;
-- va_start(ap, fmt);
-- vfprintf(cs_log_file, fmt, ap);
-- va_end(ap);
-- fputc('\n', cs_log_file);
-- fflush(cs_log_file);
--}
-
--void cs_log_set_file(FILE *file) WEAK;
--void cs_log_set_file(FILE *file) {
-- cs_log_file = file;
-+#ifdef __cplusplus
- }
--
--#endif /* CS_ENABLE_STDIO */
--
--void cs_log_set_level(enum cs_log_level level) WEAK;
--void cs_log_set_level(enum cs_log_level level) {
-- cs_log_level = level;
--#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO
-- cs_log_ts = cs_time();
- #endif
--}
-+
-+#endif /* CS_COMMON_MG_MEM_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "common/base64.c"
-+#line 1 "common/cs_base64.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -339,7 +205,7 @@ void cs_log_set_level(enum cs_log_level
-
- #ifndef EXCLUDE_COMMON
-
--/* Amalgamated: #include "common/base64.h" */
-+/* Amalgamated: #include "common/cs_base64.h" */
-
- #include <string.h>
-
-@@ -538,64 +404,301 @@ int cs_base64_decode(const unsigned char
-
- #endif /* EXCLUDE_COMMON */
- #ifdef MG_MODULE_LINES
--#line 1 "common/cs_dirent.h"
-+#line 1 "common/cs_dbg.h"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
-
--#ifndef CS_COMMON_CS_DIRENT_H_
--#define CS_COMMON_CS_DIRENT_H_
-+#ifndef CS_COMMON_CS_DBG_H_
-+#define CS_COMMON_CS_DBG_H_
-
- /* Amalgamated: #include "common/platform.h" */
-
-+#if CS_ENABLE_STDIO
-+#include <stdio.h>
-+#endif
-+
-+#ifndef CS_ENABLE_DEBUG
-+#define CS_ENABLE_DEBUG 0
-+#endif
-+
-+#ifndef CS_LOG_ENABLE_TS_DIFF
-+#define CS_LOG_ENABLE_TS_DIFF 0
-+#endif
-+
- #ifdef __cplusplus
- extern "C" {
- #endif /* __cplusplus */
-
--#ifndef CS_ENABLE_SPIFFS
--#define CS_ENABLE_SPIFFS 0
-+/*
-+ * Log level; `LL_INFO` is the default. Use `cs_log_set_level()` to change it.
-+ */
-+enum cs_log_level {
-+ LL_NONE = -1,
-+ LL_ERROR = 0,
-+ LL_WARN = 1,
-+ LL_INFO = 2,
-+ LL_DEBUG = 3,
-+ LL_VERBOSE_DEBUG = 4,
-+
-+ _LL_MIN = -2,
-+ _LL_MAX = 5,
-+};
-+
-+/*
-+ * Set max log level to print; messages with the level above the given one will
-+ * not be printed.
-+ */
-+void cs_log_set_level(enum cs_log_level level);
-+
-+/*
-+ * Set log filter. NULL (a default) logs everything.
-+ * Otherwise, function name and file name will be tested against the given
-+ * pattern, and only matching messages will be printed.
-+ *
-+ * For the pattern syntax, refer to `mg_match_prefix()` in `str_util.h`.
-+ *
-+ * Example:
-+ * ```c
-+ * void foo(void) {
-+ * LOG(LL_INFO, ("hello from foo"));
-+ * }
-+ *
-+ * void bar(void) {
-+ * LOG(LL_INFO, ("hello from bar"));
-+ * }
-+ *
-+ * void test(void) {
-+ * cs_log_set_filter(NULL);
-+ * foo();
-+ * bar();
-+ *
-+ * cs_log_set_filter("f*");
-+ * foo();
-+ * bar(); // Will NOT print anything
-+ *
-+ * cs_log_set_filter("bar");
-+ * foo(); // Will NOT print anything
-+ * bar();
-+ * }
-+ * ```
-+ */
-+void cs_log_set_filter(const char *pattern);
-+
-+/*
-+ * Helper function which prints message prefix with the given `level`, function
-+ * name `func` and `filename`. If message should be printed (accordingly to the
-+ * current log level and filter), prints the prefix and returns 1, otherwise
-+ * returns 0.
-+ *
-+ * Clients should typically just use `LOG()` macro.
-+ */
-+int cs_log_print_prefix(enum cs_log_level level, const char *func,
-+ const char *filename);
-+
-+extern enum cs_log_level cs_log_threshold;
-+
-+#if CS_ENABLE_STDIO
-+
-+/*
-+ * Set file to write logs into. If `NULL`, logs go to `stderr`.
-+ */
-+void cs_log_set_file(FILE *file);
-+
-+/*
-+ * Prints log to the current log file, appends "\n" in the end and flushes the
-+ * stream.
-+ */
-+void cs_log_printf(const char *fmt, ...)
-+#ifdef __GNUC__
-+ __attribute__((format(printf, 1, 2)))
- #endif
-+ ;
-
--#if CS_ENABLE_SPIFFS
-+/*
-+ * Format and print message `x` with the given level `l`. Example:
-+ *
-+ * ```c
-+ * LOG(LL_INFO, ("my info message: %d", 123));
-+ * LOG(LL_DEBUG, ("my debug message: %d", 123));
-+ * ```
-+ */
-+#define LOG(l, x) \
-+ do { \
-+ if (cs_log_print_prefix(l, __func__, __FILE__)) cs_log_printf x; \
-+ } while (0)
-
--#include <spiffs.h>
-+#ifndef CS_NDEBUG
-
--typedef struct {
-- spiffs_DIR dh;
-- struct spiffs_dirent de;
--} DIR;
-+/*
-+ * Shortcut for `LOG(LL_VERBOSE_DEBUG, (...))`
-+ */
-+#define DBG(x) LOG(LL_VERBOSE_DEBUG, x)
-
--#define d_name name
--#define dirent spiffs_dirent
-+#else /* NDEBUG */
-
--int rmdir(const char *path);
--int mkdir(const char *path, mode_t mode);
-+#define DBG(x)
-
- #endif
-
--#if defined(_WIN32)
--struct dirent {
-- char d_name[MAX_PATH];
--};
-+#else /* CS_ENABLE_STDIO */
-
--typedef struct DIR {
-- HANDLE handle;
-- WIN32_FIND_DATAW info;
-- struct dirent result;
--} DIR;
-+#define LOG(l, x)
-+#define DBG(x)
-+
-+#endif
-+
-+#ifdef __cplusplus
-+}
-+#endif /* __cplusplus */
-+
-+#endif /* CS_COMMON_CS_DBG_H_ */
-+#ifdef MG_MODULE_LINES
-+#line 1 "common/cs_dbg.c"
-+#endif
-+/*
-+ * Copyright (c) 2014-2016 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+/* Amalgamated: #include "common/cs_dbg.h" */
-+
-+#include <stdarg.h>
-+#include <stdio.h>
-+#include <string.h>
-+
-+/* Amalgamated: #include "common/cs_time.h" */
-+/* Amalgamated: #include "common/str_util.h" */
-+
-+enum cs_log_level cs_log_threshold WEAK =
-+#if CS_ENABLE_DEBUG
-+ LL_VERBOSE_DEBUG;
-+#else
-+ LL_ERROR;
-+#endif
-+
-+static char *s_filter_pattern = NULL;
-+static size_t s_filter_pattern_len;
-+
-+void cs_log_set_filter(const char *pattern) WEAK;
-+
-+#if CS_ENABLE_STDIO
-+
-+FILE *cs_log_file WEAK = NULL;
-+
-+#if CS_LOG_ENABLE_TS_DIFF
-+double cs_log_ts WEAK;
-+#endif
-+
-+enum cs_log_level cs_log_cur_msg_level WEAK = LL_NONE;
-+
-+void cs_log_set_filter(const char *pattern) {
-+ free(s_filter_pattern);
-+ if (pattern != NULL) {
-+ s_filter_pattern = strdup(pattern);
-+ s_filter_pattern_len = strlen(pattern);
-+ } else {
-+ s_filter_pattern = NULL;
-+ s_filter_pattern_len = 0;
-+ }
-+}
-+
-+int cs_log_print_prefix(enum cs_log_level, const char *, const char *) WEAK;
-+int cs_log_print_prefix(enum cs_log_level level, const char *func,
-+ const char *filename) {
-+ char prefix[21];
-+
-+ if (level > cs_log_threshold) return 0;
-+ if (s_filter_pattern != NULL &&
-+ mg_match_prefix(s_filter_pattern, s_filter_pattern_len, func) == 0 &&
-+ mg_match_prefix(s_filter_pattern, s_filter_pattern_len, filename) == 0) {
-+ return 0;
-+ }
-+
-+ strncpy(prefix, func, 20);
-+ prefix[20] = '\0';
-+ if (cs_log_file == NULL) cs_log_file = stderr;
-+ cs_log_cur_msg_level = level;
-+ fprintf(cs_log_file, "%-20s ", prefix);
-+#if CS_LOG_ENABLE_TS_DIFF
-+ {
-+ double now = cs_time();
-+ fprintf(cs_log_file, "%7u ", (unsigned int) ((now - cs_log_ts) * 1000000));
-+ cs_log_ts = now;
-+ }
-+#endif
-+ return 1;
-+}
-+
-+void cs_log_printf(const char *fmt, ...) WEAK;
-+void cs_log_printf(const char *fmt, ...) {
-+ va_list ap;
-+ va_start(ap, fmt);
-+ vfprintf(cs_log_file, fmt, ap);
-+ va_end(ap);
-+ fputc('\n', cs_log_file);
-+ fflush(cs_log_file);
-+ cs_log_cur_msg_level = LL_NONE;
-+}
-+
-+void cs_log_set_file(FILE *file) WEAK;
-+void cs_log_set_file(FILE *file) {
-+ cs_log_file = file;
-+}
-+
-+#else
-+
-+void cs_log_set_filter(const char *pattern) {
-+ (void) pattern;
-+}
-+
-+#endif /* CS_ENABLE_STDIO */
-+
-+void cs_log_set_level(enum cs_log_level level) WEAK;
-+void cs_log_set_level(enum cs_log_level level) {
-+ cs_log_threshold = level;
-+#if CS_LOG_ENABLE_TS_DIFF && CS_ENABLE_STDIO
-+ cs_log_ts = cs_time();
- #endif
-+}
-+#ifdef MG_MODULE_LINES
-+#line 1 "common/cs_dirent.h"
-+#endif
-+/*
-+ * Copyright (c) 2014-2016 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#ifndef CS_COMMON_CS_DIRENT_H_
-+#define CS_COMMON_CS_DIRENT_H_
-+
-+#include <limits.h>
-
--#if CS_ENABLE_SPIFFS
--extern spiffs *cs_spiffs_get_fs(void);
-+/* Amalgamated: #include "common/platform.h" */
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif /* __cplusplus */
-+
-+#ifdef CS_DEFINE_DIRENT
-+typedef struct { int dummy; } DIR;
-+
-+struct dirent {
-+ int d_ino;
-+#ifdef _WIN32
-+ char d_name[MAX_PATH];
-+#else
-+ /* TODO(rojer): Use PATH_MAX but make sure it's sane on every platform */
-+ char d_name[256];
- #endif
-+};
-
--#if defined(_WIN32) || CS_ENABLE_SPIFFS
- DIR *opendir(const char *dir_name);
- int closedir(DIR *dir);
- struct dirent *readdir(DIR *dir);
--#endif
-+#endif /* CS_DEFINE_DIRENT */
-
- #ifdef __cplusplus
- }
-@@ -612,6 +715,7 @@ struct dirent *readdir(DIR *dir);
-
- #ifndef EXCLUDE_COMMON
-
-+/* Amalgamated: #include "common/mg_mem.h" */
- /* Amalgamated: #include "common/cs_dirent.h" */
-
- /*
-@@ -619,23 +723,22 @@ struct dirent *readdir(DIR *dir);
- * for systems which do not natively support it (e.g. Windows).
- */
-
--#ifndef MG_FREE
--#define MG_FREE free
--#endif
--
--#ifndef MG_MALLOC
--#define MG_MALLOC malloc
--#endif
--
- #ifdef _WIN32
-+struct win32_dir {
-+ DIR d;
-+ HANDLE handle;
-+ WIN32_FIND_DATAW info;
-+ struct dirent result;
-+};
-+
- DIR *opendir(const char *name) {
-- DIR *dir = NULL;
-+ struct win32_dir *dir = NULL;
- wchar_t wpath[MAX_PATH];
- DWORD attrs;
-
- if (name == NULL) {
- SetLastError(ERROR_BAD_ARGUMENTS);
-- } else if ((dir = (DIR *) MG_MALLOC(sizeof(*dir))) == NULL) {
-+ } else if ((dir = (struct win32_dir *) MG_MALLOC(sizeof(*dir))) == NULL) {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- } else {
- to_wchar(name, wpath, ARRAY_SIZE(wpath));
-@@ -650,10 +753,11 @@ DIR *opendir(const char *name) {
- }
- }
-
-- return dir;
-+ return (DIR *) dir;
- }
-
--int closedir(DIR *dir) {
-+int closedir(DIR *d) {
-+ struct win32_dir *dir = (struct win32_dir *) d;
- int result = 0;
-
- if (dir != NULL) {
-@@ -668,10 +772,12 @@ int closedir(DIR *dir) {
- return result;
- }
-
--struct dirent *readdir(DIR *dir) {
-+struct dirent *readdir(DIR *d) {
-+ struct win32_dir *dir = (struct win32_dir *) d;
- struct dirent *result = NULL;
-
- if (dir) {
-+ memset(&dir->result, 0, sizeof(dir->result));
- if (dir->handle != INVALID_HANDLE_VALUE) {
- result = &dir->result;
- (void) WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName, -1,
-@@ -694,52 +800,6 @@ struct dirent *readdir(DIR *dir) {
- }
- #endif
-
--#if CS_ENABLE_SPIFFS
--
--DIR *opendir(const char *dir_name) {
-- DIR *dir = NULL;
-- spiffs *fs = cs_spiffs_get_fs();
--
-- if (dir_name == NULL || fs == NULL ||
-- (dir = (DIR *) calloc(1, sizeof(*dir))) == NULL) {
-- return NULL;
-- }
--
-- if (SPIFFS_opendir(fs, dir_name, &dir->dh) == NULL) {
-- free(dir);
-- dir = NULL;
-- }
--
-- return dir;
--}
--
--int closedir(DIR *dir) {
-- if (dir != NULL) {
-- SPIFFS_closedir(&dir->dh);
-- free(dir);
-- }
-- return 0;
--}
--
--struct dirent *readdir(DIR *dir) {
-- return SPIFFS_readdir(&dir->dh, &dir->de);
--}
--
--/* SPIFFs doesn't support directory operations */
--int rmdir(const char *path) {
-- (void) path;
-- return ENOTSUP;
--}
--
--int mkdir(const char *path, mode_t mode) {
-- (void) path;
-- (void) mode;
-- /* for spiffs supports only root dir, which comes from mongoose as '.' */
-- return (strlen(path) == 1 && *path == '.') ? 0 : ENOTSUP;
--}
--
--#endif /* CS_ENABLE_SPIFFS */
--
- #endif /* EXCLUDE_COMMON */
-
- /* ISO C requires a translation unit to contain at least one declaration */
-@@ -795,6 +855,41 @@ double cs_time(void) {
- #endif /* _WIN32 */
- return now;
- }
-+
-+double cs_timegm(const struct tm *tm) {
-+ /* Month-to-day offset for non-leap-years. */
-+ static const int month_day[12] = {0, 31, 59, 90, 120, 151,
-+ 181, 212, 243, 273, 304, 334};
-+
-+ /* Most of the calculation is easy; leap years are the main difficulty. */
-+ int month = tm->tm_mon % 12;
-+ int year = tm->tm_year + tm->tm_mon / 12;
-+ int year_for_leap;
-+ int64_t rt;
-+
-+ if (month < 0) { /* Negative values % 12 are still negative. */
-+ month += 12;
-+ --year;
-+ }
-+
-+ /* This is the number of Februaries since 1900. */
-+ year_for_leap = (month > 1) ? year + 1 : year;
-+
-+ rt =
-+ tm->tm_sec /* Seconds */
-+ +
-+ 60 *
-+ (tm->tm_min /* Minute = 60 seconds */
-+ +
-+ 60 * (tm->tm_hour /* Hour = 60 minutes */
-+ +
-+ 24 * (month_day[month] + tm->tm_mday - 1 /* Day = 24 hours */
-+ + 365 * (year - 70) /* Year = 365 days */
-+ + (year_for_leap - 69) / 4 /* Every 4 years is leap... */
-+ - (year_for_leap - 1) / 100 /* Except centuries... */
-+ + (year_for_leap + 299) / 400))); /* Except 400s. */
-+ return rt < 0 ? -1 : (double) rt;
-+}
- #ifdef MG_MODULE_LINES
- #line 1 "common/cs_endian.h"
- #endif
-@@ -806,6 +901,10 @@ double cs_time(void) {
- #ifndef CS_COMMON_CS_ENDIAN_H_
- #define CS_COMMON_CS_ENDIAN_H_
-
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
- /*
- * clang with std=-c99 uses __LITTLE_ENDIAN, by default
- * while for ex, RTOS gcc - LITTLE_ENDIAN, by default
-@@ -821,9 +920,13 @@ double cs_time(void) {
- #endif /* BIG_ENDIAN */
- #endif /* BYTE_ORDER */
-
-+#ifdef __cplusplus
-+}
-+#endif
-+
- #endif /* CS_COMMON_CS_ENDIAN_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "common/md5.c"
-+#line 1 "common/cs_md5.c"
- #endif
- /*
- * This code implements the MD5 message-digest algorithm.
-@@ -842,11 +945,11 @@ double cs_time(void) {
- * will fill a supplied 16-byte array with the digest.
- */
-
--/* Amalgamated: #include "common/md5.h" */
-+/* Amalgamated: #include "common/cs_md5.h" */
- /* Amalgamated: #include "common/str_util.h" */
-
- #if !defined(EXCLUDE_COMMON)
--#if !DISABLE_MD5
-+#if !CS_DISABLE_MD5
-
- /* Amalgamated: #include "common/cs_endian.h" */
-
-@@ -877,7 +980,7 @@ static void byteReverse(unsigned char *b
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
--void MD5_Init(MD5_CTX *ctx) {
-+void cs_md5_init(cs_md5_ctx *ctx) {
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
-@@ -887,7 +990,7 @@ void MD5_Init(MD5_CTX *ctx) {
- ctx->bits[1] = 0;
- }
-
--static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
-+static void cs_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
- register uint32_t a, b, c, d;
-
- a = buf[0];
-@@ -969,7 +1072,7 @@ static void MD5Transform(uint32_t buf[4]
- buf[3] += d;
- }
-
--void MD5_Update(MD5_CTX *ctx, const unsigned char *buf, size_t len) {
-+void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) {
- uint32_t t;
-
- t = ctx->bits[0];
-@@ -988,7 +1091,7 @@ void MD5_Update(MD5_CTX *ctx, const unsi
- }
- memcpy(p, buf, t);
- byteReverse(ctx->in, 16);
-- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-+ cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- buf += t;
- len -= t;
- }
-@@ -996,7 +1099,7 @@ void MD5_Update(MD5_CTX *ctx, const unsi
- while (len >= 64) {
- memcpy(ctx->in, buf, 64);
- byteReverse(ctx->in, 16);
-- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-+ cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- buf += 64;
- len -= 64;
- }
-@@ -1004,7 +1107,7 @@ void MD5_Update(MD5_CTX *ctx, const unsi
- memcpy(ctx->in, buf, len);
- }
-
--void MD5_Final(unsigned char digest[16], MD5_CTX *ctx) {
-+void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) {
- unsigned count;
- unsigned char *p;
- uint32_t *a;
-@@ -1017,7 +1120,7 @@ void MD5_Final(unsigned char digest[16],
- if (count < 8) {
- memset(p, 0, count);
- byteReverse(ctx->in, 16);
-- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-+ cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- memset(ctx->in, 0, 56);
- } else {
- memset(p, 0, count - 8);
-@@ -1028,235 +1131,23 @@ void MD5_Final(unsigned char digest[16],
- a[14] = ctx->bits[0];
- a[15] = ctx->bits[1];
-
-- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-+ cs_md5_transform(ctx->buf, (uint32_t *) ctx->in);
- byteReverse((unsigned char *) ctx->buf, 4);
- memcpy(digest, ctx->buf, 16);
- memset((char *) ctx, 0, sizeof(*ctx));
- }
--#endif /* DISABLE_MD5 */
--
--char *cs_md5(char buf[33], ...) {
-- unsigned char hash[16];
-- const unsigned char *p;
-- va_list ap;
-- MD5_CTX ctx;
--
-- MD5_Init(&ctx);
--
-- va_start(ap, buf);
-- while ((p = va_arg(ap, const unsigned char *) ) != NULL) {
-- size_t len = va_arg(ap, size_t);
-- MD5_Update(&ctx, p, len);
-- }
-- va_end(ap);
--
-- MD5_Final(hash, &ctx);
-- cs_to_hex(buf, hash, sizeof(hash));
--
-- return buf;
--}
--
--#endif /* EXCLUDE_COMMON */
--#ifdef MG_MODULE_LINES
--#line 1 "common/mbuf.c"
--#endif
--/*
-- * Copyright (c) 2014 Cesanta Software Limited
-- * All rights reserved
-- */
--
--#ifndef EXCLUDE_COMMON
--
--#include <assert.h>
--#include <string.h>
--/* Amalgamated: #include "common/mbuf.h" */
--
--#ifndef MBUF_REALLOC
--#define MBUF_REALLOC realloc
--#endif
--
--#ifndef MBUF_FREE
--#define MBUF_FREE free
--#endif
--
--void mbuf_init(struct mbuf *mbuf, size_t initial_size) WEAK;
--void mbuf_init(struct mbuf *mbuf, size_t initial_size) {
-- mbuf->len = mbuf->size = 0;
-- mbuf->buf = NULL;
-- mbuf_resize(mbuf, initial_size);
--}
--
--void mbuf_free(struct mbuf *mbuf) WEAK;
--void mbuf_free(struct mbuf *mbuf) {
-- if (mbuf->buf != NULL) {
-- MBUF_FREE(mbuf->buf);
-- mbuf_init(mbuf, 0);
-- }
--}
--
--void mbuf_resize(struct mbuf *a, size_t new_size) WEAK;
--void mbuf_resize(struct mbuf *a, size_t new_size) {
-- if (new_size > a->size || (new_size < a->size && new_size >= a->len)) {
-- char *buf = (char *) MBUF_REALLOC(a->buf, new_size);
-- /*
-- * In case realloc fails, there's not much we can do, except keep things as
-- * they are. Note that NULL is a valid return value from realloc when
-- * size == 0, but that is covered too.
-- */
-- if (buf == NULL && new_size != 0) return;
-- a->buf = buf;
-- a->size = new_size;
-- }
--}
--
--void mbuf_trim(struct mbuf *mbuf) WEAK;
--void mbuf_trim(struct mbuf *mbuf) {
-- mbuf_resize(mbuf, mbuf->len);
--}
--
--size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t) WEAK;
--size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t len) {
-- char *p = NULL;
--
-- assert(a != NULL);
-- assert(a->len <= a->size);
-- assert(off <= a->len);
--
-- /* check overflow */
-- if (~(size_t) 0 - (size_t) a->buf < len) return 0;
--
-- if (a->len + len <= a->size) {
-- memmove(a->buf + off + len, a->buf + off, a->len - off);
-- if (buf != NULL) {
-- memcpy(a->buf + off, buf, len);
-- }
-- a->len += len;
-- } else {
-- size_t new_size = (size_t)((a->len + len) * MBUF_SIZE_MULTIPLIER);
-- if ((p = (char *) MBUF_REALLOC(a->buf, new_size)) != NULL) {
-- a->buf = p;
-- memmove(a->buf + off + len, a->buf + off, a->len - off);
-- if (buf != NULL) memcpy(a->buf + off, buf, len);
-- a->len += len;
-- a->size = new_size;
-- } else {
-- len = 0;
-- }
-- }
--
-- return len;
--}
--
--size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) WEAK;
--size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) {
-- return mbuf_insert(a, a->len, buf, len);
--}
--
--void mbuf_remove(struct mbuf *mb, size_t n) WEAK;
--void mbuf_remove(struct mbuf *mb, size_t n) {
-- if (n > 0 && n <= mb->len) {
-- memmove(mb->buf, mb->buf + n, mb->len - n);
-- mb->len -= n;
-- }
--}
-
-+#endif /* CS_DISABLE_MD5 */
- #endif /* EXCLUDE_COMMON */
- #ifdef MG_MODULE_LINES
--#line 1 "common/mg_str.c"
--#endif
--/*
-- * Copyright (c) 2014-2016 Cesanta Software Limited
-- * All rights reserved
-- */
--
--/* Amalgamated: #include "common/mg_str.h" */
--
--#include <stdlib.h>
--#include <string.h>
--
--int mg_ncasecmp(const char *s1, const char *s2, size_t len) WEAK;
--
--struct mg_str mg_mk_str(const char *s) WEAK;
--struct mg_str mg_mk_str(const char *s) {
-- struct mg_str ret = {s, 0};
-- if (s != NULL) ret.len = strlen(s);
-- return ret;
--}
--
--struct mg_str mg_mk_str_n(const char *s, size_t len) WEAK;
--struct mg_str mg_mk_str_n(const char *s, size_t len) {
-- struct mg_str ret = {s, len};
-- return ret;
--}
--
--int mg_vcmp(const struct mg_str *str1, const char *str2) WEAK;
--int mg_vcmp(const struct mg_str *str1, const char *str2) {
-- size_t n2 = strlen(str2), n1 = str1->len;
-- int r = memcmp(str1->p, str2, (n1 < n2) ? n1 : n2);
-- if (r == 0) {
-- return n1 - n2;
-- }
-- return r;
--}
--
--int mg_vcasecmp(const struct mg_str *str1, const char *str2) WEAK;
--int mg_vcasecmp(const struct mg_str *str1, const char *str2) {
-- size_t n2 = strlen(str2), n1 = str1->len;
-- int r = mg_ncasecmp(str1->p, str2, (n1 < n2) ? n1 : n2);
-- if (r == 0) {
-- return n1 - n2;
-- }
-- return r;
--}
--
--struct mg_str mg_strdup(const struct mg_str s) WEAK;
--struct mg_str mg_strdup(const struct mg_str s) {
-- struct mg_str r = {NULL, 0};
-- if (s.len > 0 && s.p != NULL) {
-- r.p = (char *) malloc(s.len);
-- if (r.p != NULL) {
-- memcpy((char *) r.p, s.p, s.len);
-- r.len = s.len;
-- }
-- }
-- return r;
--}
--
--int mg_strcmp(const struct mg_str str1, const struct mg_str str2) WEAK;
--int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
-- size_t i = 0;
-- while (i < str1.len && i < str2.len) {
-- if (str1.p[i] < str2.p[i]) return -1;
-- if (str1.p[i] > str2.p[i]) return 1;
-- i++;
-- }
-- if (i < str1.len) return 1;
-- if (i < str2.len) return -1;
-- return 0;
--}
--
--int mg_strncmp(const struct mg_str, const struct mg_str, size_t n) WEAK;
--int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n) {
-- struct mg_str s1 = str1;
-- struct mg_str s2 = str2;
--
-- if (s1.len > n) {
-- s1.len = n;
-- }
-- if (s2.len > n) {
-- s2.len = n;
-- }
-- return mg_strcmp(s1, s2);
--}
--#ifdef MG_MODULE_LINES
--#line 1 "common/sha1.c"
-+#line 1 "common/cs_sha1.c"
- #endif
- /* Copyright(c) By Steve Reid <steve@edmweb.com> */
- /* 100% Public Domain */
-
--/* Amalgamated: #include "common/sha1.h" */
-+/* Amalgamated: #include "common/cs_sha1.h" */
-
--#if !DISABLE_SHA1 && !defined(EXCLUDE_COMMON)
-+#if !CS_DISABLE_SHA1 && !defined(EXCLUDE_COMMON)
-
- /* Amalgamated: #include "common/cs_endian.h" */
-
-@@ -1505,6 +1396,233 @@ void cs_hmac_sha1(const unsigned char *k
-
- #endif /* EXCLUDE_COMMON */
- #ifdef MG_MODULE_LINES
-+#line 1 "common/mbuf.c"
-+#endif
-+/*
-+ * Copyright (c) 2014 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#ifndef EXCLUDE_COMMON
-+
-+#include <assert.h>
-+#include <string.h>
-+/* Amalgamated: #include "common/mbuf.h" */
-+
-+#ifndef MBUF_REALLOC
-+#define MBUF_REALLOC realloc
-+#endif
-+
-+#ifndef MBUF_FREE
-+#define MBUF_FREE free
-+#endif
-+
-+void mbuf_init(struct mbuf *mbuf, size_t initial_size) WEAK;
-+void mbuf_init(struct mbuf *mbuf, size_t initial_size) {
-+ mbuf->len = mbuf->size = 0;
-+ mbuf->buf = NULL;
-+ mbuf_resize(mbuf, initial_size);
-+}
-+
-+void mbuf_free(struct mbuf *mbuf) WEAK;
-+void mbuf_free(struct mbuf *mbuf) {
-+ if (mbuf->buf != NULL) {
-+ MBUF_FREE(mbuf->buf);
-+ mbuf_init(mbuf, 0);
-+ }
-+}
-+
-+void mbuf_resize(struct mbuf *a, size_t new_size) WEAK;
-+void mbuf_resize(struct mbuf *a, size_t new_size) {
-+ if (new_size > a->size || (new_size < a->size && new_size >= a->len)) {
-+ char *buf = (char *) MBUF_REALLOC(a->buf, new_size);
-+ /*
-+ * In case realloc fails, there's not much we can do, except keep things as
-+ * they are. Note that NULL is a valid return value from realloc when
-+ * size == 0, but that is covered too.
-+ */
-+ if (buf == NULL && new_size != 0) return;
-+ a->buf = buf;
-+ a->size = new_size;
-+ }
-+}
-+
-+void mbuf_trim(struct mbuf *mbuf) WEAK;
-+void mbuf_trim(struct mbuf *mbuf) {
-+ mbuf_resize(mbuf, mbuf->len);
-+}
-+
-+size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t) WEAK;
-+size_t mbuf_insert(struct mbuf *a, size_t off, const void *buf, size_t len) {
-+ char *p = NULL;
-+
-+ assert(a != NULL);
-+ assert(a->len <= a->size);
-+ assert(off <= a->len);
-+
-+ /* check overflow */
-+ if (~(size_t) 0 - (size_t) a->buf < len) return 0;
-+
-+ if (a->len + len <= a->size) {
-+ memmove(a->buf + off + len, a->buf + off, a->len - off);
-+ if (buf != NULL) {
-+ memcpy(a->buf + off, buf, len);
-+ }
-+ a->len += len;
-+ } else {
-+ size_t new_size = (size_t)((a->len + len) * MBUF_SIZE_MULTIPLIER);
-+ if ((p = (char *) MBUF_REALLOC(a->buf, new_size)) != NULL) {
-+ a->buf = p;
-+ memmove(a->buf + off + len, a->buf + off, a->len - off);
-+ if (buf != NULL) memcpy(a->buf + off, buf, len);
-+ a->len += len;
-+ a->size = new_size;
-+ } else {
-+ len = 0;
-+ }
-+ }
-+
-+ return len;
-+}
-+
-+size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) WEAK;
-+size_t mbuf_append(struct mbuf *a, const void *buf, size_t len) {
-+ return mbuf_insert(a, a->len, buf, len);
-+}
-+
-+void mbuf_remove(struct mbuf *mb, size_t n) WEAK;
-+void mbuf_remove(struct mbuf *mb, size_t n) {
-+ if (n > 0 && n <= mb->len) {
-+ memmove(mb->buf, mb->buf + n, mb->len - n);
-+ mb->len -= n;
-+ }
-+}
-+
-+#endif /* EXCLUDE_COMMON */
-+#ifdef MG_MODULE_LINES
-+#line 1 "common/mg_str.c"
-+#endif
-+/*
-+ * Copyright (c) 2014-2016 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+/* Amalgamated: #include "common/mg_mem.h" */
-+/* Amalgamated: #include "common/mg_str.h" */
-+
-+#include <stdlib.h>
-+#include <string.h>
-+
-+int mg_ncasecmp(const char *s1, const char *s2, size_t len) WEAK;
-+
-+struct mg_str mg_mk_str(const char *s) WEAK;
-+struct mg_str mg_mk_str(const char *s) {
-+ struct mg_str ret = {s, 0};
-+ if (s != NULL) ret.len = strlen(s);
-+ return ret;
-+}
-+
-+struct mg_str mg_mk_str_n(const char *s, size_t len) WEAK;
-+struct mg_str mg_mk_str_n(const char *s, size_t len) {
-+ struct mg_str ret = {s, len};
-+ return ret;
-+}
-+
-+int mg_vcmp(const struct mg_str *str1, const char *str2) WEAK;
-+int mg_vcmp(const struct mg_str *str1, const char *str2) {
-+ size_t n2 = strlen(str2), n1 = str1->len;
-+ int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);
-+ if (r == 0) {
-+ return n1 - n2;
-+ }
-+ return r;
-+}
-+
-+int mg_vcasecmp(const struct mg_str *str1, const char *str2) WEAK;
-+int mg_vcasecmp(const struct mg_str *str1, const char *str2) {
-+ size_t n2 = strlen(str2), n1 = str1->len;
-+ int r = mg_ncasecmp(str1->p, str2, (n1 < n2) ? n1 : n2);
-+ if (r == 0) {
-+ return n1 - n2;
-+ }
-+ return r;
-+}
-+
-+static struct mg_str mg_strdup_common(const struct mg_str s,
-+ int nul_terminate) {
-+ struct mg_str r = {NULL, 0};
-+ if (s.len > 0 && s.p != NULL) {
-+ char *sc = (char *) MG_MALLOC(s.len + (nul_terminate ? 1 : 0));
-+ if (sc != NULL) {
-+ memcpy(sc, s.p, s.len);
-+ if (nul_terminate) sc[s.len] = '\0';
-+ r.p = sc;
-+ r.len = s.len;
-+ }
-+ }
-+ return r;
-+}
-+
-+struct mg_str mg_strdup(const struct mg_str s) WEAK;
-+struct mg_str mg_strdup(const struct mg_str s) {
-+ return mg_strdup_common(s, 0 /* NUL-terminate */);
-+}
-+
-+struct mg_str mg_strdup_nul(const struct mg_str s) WEAK;
-+struct mg_str mg_strdup_nul(const struct mg_str s) {
-+ return mg_strdup_common(s, 1 /* NUL-terminate */);
-+}
-+
-+const char *mg_strchr(const struct mg_str s, int c) WEAK;
-+const char *mg_strchr(const struct mg_str s, int c) {
-+ size_t i;
-+ for (i = 0; i < s.len; i++) {
-+ if (s.p[i] == c) return &s.p[i];
-+ }
-+ return NULL;
-+}
-+
-+int mg_strcmp(const struct mg_str str1, const struct mg_str str2) WEAK;
-+int mg_strcmp(const struct mg_str str1, const struct mg_str str2) {
-+ size_t i = 0;
-+ while (i < str1.len && i < str2.len) {
-+ if (str1.p[i] < str2.p[i]) return -1;
-+ if (str1.p[i] > str2.p[i]) return 1;
-+ i++;
-+ }
-+ if (i < str1.len) return 1;
-+ if (i < str2.len) return -1;
-+ return 0;
-+}
-+
-+int mg_strncmp(const struct mg_str, const struct mg_str, size_t n) WEAK;
-+int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n) {
-+ struct mg_str s1 = str1;
-+ struct mg_str s2 = str2;
-+
-+ if (s1.len > n) {
-+ s1.len = n;
-+ }
-+ if (s2.len > n) {
-+ s2.len = n;
-+ }
-+ return mg_strcmp(s1, s2);
-+}
-+
-+const char *mg_strstr(const struct mg_str haystack,
-+ const struct mg_str needle) WEAK;
-+const char *mg_strstr(const struct mg_str haystack,
-+ const struct mg_str needle) {
-+ size_t i;
-+ if (needle.len > haystack.len) return NULL;
-+ for (i = 0; i <= haystack.len - needle.len; i++) {
-+ if (memcmp(haystack.p + i, needle.p, needle.len) == 0) {
-+ return haystack.p + i;
-+ }
-+ }
-+ return NULL;
-+}
-+#ifdef MG_MODULE_LINES
- #line 1 "common/str_util.c"
- #endif
- /*
-@@ -1514,20 +1632,15 @@ void cs_hmac_sha1(const unsigned char *k
-
- #ifndef EXCLUDE_COMMON
-
--/* Amalgamated: #include "common/platform.h" */
- /* Amalgamated: #include "common/str_util.h" */
-+/* Amalgamated: #include "common/mg_mem.h" */
-+/* Amalgamated: #include "common/platform.h" */
-
- #ifndef C_DISABLE_BUILTIN_SNPRINTF
- #define C_DISABLE_BUILTIN_SNPRINTF 0
- #endif
-
--#ifndef MG_MALLOC
--#define MG_MALLOC malloc
--#endif
--
--#ifndef MG_FREE
--#define MG_FREE free
--#endif
-+/* Amalgamated: #include "common/mg_mem.h" */
-
- size_t c_strnlen(const char *s, size_t maxlen) WEAK;
- size_t c_strnlen(const char *s, size_t maxlen) {
-@@ -1793,7 +1906,7 @@ const char *c_strnstr(const char *s, con
- char *strdup(const char *src) WEAK;
- char *strdup(const char *src) {
- size_t len = strlen(src) + 1;
-- char *ret = malloc(len);
-+ char *ret = MG_MALLOC(len);
- if (ret != NULL) {
- strcpy(ret, src);
- }
-@@ -1898,12 +2011,24 @@ int mg_avprintf(char **buf, size_t size,
- *buf = NULL; /* LCOV_EXCL_START */
- while (len < 0) {
- MG_FREE(*buf);
-+ if (size == 0) {
-+ size = 5;
-+ }
- size *= 2;
-- if ((*buf = (char *) MG_MALLOC(size)) == NULL) break;
-+ if ((*buf = (char *) MG_MALLOC(size)) == NULL) {
-+ len = -1;
-+ break;
-+ }
- va_copy(ap_copy, ap);
-- len = vsnprintf(*buf, size, fmt, ap_copy);
-+ len = vsnprintf(*buf, size - 1, fmt, ap_copy);
- va_end(ap_copy);
- }
-+
-+ /*
-+ * Microsoft version of vsnprintf() is not always null-terminated, so put
-+ * the terminator manually
-+ */
-+ (*buf)[len] = 0;
- /* LCOV_EXCL_STOP */
- } else if (len >= (int) size) {
- /* Standard-compliant code path. Allocate a buffer that is large enough. */
-@@ -1919,95 +2044,109 @@ int mg_avprintf(char **buf, size_t size,
- return len;
- }
-
--#endif /* EXCLUDE_COMMON */
--#ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/tun.h"
--#endif
--/*
-- * Copyright (c) 2014-2016 Cesanta Software Limited
-- * All rights reserved
-- */
--
--#ifndef CS_MONGOOSE_SRC_TUN_H_
--#define CS_MONGOOSE_SRC_TUN_H_
--
--#if MG_ENABLE_TUN
--
--/* Amalgamated: #include "mongoose/src/net.h" */
--/* Amalgamated: #include "common/mg_str.h" */
--
--#ifndef MG_TUN_RECONNECT_INTERVAL
--#define MG_TUN_RECONNECT_INTERVAL 1
--#endif
--
--#define MG_TUN_PROTO_NAME "mg_tun"
--
--#define MG_TUN_DATA_FRAME 0x0
--#define MG_TUN_F_END_STREAM 0x1
--
--/*
-- * MG TUN frame format is loosely based on HTTP/2.
-- * However since the communication happens via WebSocket
-- * there is no need to encode the frame length, since that's
-- * solved by WebSocket framing.
-- *
-- * TODO(mkm): Detailed description of the protocol.
-- */
--struct mg_tun_frame {
-- uint8_t type;
-- uint8_t flags;
-- uint32_t stream_id; /* opaque stream identifier */
-- struct mg_str body;
--};
--
--struct mg_tun_ssl_opts {
--#if MG_ENABLE_SSL
-- const char *ssl_cert;
-- const char *ssl_key;
-- const char *ssl_ca_cert;
--#else
-- int dummy; /* some compilers don't like empty structs */
--#endif
--};
--
--struct mg_tun_client {
-- struct mg_mgr *mgr;
-- struct mg_iface *iface;
-- const char *disp_url;
-- struct mg_tun_ssl_opts ssl;
--
-- uint32_t last_stream_id; /* stream id of most recently accepted connection */
-+const char *mg_next_comma_list_entry(const char *, struct mg_str *,
-+ struct mg_str *) WEAK;
-+const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
-+ struct mg_str *eq_val) {
-+ struct mg_str ret = mg_next_comma_list_entry_n(mg_mk_str(list), val, eq_val);
-+ return ret.p;
-+}
-
-- struct mg_connection *disp;
-- struct mg_connection *listener;
-- struct mg_connection *reconnect;
--};
-+struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
-+ struct mg_str *eq_val) WEAK;
-+struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
-+ struct mg_str *eq_val) {
-+ if (list.len == 0) {
-+ /* End of the list */
-+ list = mg_mk_str(NULL);
-+ } else {
-+ const char *chr = NULL;
-+ *val = list;
-
--#ifdef __cplusplus
--extern "C" {
--#endif /* __cplusplus */
-+ if ((chr = mg_strchr(*val, ',')) != NULL) {
-+ /* Comma found. Store length and shift the list ptr */
-+ val->len = chr - val->p;
-+ chr++;
-+ list.len -= (chr - list.p);
-+ list.p = chr;
-+ } else {
-+ /* This value is the last one */
-+ list = mg_mk_str_n(list.p + list.len, 0);
-+ }
-
--struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr,
-- const char *dispatcher,
-- mg_event_handler_t handler,
-- struct mg_bind_opts opts);
-+ 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->p = (const char *) memchr(val->p, '=', val->len);
-+ if (eq_val->p != NULL) {
-+ eq_val->p++; /* Skip over '=' character */
-+ eq_val->len = val->p + val->len - eq_val->p;
-+ val->len = (eq_val->p - val->p) - 1;
-+ }
-+ }
-+ }
-
--int mg_tun_parse_frame(void *data, size_t len, struct mg_tun_frame *frame);
-+ return list;
-+}
-
--void mg_tun_send_frame(struct mg_connection *ws, uint32_t stream_id,
-- uint8_t type, uint8_t flags, struct mg_str msg);
-+size_t mg_match_prefix_n(const struct mg_str, const struct mg_str) WEAK;
-+size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {
-+ const char *or_str;
-+ size_t res = 0, len = 0, i = 0, j = 0;
-
--void mg_tun_destroy_client(struct mg_tun_client *client);
-+ if ((or_str = (const char *) memchr(pattern.p, '|', pattern.len)) != NULL ||
-+ (or_str = (const char *) memchr(pattern.p, ',', pattern.len)) != NULL) {
-+ struct mg_str pstr = {pattern.p, (size_t)(or_str - pattern.p)};
-+ res = mg_match_prefix_n(pstr, str);
-+ if (res > 0) return res;
-+ pstr.p = or_str + 1;
-+ pstr.len = (pattern.p + pattern.len) - (or_str + 1);
-+ return mg_match_prefix_n(pstr, str);
-+ }
-
--#ifdef __cplusplus
-+ for (; i < pattern.len && j < str.len; i++, j++) {
-+ if (pattern.p[i] == '?') {
-+ continue;
-+ } else if (pattern.p[i] == '*') {
-+ i++;
-+ if (i < pattern.len && pattern.p[i] == '*') {
-+ i++;
-+ len = str.len - j;
-+ } else {
-+ len = 0;
-+ while (j + len < str.len && str.p[j + len] != '/') len++;
-+ }
-+ if (i == pattern.len || (pattern.p[i] == '$' && i == pattern.len - 1))
-+ return j + len;
-+ do {
-+ const struct mg_str pstr = {pattern.p + i, pattern.len - i};
-+ const struct mg_str sstr = {str.p + j + len, str.len - j - len};
-+ res = mg_match_prefix_n(pstr, sstr);
-+ } while (res == 0 && len != 0 && len-- > 0);
-+ return res == 0 ? 0 : j + res + len;
-+ } else if (str_util_lowercase(&pattern.p[i]) !=
-+ str_util_lowercase(&str.p[j])) {
-+ break;
-+ }
-+ }
-+ if (i < pattern.len && pattern.p[i] == '$') {
-+ return j == str.len ? str.len : 0;
-+ }
-+ return i == pattern.len ? j : 0;
- }
--#endif /* __cplusplus */
-
--#endif /* MG_ENABLE_TUN */
-+size_t mg_match_prefix(const char *, int, const char *) WEAK;
-+size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
-+ const struct mg_str pstr = {pattern, (size_t) pattern_len};
-+ struct mg_str s = {str, 0};
-+ if (str != NULL) s.len = strlen(str);
-+ return mg_match_prefix_n(pstr, s);
-+}
-
--#endif /* CS_MONGOOSE_SRC_TUN_H_ */
-+#endif /* EXCLUDE_COMMON */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net.c"
-+#line 1 "mongoose/src/mg_net.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -2028,11 +2167,10 @@ void mg_tun_destroy_client(struct mg_tun
- */
-
- /* Amalgamated: #include "common/cs_time.h" */
--/* Amalgamated: #include "mongoose/src/dns.h" */
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/resolv.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
--/* Amalgamated: #include "mongoose/src/tun.h" */
-+/* Amalgamated: #include "mg_dns.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_resolv.h" */
-+/* Amalgamated: #include "mg_util.h" */
-
- #define MG_MAX_HOST_LEN 200
-
-@@ -2069,11 +2207,15 @@ MG_INTERNAL void mg_remove_conn(struct m
- if (conn->prev == NULL) conn->mgr->active_connections = conn->next;
- if (conn->prev) conn->prev->next = conn->next;
- if (conn->next) conn->next->prev = conn->prev;
-+ conn->prev = conn->next = NULL;
- conn->iface->vtable->remove_conn(conn);
- }
-
- MG_INTERNAL void mg_call(struct mg_connection *nc,
-- mg_event_handler_t ev_handler, int ev, void *ev_data) {
-+ mg_event_handler_t ev_handler, void *user_data, int ev,
-+ void *ev_data) {
-+ static int nesting_level = 0;
-+ nesting_level++;
- if (ev_handler == NULL) {
- /*
- * If protocol handler is specified, call it. Otherwise, call user-specified
-@@ -2088,29 +2230,25 @@ MG_INTERNAL void mg_call(struct mg_conne
- }
-
- #if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP
-- /* LCOV_EXCL_START */
-- if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL &&
-+ if (nc->mgr->hexdump_file != NULL && ev != MG_EV_POLL && ev != MG_EV_RECV &&
- ev != MG_EV_SEND /* handled separately */) {
-- if (ev == MG_EV_RECV) {
-- mg_hexdump_connection(nc, nc->mgr->hexdump_file, nc->recv_mbuf.buf,
-- *(int *) ev_data, ev);
-- } else {
-- mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev);
-- }
-+ mg_hexdump_connection(nc, nc->mgr->hexdump_file, NULL, 0, ev);
- }
--/* LCOV_EXCL_STOP */
- #endif
- if (ev_handler != NULL) {
- unsigned long flags_before = nc->flags;
- size_t recv_mbuf_before = nc->recv_mbuf.len, recved;
-- ev_handler(nc, ev, ev_data);
-+ ev_handler(nc, ev, ev_data MG_UD_ARG(user_data));
- recved = (recv_mbuf_before - nc->recv_mbuf.len);
- /* Prevent user handler from fiddling with system flags. */
- if (ev_handler == nc->handler && nc->flags != flags_before) {
- nc->flags = (flags_before & ~_MG_CALLBACK_MODIFIABLE_FLAGS_MASK) |
- (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK);
- }
-- if (recved > 0 && !(nc->flags & MG_F_UDP)) {
-+ /* It's important to not double-count recved bytes, and since mg_call can be
-+ * called recursively (e.g. proto_handler invokes user handler), we keep
-+ * track of recursion and only report received bytes at the top level. */
-+ if (nesting_level == 1 && recved > 0 && !(nc->flags & MG_F_UDP)) {
- nc->iface->vtable->recved(nc, recved);
- }
- }
-@@ -2119,29 +2257,27 @@ MG_INTERNAL void mg_call(struct mg_conne
- ev_handler == nc->handler ? "user" : "proto", nc->flags,
- (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
- }
-+ nesting_level--;
-+#if !MG_ENABLE_CALLBACK_USERDATA
-+ (void) user_data;
-+#endif
- }
-
- void mg_if_timer(struct mg_connection *c, double now) {
- if (c->ev_timer_time > 0 && now >= c->ev_timer_time) {
- double old_value = c->ev_timer_time;
-- mg_call(c, NULL, MG_EV_TIMER, &now);
-- /*
-- * To prevent timer firing all the time, reset the timer after delivery.
-- * However, in case user sets it to new value, do not reset.
-- */
-- if (c->ev_timer_time == old_value) {
-- c->ev_timer_time = 0;
-- }
-+ c->ev_timer_time = 0;
-+ mg_call(c, NULL, c->user_data, MG_EV_TIMER, &old_value);
- }
- }
-
- void mg_if_poll(struct mg_connection *nc, time_t now) {
- if (!(nc->flags & MG_F_SSL) || (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) {
-- mg_call(nc, NULL, MG_EV_POLL, &now);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_POLL, &now);
- }
- }
-
--static void mg_destroy_conn(struct mg_connection *conn, int destroy_if) {
-+void mg_destroy_conn(struct mg_connection *conn, int destroy_if) {
- if (destroy_if) conn->iface->vtable->destroy_conn(conn);
- if (conn->proto_data != NULL && conn->proto_data_destructor != NULL) {
- conn->proto_data_destructor(conn->proto_data);
-@@ -2158,9 +2294,14 @@ static void mg_destroy_conn(struct mg_co
-
- void mg_close_conn(struct mg_connection *conn) {
- DBG(("%p %lu %d", conn, conn->flags, conn->sock));
-+#if MG_ENABLE_SSL
-+ if (conn->flags & MG_F_SSL_HANDSHAKE_DONE) {
-+ mg_ssl_if_conn_close_notify(conn);
-+ }
-+#endif
- mg_remove_conn(conn);
- conn->iface->vtable->destroy_conn(conn);
-- mg_call(conn, NULL, MG_EV_CLOSE, NULL);
-+ mg_call(conn, NULL, conn->user_data, MG_EV_CLOSE, NULL);
- mg_destroy_conn(conn, 0 /* destroy_if */);
- }
-
-@@ -2215,36 +2356,13 @@ void mg_mgr_init_opt(struct mg_mgr *m, v
- m->ifaces[i]->vtable->init(m->ifaces[i]);
- }
- }
-+ if (opts.nameserver != NULL) {
-+ m->nameserver = strdup(opts.nameserver);
-+ }
- DBG(("=================================="));
- DBG(("init mgr=%p", m));
- }
-
--#if MG_ENABLE_JAVASCRIPT
--static enum v7_err mg_send_js(struct v7 *v7, v7_val_t *res) {
-- v7_val_t arg0 = v7_arg(v7, 0);
-- v7_val_t arg1 = v7_arg(v7, 1);
-- struct mg_connection *c = (struct mg_connection *) v7_get_ptr(v7, arg0);
-- size_t len = 0;
--
-- if (v7_is_string(arg1)) {
-- const char *data = v7_get_string(v7, &arg1, &len);
-- mg_send(c, data, len);
-- }
--
-- *res = v7_mk_number(v7, len);
--
-- return V7_OK;
--}
--
--enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7,
-- const char *init_file_name) {
-- v7_val_t v;
-- m->v7 = v7;
-- v7_set_method(v7, v7_get_global(v7), "mg_send", mg_send_js);
-- return v7_exec_file(v7, init_file_name, &v);
--}
--#endif
--
- void mg_mgr_free(struct mg_mgr *m) {
- struct mg_connection *conn, *tmp_conn;
-
-@@ -2272,6 +2390,8 @@ void mg_mgr_free(struct mg_mgr *m) {
- }
- MG_FREE(m->ifaces);
- }
-+
-+ MG_FREE((char *) m->nameserver);
- }
-
- time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) {
-@@ -2397,7 +2517,7 @@ MG_INTERNAL struct mg_connection *mg_cre
- * Address format: [PROTO://][HOST]:PORT
- *
- * HOST could be IPv4/IPv6 address or a host name.
-- * `host` is a destination buffer to hold parsed HOST part. Shoud be at least
-+ * `host` is a destination buffer to hold parsed HOST part. Should be at least
- * MG_MAX_HOST_LEN bytes long.
- * `proto` is a returned socket type, either SOCK_STREAM or SOCK_DGRAM
- *
-@@ -2504,7 +2624,7 @@ void mg_if_accept_tcp_cb(struct mg_conne
- size_t sa_len) {
- (void) sa_len;
- nc->sa = *sa;
-- mg_call(nc, NULL, MG_EV_ACCEPT, &nc->sa);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa);
- }
-
- void mg_send(struct mg_connection *nc, const void *buf, int len) {
-@@ -2514,23 +2634,35 @@ void mg_send(struct mg_connection *nc, c
- } else {
- nc->iface->vtable->tcp_send(nc, buf, len);
- }
-+}
-+
-+void mg_if_sent_cb(struct mg_connection *nc, int num_sent) {
-+ DBG(("%p %d", nc, num_sent));
- #if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP
- if (nc->mgr && nc->mgr->hexdump_file != NULL) {
-- mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_SEND);
-+ char *buf = nc->send_mbuf.buf;
-+ mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, num_sent, MG_EV_SEND);
- }
- #endif
--}
--
--void mg_if_sent_cb(struct mg_connection *nc, int num_sent) {
- if (num_sent < 0) {
- nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ } else {
-+ mbuf_remove(&nc->send_mbuf, num_sent);
-+ mbuf_trim(&nc->send_mbuf);
- }
-- mg_call(nc, NULL, MG_EV_SEND, &num_sent);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_SEND, &num_sent);
- }
-
- MG_INTERNAL void mg_recv_common(struct mg_connection *nc, void *buf, int len,
- int own) {
- DBG(("%p %d %u", nc, len, (unsigned int) nc->recv_mbuf.len));
-+
-+#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP
-+ if (nc->mgr && nc->mgr->hexdump_file != NULL) {
-+ mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, len, MG_EV_RECV);
-+ }
-+#endif
-+
- if (nc->flags & MG_F_CLOSE_IMMEDIATELY) {
- DBG(("%p discarded %d bytes", nc, len));
- /*
-@@ -2554,7 +2686,7 @@ MG_INTERNAL void mg_recv_common(struct m
- mbuf_append(&nc->recv_mbuf, buf, len);
- MG_FREE(buf);
- }
-- mg_call(nc, NULL, MG_EV_RECV, &len);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &len);
- }
-
- void mg_if_recv_tcp_cb(struct mg_connection *nc, void *buf, int len, int own) {
-@@ -2602,7 +2734,7 @@ void mg_if_recv_udp_cb(struct mg_connect
- */
- nc->flags |= MG_F_SEND_AND_CLOSE;
- mg_add_conn(lc->mgr, nc);
-- mg_call(nc, NULL, MG_EV_ACCEPT, &nc->sa);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_ACCEPT, &nc->sa);
- } else {
- DBG(("OOM"));
- /* No return here, we still need to drop on the floor */
-@@ -2614,7 +2746,6 @@ void mg_if_recv_udp_cb(struct mg_connect
- } else {
- /* Drop on the floor. */
- MG_FREE(buf);
-- nc->iface->vtable->recved(nc, len);
- }
- }
-
-@@ -2646,7 +2777,7 @@ void mg_if_connect_cb(struct mg_connecti
- if (err != 0) {
- nc->flags |= MG_F_CLOSE_IMMEDIATELY;
- }
-- mg_call(nc, NULL, MG_EV_CONNECT, &err);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &err);
- }
-
- #if MG_ENABLE_ASYNC_RESOLVER
-@@ -2684,27 +2815,29 @@ static void resolve_cb(struct mg_dns_mes
-
- if (e == MG_RESOLVE_TIMEOUT) {
- double now = mg_time();
-- mg_call(nc, NULL, MG_EV_TIMER, &now);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_TIMER, &now);
- }
-
- /*
- * If we get there was no MG_DNS_A_RECORD in the answer
- */
-- mg_call(nc, NULL, MG_EV_CONNECT, &failure);
-- mg_call(nc, NULL, MG_EV_CLOSE, NULL);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_CONNECT, &failure);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_CLOSE, NULL);
- mg_destroy_conn(nc, 1 /* destroy_if */);
- }
- #endif
-
- struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
-- mg_event_handler_t callback) {
-+ MG_CB(mg_event_handler_t callback,
-+ void *user_data)) {
- struct mg_connect_opts opts;
- memset(&opts, 0, sizeof(opts));
-- return mg_connect_opt(mgr, address, callback, opts);
-+ return mg_connect_opt(mgr, address, MG_CB(callback, user_data), opts);
- }
-
- struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
-- mg_event_handler_t callback,
-+ MG_CB(mg_event_handler_t callback,
-+ void *user_data),
- struct mg_connect_opts opts) {
- struct mg_connection *nc = NULL;
- int proto, rc;
-@@ -2727,14 +2860,19 @@ struct mg_connection *mg_connect_opt(str
-
- nc->flags |= opts.flags & _MG_ALLOWED_CONNECT_FLAGS_MASK;
- nc->flags |= (proto == SOCK_DGRAM) ? MG_F_UDP : 0;
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ nc->user_data = user_data;
-+#else
- nc->user_data = opts.user_data;
-+#endif
-
- #if MG_ENABLE_SSL
- DBG(("%p %s %s,%s,%s", nc, address, (opts.ssl_cert ? opts.ssl_cert : "-"),
- (opts.ssl_key ? opts.ssl_key : "-"),
- (opts.ssl_ca_cert ? opts.ssl_ca_cert : "-")));
-
-- if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL) {
-+ if (opts.ssl_cert != NULL || opts.ssl_ca_cert != NULL ||
-+ opts.ssl_psk_identity != NULL) {
- const char *err_msg = NULL;
- struct mg_ssl_if_conn_params params;
- if (nc->flags & MG_F_UDP) {
-@@ -2746,6 +2884,9 @@ struct mg_connection *mg_connect_opt(str
- params.cert = opts.ssl_cert;
- params.key = opts.ssl_key;
- params.ca_cert = opts.ssl_ca_cert;
-+ params.cipher_suites = opts.ssl_cipher_suites;
-+ params.psk_identity = opts.ssl_psk_identity;
-+ params.psk_key = opts.ssl_psk_key;
- if (opts.ssl_ca_cert != NULL) {
- if (opts.ssl_server_name != NULL) {
- if (strcmp(opts.ssl_server_name, "*") != 0) {
-@@ -2774,6 +2915,7 @@ struct mg_connection *mg_connect_opt(str
- struct mg_resolve_async_opts o;
- memset(&o, 0, sizeof(o));
- o.dns_conn = &dns_conn;
-+ o.nameserver = opts.nameserver;
- if (mg_resolve_async_opt(nc->mgr, host, MG_DNS_A_RECORD, resolve_cb, nc,
- o) != 0) {
- MG_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup");
-@@ -2795,14 +2937,16 @@ struct mg_connection *mg_connect_opt(str
- }
-
- struct mg_connection *mg_bind(struct mg_mgr *srv, const char *address,
-- mg_event_handler_t event_handler) {
-+ MG_CB(mg_event_handler_t event_handler,
-+ void *user_data)) {
- struct mg_bind_opts opts;
- memset(&opts, 0, sizeof(opts));
-- return mg_bind_opt(srv, address, event_handler, opts);
-+ return mg_bind_opt(srv, address, MG_CB(event_handler, user_data), opts);
- }
-
- struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
-- mg_event_handler_t callback,
-+ MG_CB(mg_event_handler_t callback,
-+ void *user_data),
- struct mg_bind_opts opts) {
- union socket_address sa;
- struct mg_connection *nc = NULL;
-@@ -2810,14 +2954,16 @@ struct mg_connection *mg_bind_opt(struct
- struct mg_add_sock_opts add_sock_opts;
- char host[MG_MAX_HOST_LEN];
-
-- MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ opts.user_data = user_data;
-+#endif
-
--#if MG_ENABLE_TUN
-- if (mg_strncmp(mg_mk_str(address), mg_mk_str("ws://"), 5) == 0 ||
-- mg_strncmp(mg_mk_str(address), mg_mk_str("wss://"), 6) == 0) {
-- return mg_tun_bind_opt(mgr, address, callback, opts);
-+ if (callback == NULL) {
-+ MG_SET_PTRPTR(opts.error_string, "handler is required");
-+ return NULL;
- }
--#endif
-+
-+ MG_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);
-
- if (mg_parse_address(address, &sa, &proto, host, sizeof(host)) <= 0) {
- MG_SET_PTRPTR(opts.error_string, "cannot parse address");
-@@ -2850,6 +2996,7 @@ struct mg_connection *mg_bind_opt(struct
- params.cert = opts.ssl_cert;
- params.key = opts.ssl_key;
- params.ca_cert = opts.ssl_ca_cert;
-+ params.cipher_suites = opts.ssl_cipher_suites;
- if (mg_ssl_if_conn_init(nc, &params, &err_msg) != MG_SSL_OK) {
- MG_SET_PTRPTR(opts.error_string, err_msg);
- mg_destroy_conn(nc, 1 /* destroy_if */);
-@@ -2945,7 +3092,7 @@ int mg_check_ip_acl(const char *acl, uin
- }
- }
-
-- DBG(("%08x %c", remote_ip, allowed));
-+ DBG(("%08x %c", (unsigned int) remote_ip, allowed));
- return allowed == '+';
- }
-
-@@ -2963,7 +3110,7 @@ double mg_set_timer(struct mg_connection
- * connections, so not processed yet. It has a DNS resolver connection
- * linked to it. Set up a timer for the DNS connection.
- */
-- DBG(("%p %p %d -> %lu", c, c->priv_2, c->flags & MG_F_RESOLVING,
-+ DBG(("%p %p %d -> %lu", c, c->priv_2, (c->flags & MG_F_RESOLVING ? 1 : 0),
- (unsigned long) timestamp));
- if ((c->flags & MG_F_RESOLVING) && c->priv_2 != NULL) {
- ((struct mg_connection *) c->priv_2)->ev_timer_time = timestamp;
-@@ -2983,8 +3130,13 @@ void mg_if_get_conn_addr(struct mg_conne
- }
-
- struct mg_connection *mg_add_sock_opt(struct mg_mgr *s, sock_t sock,
-- mg_event_handler_t callback,
-+ MG_CB(mg_event_handler_t callback,
-+ void *user_data),
- struct mg_add_sock_opts opts) {
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ opts.user_data = user_data;
-+#endif
-+
- struct mg_connection *nc = mg_create_connection_base(s, callback, opts);
- if (nc != NULL) {
- mg_sock_set(nc, sock);
-@@ -2994,17 +3146,18 @@ struct mg_connection *mg_add_sock_opt(st
- }
-
- struct mg_connection *mg_add_sock(struct mg_mgr *s, sock_t sock,
-- mg_event_handler_t callback) {
-+ MG_CB(mg_event_handler_t callback,
-+ void *user_data)) {
- struct mg_add_sock_opts opts;
- memset(&opts, 0, sizeof(opts));
-- return mg_add_sock_opt(s, sock, callback, opts);
-+ return mg_add_sock_opt(s, sock, MG_CB(callback, user_data), opts);
- }
-
- double mg_time(void) {
- return cs_time();
- }
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net_if_socket.h"
-+#line 1 "mongoose/src/mg_net_if_socket.h"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -3014,7 +3167,7 @@ double mg_time(void) {
- #ifndef CS_MONGOOSE_SRC_NET_IF_SOCKET_H_
- #define CS_MONGOOSE_SRC_NET_IF_SOCKET_H_
-
--/* Amalgamated: #include "mongoose/src/net_if.h" */
-+/* Amalgamated: #include "mg_net_if.h" */
-
- #ifdef __cplusplus
- extern "C" {
-@@ -3024,7 +3177,7 @@ extern "C" {
- #define MG_ENABLE_NET_IF_SOCKET MG_NET_IF == MG_NET_IF_SOCKET
- #endif
-
--extern struct mg_iface_vtable mg_socket_iface_vtable;
-+extern const struct mg_iface_vtable mg_socket_iface_vtable;
-
- #ifdef __cplusplus
- }
-@@ -3032,58 +3185,46 @@ extern struct mg_iface_vtable mg_socket_
-
- #endif /* CS_MONGOOSE_SRC_NET_IF_SOCKET_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net_if_tun.h"
-+#line 1 "mongoose/src/mg_net_if_socks.h"
- #endif
- /*
-- * Copyright (c) 2014-2016 Cesanta Software Limited
-- * All rights reserved
-- */
--
--#ifndef CS_MONGOOSE_SRC_NET_IF_TUN_H_
--#define CS_MONGOOSE_SRC_NET_IF_TUN_H_
--
--#if MG_ENABLE_TUN
-+* Copyright (c) 2014-2017 Cesanta Software Limited
-+* All rights reserved
-+*/
-
--/* Amalgamated: #include "mongoose/src/net_if.h" */
-+#ifndef CS_MONGOOSE_SRC_NET_IF_SOCKS_H_
-+#define CS_MONGOOSE_SRC_NET_IF_SOCKS_H_
-
--struct mg_tun_client;
-+#if MG_ENABLE_SOCKS
-+/* Amalgamated: #include "mg_net_if.h" */
-
- #ifdef __cplusplus
- extern "C" {
- #endif /* __cplusplus */
-
--extern struct mg_iface_vtable mg_tun_iface_vtable;
--
--struct mg_connection *mg_tun_if_find_conn(struct mg_tun_client *client,
-- uint32_t stream_id);
-+extern const struct mg_iface_vtable mg_socks_iface_vtable;
-
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
--
--#endif /* MG_ENABLE_TUN */
--
--#endif /* CS_MONGOOSE_SRC_NET_IF_TUN_H_ */
-+#endif /* MG_ENABLE_SOCKS */
-+#endif /* CS_MONGOOSE_SRC_NET_IF_SOCKS_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net_if.c"
-+#line 1 "mongoose/src/mg_net_if.c"
- #endif
--/* Amalgamated: #include "mongoose/src/net_if.h" */
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/net_if_socket.h" */
--/* Amalgamated: #include "mongoose/src/net_if_tun.h" */
-+/* Amalgamated: #include "mg_net_if.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_net_if_socket.h" */
-
--extern struct mg_iface_vtable mg_default_iface_vtable;
-+extern const struct mg_iface_vtable mg_default_iface_vtable;
-
--#if MG_ENABLE_TUN
--struct mg_iface_vtable *mg_ifaces[] = {&mg_default_iface_vtable,
-- &mg_tun_iface_vtable};
--#else
--struct mg_iface_vtable *mg_ifaces[] = {&mg_default_iface_vtable};
--#endif
-+const struct mg_iface_vtable *mg_ifaces[] = {
-+ &mg_default_iface_vtable,
-+};
-
- int mg_num_ifaces = (int) (sizeof(mg_ifaces) / sizeof(mg_ifaces[0]));
-
--struct mg_iface *mg_if_create_iface(struct mg_iface_vtable *vtable,
-+struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable,
- struct mg_mgr *mgr) {
- struct mg_iface *iface = (struct mg_iface *) MG_CALLOC(1, sizeof(*iface));
- iface->mgr = mgr;
-@@ -3093,7 +3234,7 @@ struct mg_iface *mg_if_create_iface(stru
- }
-
- struct mg_iface *mg_find_iface(struct mg_mgr *mgr,
-- struct mg_iface_vtable *vtable,
-+ const struct mg_iface_vtable *vtable,
- struct mg_iface *from) {
- int i = 0;
- if (from != NULL) {
-@@ -3113,7 +3254,7 @@ struct mg_iface *mg_find_iface(struct mg
- return NULL;
- }
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net_if_socket.c"
-+#line 1 "mongoose/src/mg_net_if_socket.c"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -3122,9 +3263,9 @@ struct mg_iface *mg_find_iface(struct mg
-
- #if MG_ENABLE_NET_IF_SOCKET
-
--/* Amalgamated: #include "mongoose/src/net_if_socket.h" */
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
-+/* Amalgamated: #include "mg_net_if_socket.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_util.h" */
-
- #define MG_TCP_RECV_BUFFER_SIZE 1024
- #define MG_UDP_RECV_BUFFER_SIZE 1500
-@@ -3145,17 +3286,16 @@ void mg_set_non_blocking_mode(sock_t soc
- #endif
- }
-
--static int mg_is_error(int n) {
-+static int mg_is_error(void) {
- int err = mg_get_errno();
-- return (n < 0 && err != EINPROGRESS && err != EWOULDBLOCK
-+ return err != EINPROGRESS && err != EWOULDBLOCK
- #ifndef WINCE
-- && err != EAGAIN && err != EINTR
-+ && err != EAGAIN && err != EINTR
- #endif
- #ifdef _WIN32
-- && WSAGetLastError() != WSAEINTR &&
-- WSAGetLastError() != WSAEWOULDBLOCK
-+ && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK
- #endif
-- );
-+ ;
- }
-
- void mg_socket_if_connect_tcp(struct mg_connection *nc,
-@@ -3170,7 +3310,7 @@ void mg_socket_if_connect_tcp(struct mg_
- mg_set_non_blocking_mode(nc->sock);
- #endif
- rc = connect(nc->sock, &sa->sa, sizeof(sa->sin));
-- nc->err = mg_is_error(rc) ? mg_get_errno() : 0;
-+ nc->err = rc < 0 && mg_is_error() ? mg_get_errno() : 0;
- DBG(("%p sock %d rc %d errno %d err %d", nc, nc->sock, rc, mg_get_errno(),
- nc->err));
- }
-@@ -3183,8 +3323,11 @@ void mg_socket_if_connect_udp(struct mg_
- }
- if (nc->flags & MG_F_ENABLE_BROADCAST) {
- int optval = 1;
-- setsockopt(nc->sock, SOL_SOCKET, SO_BROADCAST, (const char *) &optval,
-- sizeof(optval));
-+ if (setsockopt(nc->sock, SOL_SOCKET, SO_BROADCAST, (const char *) &optval,
-+ sizeof(optval)) < 0) {
-+ nc->err = mg_get_errno() ? mg_get_errno() : 1;
-+ return;
-+ }
- }
- nc->err = 0;
- }
-@@ -3246,7 +3389,7 @@ static int mg_accept_conn(struct mg_conn
- /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */
- sock_t sock = accept(lc->sock, &sa.sa, &sa_len);
- if (sock == INVALID_SOCKET) {
-- if (mg_is_error(-1)) DBG(("%p: failed to accept: %d", lc, mg_get_errno()));
-+ if (mg_is_error()) DBG(("%p: failed to accept: %d", lc, mg_get_errno()));
- return 0;
- }
- nc = mg_if_accept_new_conn(lc);
-@@ -3331,10 +3474,7 @@ static void mg_write_to_socket(struct mg
- sendto(nc->sock, io->buf, io->len, 0, &nc->sa.sa, sizeof(nc->sa.sin));
- DBG(("%p %d %d %d %s:%hu", nc, nc->sock, n, mg_get_errno(),
- inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port)));
-- if (n > 0) {
-- mbuf_remove(io, n);
-- mg_if_sent_cb(nc, n);
-- }
-+ mg_if_sent_cb(nc, n);
- return;
- }
-
-@@ -3361,17 +3501,9 @@ static void mg_write_to_socket(struct mg
- {
- n = (int) MG_SEND_FUNC(nc->sock, io->buf, io->len, 0);
- DBG(("%p %d bytes -> %d", nc, n, nc->sock));
-- if (n < 0 && mg_is_error(n)) {
-- /* Something went wrong, drop the connection. */
-- nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-- return;
-- }
- }
-
-- if (n > 0) {
-- mbuf_remove(io, n);
-- mg_if_sent_cb(nc, n);
-- }
-+ mg_if_sent_cb(nc, n);
- }
-
- MG_INTERNAL size_t recv_avail_size(struct mg_connection *conn, size_t max) {
-@@ -3426,7 +3558,7 @@ static void mg_handle_tcp_read(struct mg
- if (n == 0) {
- /* Orderly shutdown of the socket, try flushing output. */
- conn->flags |= MG_F_SEND_AND_CLOSE;
-- } else if (mg_is_error(n)) {
-+ } else if (n < 0 && mg_is_error()) {
- conn->flags |= MG_F_CLOSE_IMMEDIATELY;
- }
- }
-@@ -3574,7 +3706,8 @@ static void mg_mgr_handle_ctl_sock(struc
- if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) {
- struct mg_connection *nc;
- for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
-- ctl_msg.callback(nc, MG_EV_POLL, ctl_msg.message);
-+ ctl_msg.callback(nc, MG_EV_POLL,
-+ ctl_msg.message MG_UD_ARG(nc->user_data));
- }
- }
- }
-@@ -3592,9 +3725,7 @@ void mg_socket_if_init(struct mg_iface *
- (void) iface;
- DBG(("%p using select()", iface->mgr));
- #if MG_ENABLE_BROADCAST
-- do {
-- mg_socketpair(iface->mgr->ctl, SOCK_DGRAM);
-- } while (iface->mgr->ctl[0] == INVALID_SOCKET);
-+ mg_socketpair(iface->mgr->ctl, SOCK_DGRAM);
- #endif
- }
-
-@@ -3613,7 +3744,7 @@ void mg_socket_if_remove_conn(struct mg_
- void mg_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
- if (sock != INVALID_SOCKET
- #ifdef __unix__
-- && sock < FD_SETSIZE
-+ && sock < (sock_t) FD_SETSIZE
- #endif
- ) {
- FD_SET(sock, set);
-@@ -3656,12 +3787,18 @@ time_t mg_socket_if_poll(struct mg_iface
-
- #ifdef __unix__
- /* A hack to make sure all our file descriptos fit into FD_SETSIZE. */
-- if (nc->sock >= FD_SETSIZE && try_dup) {
-+ if (nc->sock >= (sock_t) FD_SETSIZE && try_dup) {
- int new_sock = dup(nc->sock);
-- if (new_sock >= 0 && new_sock < FD_SETSIZE) {
-- closesocket(nc->sock);
-- DBG(("new sock %d -> %d", nc->sock, new_sock));
-- nc->sock = new_sock;
-+ if (new_sock >= 0) {
-+ if (new_sock < (sock_t) FD_SETSIZE) {
-+ closesocket(nc->sock);
-+ DBG(("new sock %d -> %d", nc->sock, new_sock));
-+ nc->sock = new_sock;
-+ } else {
-+ closesocket(new_sock);
-+ DBG(("new sock is still larger than FD_SETSIZE, disregard"));
-+ try_dup = 0;
-+ }
- } else {
- try_dup = 0;
- }
-@@ -3752,6 +3889,26 @@ time_t mg_socket_if_poll(struct mg_iface
- }
-
- #if MG_ENABLE_BROADCAST
-+MG_INTERNAL void mg_socketpair_close(sock_t *sock) {
-+ while (1) {
-+ if (closesocket(*sock) == -1 && errno == EINTR) continue;
-+ break;
-+ }
-+ *sock = INVALID_SOCKET;
-+}
-+
-+MG_INTERNAL sock_t
-+mg_socketpair_accept(sock_t sock, union socket_address *sa, socklen_t sa_len) {
-+ sock_t rc;
-+ while (1) {
-+ if ((rc = accept(sock, &sa->sa, &sa_len)) == INVALID_SOCKET &&
-+ errno == EINTR)
-+ continue;
-+ break;
-+ }
-+ return rc;
-+}
-+
- int mg_socketpair(sock_t sp[2], int sock_type) {
- union socket_address sa;
- sock_t sock;
-@@ -3774,21 +3931,20 @@ int mg_socketpair(sock_t sp[2], int sock
- } else if (sock_type == SOCK_DGRAM &&
- (getsockname(sp[0], &sa.sa, &len) != 0 ||
- connect(sock, &sa.sa, len) != 0)) {
-- } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock
-- : accept(sock, &sa.sa, &len))) ==
-+ } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock : mg_socketpair_accept(
-+ sock, &sa, len))) ==
- INVALID_SOCKET) {
- } else {
- mg_set_close_on_exec(sp[0]);
- mg_set_close_on_exec(sp[1]);
-- if (sock_type == SOCK_STREAM) closesocket(sock);
-+ if (sock_type == SOCK_STREAM) mg_socketpair_close(&sock);
- ret = 1;
- }
-
- if (!ret) {
-- if (sp[0] != INVALID_SOCKET) closesocket(sp[0]);
-- if (sp[1] != INVALID_SOCKET) closesocket(sp[1]);
-- if (sock != INVALID_SOCKET) closesocket(sock);
-- sock = sp[0] = sp[1] = INVALID_SOCKET;
-+ if (sp[0] != INVALID_SOCKET) mg_socketpair_close(&sp[0]);
-+ if (sp[1] != INVALID_SOCKET) mg_socketpair_close(&sp[1]);
-+ if (sock != INVALID_SOCKET) mg_socketpair_close(&sock);
- }
-
- return ret;
-@@ -3814,6 +3970,10 @@ void mg_sock_to_str(sock_t sock, char *b
-
- void mg_socket_if_get_conn_addr(struct mg_connection *nc, int remote,
- union socket_address *sa) {
-+ if ((nc->flags & MG_F_UDP) && remote) {
-+ memcpy(sa, &nc->sa, sizeof(*sa));
-+ return;
-+ }
- mg_sock_get_addr(nc->sock, remote, sa);
- }
-
-@@ -3839,187 +3999,226 @@ void mg_socket_if_get_conn_addr(struct m
- }
- /* clang-format on */
-
--struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_socket_iface_vtable = MG_SOCKET_IFACE_VTABLE;
- #if MG_NET_IF == MG_NET_IF_SOCKET
--struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_default_iface_vtable = MG_SOCKET_IFACE_VTABLE;
- #endif
-
- #endif /* MG_ENABLE_NET_IF_SOCKET */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net_if_tun.c"
-+#line 1 "mongoose/src/mg_net_if_socks.c"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
-
--#if MG_ENABLE_TUN
-+#if MG_ENABLE_SOCKS
-
--/* Amalgamated: #include "common/cs_dbg.h" */
--/* Amalgamated: #include "common/cs_time.h" */
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/net_if_tun.h" */
--/* Amalgamated: #include "mongoose/src/tun.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
-+struct socksdata {
-+ char *proxy_addr; /* HOST:PORT of the socks5 proxy server */
-+ struct mg_connection *s; /* Respective connection to the server */
-+ struct mg_connection *c; /* Connection to the client */
-+ struct mbuf tmp; /* Temporary buffer for sent data */
-+};
-
--#define MG_TCP_RECV_BUFFER_SIZE 1024
--#define MG_UDP_RECV_BUFFER_SIZE 1500
-+static void socks_if_disband(struct socksdata *d) {
-+ LOG(LL_DEBUG, ("disbanding proxy %p %p", d->c, d->s));
-+ if (d->c) d->c->flags |= MG_F_SEND_AND_CLOSE;
-+ if (d->s) d->s->flags |= MG_F_SEND_AND_CLOSE;
-+ d->c = d->s = NULL;
-+}
-+
-+static void socks_if_handler(struct mg_connection *c, int ev, void *ev_data) {
-+ struct socksdata *d = (struct socksdata *) c->user_data;
-+ if (ev == MG_EV_CONNECT) {
-+ int res = *(int *) ev_data;
-+ if (res == 0) {
-+ /* Send handshake to the proxy server */
-+ unsigned char buf[] = {MG_SOCKS_VERSION, 1, MG_SOCKS_HANDSHAKE_NOAUTH};
-+ mg_send(d->s, buf, sizeof(buf));
-+ LOG(LL_DEBUG, ("Sent handshake to %s", d->proxy_addr));
-+ } else {
-+ LOG(LL_ERROR, ("Cannot connect to %s: %d", d->proxy_addr, res));
-+ d->c->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ }
-+ } else if (ev == MG_EV_CLOSE) {
-+ socks_if_disband(d);
-+ } else if (ev == MG_EV_RECV) {
-+ /* Handle handshake reply */
-+ if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) {
-+ /* TODO(lsm): process IPv6 too */
-+ unsigned char buf[10] = {MG_SOCKS_VERSION, MG_SOCKS_CMD_CONNECT, 0,
-+ MG_SOCKS_ADDR_IPV4};
-+ if (c->recv_mbuf.len < 2) return;
-+ if ((unsigned char) c->recv_mbuf.buf[1] == MG_SOCKS_HANDSHAKE_FAILURE) {
-+ LOG(LL_ERROR, ("Server kicked us out"));
-+ socks_if_disband(d);
-+ return;
-+ }
-+ mbuf_remove(&c->recv_mbuf, 2);
-+ c->flags |= MG_SOCKS_HANDSHAKE_DONE;
-
--void mg_tun_if_connect_tcp(struct mg_connection *nc,
-- const union socket_address *sa) {
-- (void) nc;
-+ /* Send connect request */
-+ memcpy(buf + 4, &d->c->sa.sin.sin_addr, 4);
-+ memcpy(buf + 8, &d->c->sa.sin.sin_port, 2);
-+ mg_send(c, buf, sizeof(buf));
-+ }
-+ /* Process connect request */
-+ if ((c->flags & MG_SOCKS_HANDSHAKE_DONE) &&
-+ !(c->flags & MG_SOCKS_CONNECT_DONE)) {
-+ if (c->recv_mbuf.len < 10) return;
-+ if (c->recv_mbuf.buf[1] != MG_SOCKS_SUCCESS) {
-+ LOG(LL_ERROR, ("Socks connection error: %d", c->recv_mbuf.buf[1]));
-+ socks_if_disband(d);
-+ return;
-+ }
-+ mbuf_remove(&c->recv_mbuf, 10);
-+ c->flags |= MG_SOCKS_CONNECT_DONE;
-+ /* Connected. Move sent data from client, if any, to server */
-+ if (d->s && d->c) {
-+ mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len);
-+ mbuf_free(&d->tmp);
-+ }
-+ }
-+ /* All flags are set, we're in relay mode */
-+ if ((c->flags & MG_SOCKS_CONNECT_DONE) && d->c && d->s) {
-+ mbuf_append(&d->c->recv_mbuf, d->s->recv_mbuf.buf, d->s->recv_mbuf.len);
-+ mbuf_remove(&d->s->recv_mbuf, d->s->recv_mbuf.len);
-+ }
-+ }
-+}
-+
-+static void mg_socks_if_connect_tcp(struct mg_connection *c,
-+ const union socket_address *sa) {
-+ struct socksdata *d = (struct socksdata *) c->iface->data;
-+ d->c = c;
-+ d->s = mg_connect(c->mgr, d->proxy_addr, socks_if_handler);
-+ d->s->user_data = d;
-+ LOG(LL_DEBUG, ("%p %s", c, d->proxy_addr));
- (void) sa;
- }
-
--void mg_tun_if_connect_udp(struct mg_connection *nc) {
-- (void) nc;
-+static void mg_socks_if_connect_udp(struct mg_connection *c) {
-+ (void) c;
- }
-
--int mg_tun_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
-- (void) nc;
-+static int mg_socks_if_listen_tcp(struct mg_connection *c,
-+ union socket_address *sa) {
-+ (void) c;
- (void) sa;
- return 0;
- }
-
--int mg_tun_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
-- (void) nc;
-+static int mg_socks_if_listen_udp(struct mg_connection *c,
-+ union socket_address *sa) {
-+ (void) c;
- (void) sa;
- return -1;
- }
-
--void mg_tun_if_tcp_send(struct mg_connection *nc, const void *buf, size_t len) {
-- struct mg_tun_client *client = (struct mg_tun_client *) nc->iface->data;
-- uint32_t stream_id = (uint32_t)(uintptr_t) nc->mgr_data;
-- struct mg_str msg = {(char *) buf, len};
--#if MG_ENABLE_HEXDUMP
-- char hex[512];
-- mg_hexdump(buf, len, hex, sizeof(hex));
-- LOG(LL_DEBUG, ("sending to stream %zu:\n%s", stream_id, hex));
--#endif
--
-- mg_tun_send_frame(client->disp, stream_id, MG_TUN_DATA_FRAME, 0, msg);
-+static void mg_socks_if_tcp_send(struct mg_connection *c, const void *buf,
-+ size_t len) {
-+ struct socksdata *d = (struct socksdata *) c->iface->data;
-+ LOG(LL_DEBUG, ("%p -> %p %d %d", c, buf, (int) len, (int) c->send_mbuf.len));
-+ if (d && d->s && d->s->flags & MG_SOCKS_CONNECT_DONE) {
-+ mbuf_append(&d->s->send_mbuf, d->tmp.buf, d->tmp.len);
-+ mbuf_append(&d->s->send_mbuf, buf, len);
-+ mbuf_free(&d->tmp);
-+ } else {
-+ mbuf_append(&d->tmp, buf, len);
-+ }
- }
-
--void mg_tun_if_udp_send(struct mg_connection *nc, const void *buf, size_t len) {
-- (void) nc;
-+static void mg_socks_if_udp_send(struct mg_connection *c, const void *buf,
-+ size_t len) {
-+ (void) c;
- (void) buf;
- (void) len;
- }
-
--void mg_tun_if_recved(struct mg_connection *nc, size_t len) {
-- (void) nc;
-+static void mg_socks_if_recved(struct mg_connection *c, size_t len) {
-+ (void) c;
- (void) len;
- }
-
--int mg_tun_if_create_conn(struct mg_connection *nc) {
-- (void) nc;
-+static int mg_socks_if_create_conn(struct mg_connection *c) {
-+ (void) c;
- return 1;
- }
-
--void mg_tun_if_destroy_conn(struct mg_connection *nc) {
-- struct mg_tun_client *client = (struct mg_tun_client *) nc->iface->data;
--
-- if (nc->flags & MG_F_LISTENING) {
-- mg_tun_destroy_client(client);
-- } else if (client->disp) {
-- uint32_t stream_id = (uint32_t)(uintptr_t) nc->mgr_data;
-- struct mg_str msg = {NULL, 0};
--
-- LOG(LL_DEBUG, ("closing %zu:", stream_id));
-- mg_tun_send_frame(client->disp, stream_id, MG_TUN_DATA_FRAME,
-- MG_TUN_F_END_STREAM, msg);
-- }
-+static void mg_socks_if_destroy_conn(struct mg_connection *c) {
-+ c->iface->vtable->free(c->iface);
-+ MG_FREE(c->iface);
-+ c->iface = NULL;
-+ LOG(LL_DEBUG, ("%p", c));
- }
-
--/* Associate a socket to a connection. */
--void mg_tun_if_sock_set(struct mg_connection *nc, sock_t sock) {
-- (void) nc;
-+static void mg_socks_if_sock_set(struct mg_connection *c, sock_t sock) {
-+ (void) c;
- (void) sock;
- }
-
--void mg_tun_if_init(struct mg_iface *iface) {
-+static void mg_socks_if_init(struct mg_iface *iface) {
- (void) iface;
- }
-
--void mg_tun_if_free(struct mg_iface *iface) {
-- (void) iface;
-+static void mg_socks_if_free(struct mg_iface *iface) {
-+ struct socksdata *d = (struct socksdata *) iface->data;
-+ LOG(LL_DEBUG, ("%p", iface));
-+ if (d != NULL) {
-+ socks_if_disband(d);
-+ mbuf_free(&d->tmp);
-+ MG_FREE(d->proxy_addr);
-+ MG_FREE(d);
-+ iface->data = NULL;
-+ }
- }
-
--void mg_tun_if_add_conn(struct mg_connection *nc) {
-- nc->sock = INVALID_SOCKET;
-+static void mg_socks_if_add_conn(struct mg_connection *c) {
-+ c->sock = INVALID_SOCKET;
- }
-
--void mg_tun_if_remove_conn(struct mg_connection *nc) {
-- (void) nc;
-+static void mg_socks_if_remove_conn(struct mg_connection *c) {
-+ (void) c;
- }
-
--time_t mg_tun_if_poll(struct mg_iface *iface, int timeout_ms) {
-+static time_t mg_socks_if_poll(struct mg_iface *iface, int timeout_ms) {
-+ LOG(LL_DEBUG, ("%p", iface));
- (void) iface;
- (void) timeout_ms;
- return (time_t) cs_time();
- }
-
--void mg_tun_if_get_conn_addr(struct mg_connection *nc, int remote,
-- union socket_address *sa) {
-- (void) nc;
-+static void mg_socks_if_get_conn_addr(struct mg_connection *c, int remote,
-+ union socket_address *sa) {
-+ LOG(LL_DEBUG, ("%p", c));
-+ (void) c;
- (void) remote;
- (void) sa;
- }
-
--struct mg_connection *mg_tun_if_find_conn(struct mg_tun_client *client,
-- uint32_t stream_id) {
-- struct mg_connection *nc = NULL;
--
-- for (nc = client->mgr->active_connections; nc != NULL; nc = nc->next) {
-- if (nc->iface != client->iface || (nc->flags & MG_F_LISTENING)) {
-- continue;
-- }
-- if (stream_id == (uint32_t)(uintptr_t) nc->mgr_data) {
-- return nc;
-- }
-- }
--
-- if (stream_id > client->last_stream_id) {
-- /* create a new connection */
-- LOG(LL_DEBUG, ("new stream 0x%lx, accepting", stream_id));
-- nc = mg_if_accept_new_conn(client->listener);
-- nc->mgr_data = (void *) (uintptr_t) stream_id;
-- client->last_stream_id = stream_id;
-- } else {
-- LOG(LL_DEBUG, ("Ignoring stream 0x%lx (last_stream_id 0x%lx)", stream_id,
-- client->last_stream_id));
-- }
-+const struct mg_iface_vtable mg_socks_iface_vtable = {
-+ mg_socks_if_init, mg_socks_if_free,
-+ mg_socks_if_add_conn, mg_socks_if_remove_conn,
-+ mg_socks_if_poll, mg_socks_if_listen_tcp,
-+ mg_socks_if_listen_udp, mg_socks_if_connect_tcp,
-+ mg_socks_if_connect_udp, mg_socks_if_tcp_send,
-+ mg_socks_if_udp_send, mg_socks_if_recved,
-+ mg_socks_if_create_conn, mg_socks_if_destroy_conn,
-+ mg_socks_if_sock_set, mg_socks_if_get_conn_addr,
-+};
-
-- return nc;
-+struct mg_iface *mg_socks_mk_iface(struct mg_mgr *mgr, const char *proxy_addr) {
-+ struct mg_iface *iface = mg_if_create_iface(&mg_socks_iface_vtable, mgr);
-+ iface->data = MG_CALLOC(1, sizeof(struct socksdata));
-+ ((struct socksdata *) iface->data)->proxy_addr = strdup(proxy_addr);
-+ return iface;
- }
-
--/* clang-format off */
--#define MG_TUN_IFACE_VTABLE \
-- { \
-- mg_tun_if_init, \
-- mg_tun_if_free, \
-- mg_tun_if_add_conn, \
-- mg_tun_if_remove_conn, \
-- mg_tun_if_poll, \
-- mg_tun_if_listen_tcp, \
-- mg_tun_if_listen_udp, \
-- mg_tun_if_connect_tcp, \
-- mg_tun_if_connect_udp, \
-- mg_tun_if_tcp_send, \
-- mg_tun_if_udp_send, \
-- mg_tun_if_recved, \
-- mg_tun_if_create_conn, \
-- mg_tun_if_destroy_conn, \
-- mg_tun_if_sock_set, \
-- mg_tun_if_get_conn_addr, \
-- }
--/* clang-format on */
--
--struct mg_iface_vtable mg_tun_iface_vtable = MG_TUN_IFACE_VTABLE;
--
--#endif /* MG_ENABLE_TUN */
-+#endif
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/ssl_if_openssl.c"
-+#line 1 "mongoose/src/mg_ssl_if_openssl.c"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -4037,6 +4236,8 @@ struct mg_iface_vtable mg_tun_iface_vtab
- struct mg_ssl_if_ctx {
- SSL *ssl;
- SSL_CTX *ssl_ctx;
-+ struct mbuf psk;
-+ size_t identity_len;
- };
-
- void mg_ssl_if_init() {
-@@ -4060,7 +4261,10 @@ enum mg_ssl_if_result mg_ssl_if_conn_acc
- static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert,
- const char *key, const char **err_msg);
- static enum mg_ssl_if_result mg_use_ca_cert(SSL_CTX *ctx, const char *cert);
--static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx);
-+static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl);
-+static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx,
-+ const char *identity,
-+ const char *key_str);
-
- enum mg_ssl_if_result mg_ssl_if_conn_init(
- struct mg_connection *nc, const struct mg_ssl_if_conn_params *params,
-@@ -4085,6 +4289,21 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- return MG_SSL_ERROR;
- }
-
-+#ifndef KR_VERSION
-+ /* Disable deprecated protocols. */
-+ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
-+ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3);
-+ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
-+#ifdef MG_SSL_OPENSSL_NO_COMPRESSION
-+ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
-+#endif
-+#ifdef MG_SSL_OPENSSL_CIPHER_SERVER_PREFERENCE
-+ SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-+#endif
-+#else
-+/* Krypton only supports TLSv1.2 anyway. */
-+#endif
-+
- if (params->cert != NULL &&
- mg_use_cert(ctx->ssl_ctx, params->cert, params->key, err_msg) !=
- MG_SSL_OK) {
-@@ -4105,7 +4324,17 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- #endif
- }
-
-- mg_set_cipher_list(ctx->ssl_ctx);
-+ if (mg_set_cipher_list(ctx->ssl_ctx, params->cipher_suites) != MG_SSL_OK) {
-+ MG_SET_PTRPTR(err_msg, "Invalid cipher suite list");
-+ return MG_SSL_ERROR;
-+ }
-+
-+ mbuf_init(&ctx->psk, 0);
-+ if (mg_ssl_if_ossl_set_psk(ctx, params->psk_identity, params->psk_key) !=
-+ MG_SSL_OK) {
-+ MG_SET_PTRPTR(err_msg, "Invalid PSK settings");
-+ return MG_SSL_ERROR;
-+ }
-
- if (!(nc->flags & MG_F_LISTENING) &&
- (ctx->ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
-@@ -4159,12 +4388,19 @@ int mg_ssl_if_write(struct mg_connection
- return n;
- }
-
-+void mg_ssl_if_conn_close_notify(struct mg_connection *nc) {
-+ struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
-+ if (ctx == NULL) return;
-+ SSL_shutdown(ctx->ssl);
-+}
-+
- void mg_ssl_if_conn_free(struct mg_connection *nc) {
- struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
- if (ctx == NULL) return;
- nc->ssl_if_data = NULL;
- if (ctx->ssl != NULL) SSL_free(ctx->ssl);
- if (ctx->ssl_ctx != NULL && nc->listener == NULL) SSL_CTX_free(ctx->ssl_ctx);
-+ mbuf_free(&ctx->psk);
- memset(ctx, 0, sizeof(*ctx));
- MG_FREE(ctx);
- }
-@@ -4287,11 +4523,83 @@ static enum mg_ssl_if_result mg_use_cert
- return MG_SSL_OK;
- }
-
--static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx) {
-- return (SSL_CTX_set_cipher_list(ctx, mg_s_cipher_list) == 1 ? MG_SSL_OK
-- : MG_SSL_ERROR);
-+static enum mg_ssl_if_result mg_set_cipher_list(SSL_CTX *ctx, const char *cl) {
-+ return (SSL_CTX_set_cipher_list(ctx, cl ? cl : mg_s_cipher_list) == 1
-+ ? MG_SSL_OK
-+ : MG_SSL_ERROR);
- }
-
-+#ifndef KR_VERSION
-+static unsigned int mg_ssl_if_ossl_psk_cb(SSL *ssl, const char *hint,
-+ char *identity,
-+ unsigned int max_identity_len,
-+ unsigned char *psk,
-+ unsigned int max_psk_len) {
-+ struct mg_ssl_if_ctx *ctx =
-+ (struct mg_ssl_if_ctx *) SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl));
-+ size_t key_len = ctx->psk.len - ctx->identity_len - 1;
-+ DBG(("hint: '%s'", (hint ? hint : "")));
-+ if (ctx->identity_len + 1 > max_identity_len) {
-+ DBG(("identity too long"));
-+ return 0;
-+ }
-+ if (key_len > max_psk_len) {
-+ DBG(("key too long"));
-+ return 0;
-+ }
-+ memcpy(identity, ctx->psk.buf, ctx->identity_len + 1);
-+ memcpy(psk, ctx->psk.buf + ctx->identity_len + 1, key_len);
-+ (void) ssl;
-+ return key_len;
-+}
-+
-+static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx,
-+ const char *identity,
-+ const char *key_str) {
-+ unsigned char key[32];
-+ size_t key_len;
-+ size_t i = 0;
-+ if (identity == NULL && key_str == NULL) return MG_SSL_OK;
-+ if (identity == NULL || key_str == NULL) return MG_SSL_ERROR;
-+ key_len = strlen(key_str);
-+ if (key_len != 32 && key_len != 64) return MG_SSL_ERROR;
-+ memset(key, 0, sizeof(key));
-+ key_len = 0;
-+ for (i = 0; key_str[i] != '\0'; i++) {
-+ unsigned char c;
-+ char hc = tolower((int) key_str[i]);
-+ if (hc >= '0' && hc <= '9') {
-+ c = hc - '0';
-+ } else if (hc >= 'a' && hc <= 'f') {
-+ c = hc - 'a' + 0xa;
-+ } else {
-+ return MG_SSL_ERROR;
-+ }
-+ key_len = i / 2;
-+ key[key_len] <<= 4;
-+ key[key_len] |= c;
-+ }
-+ key_len++;
-+ DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len));
-+ ctx->identity_len = strlen(identity);
-+ mbuf_append(&ctx->psk, identity, ctx->identity_len + 1);
-+ mbuf_append(&ctx->psk, key, key_len);
-+ SSL_CTX_set_psk_client_callback(ctx->ssl_ctx, mg_ssl_if_ossl_psk_cb);
-+ SSL_CTX_set_app_data(ctx->ssl_ctx, ctx);
-+ return MG_SSL_OK;
-+}
-+#else
-+static enum mg_ssl_if_result mg_ssl_if_ossl_set_psk(struct mg_ssl_if_ctx *ctx,
-+ const char *identity,
-+ const char *key_str) {
-+ (void) ctx;
-+ (void) identity;
-+ (void) key_str;
-+ /* Krypton does not support PSK. */
-+ return MG_SSL_ERROR;
-+}
-+#endif /* defined(KR_VERSION) */
-+
- const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
- const char *ca_cert) {
- const char *err_msg = NULL;
-@@ -4307,7 +4615,7 @@ const char *mg_set_ssl(struct mg_connect
-
- #endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_OPENSSL */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/ssl_if_mbedtls.c"
-+#line 1 "mongoose/src/mg_ssl_if_mbedtls.c"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -4348,6 +4656,7 @@ struct mg_ssl_if_ctx {
- mbedtls_x509_crt *cert;
- mbedtls_pk_context *key;
- mbedtls_x509_crt *ca_cert;
-+ struct mbuf cipher_suites;
- };
-
- /* Must be provided by the platform. ctx is struct mg_connection. */
-@@ -4363,7 +4672,7 @@ enum mg_ssl_if_result mg_ssl_if_conn_acc
- struct mg_ssl_if_ctx *lc_ctx = (struct mg_ssl_if_ctx *) lc->ssl_if_data;
- nc->ssl_if_data = ctx;
- if (ctx == NULL || lc_ctx == NULL) return MG_SSL_ERROR;
-- ctx->ssl = MG_CALLOC(1, sizeof(*ctx->ssl));
-+ ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl));
- if (mbedtls_ssl_setup(ctx->ssl, lc_ctx->conf) != 0) {
- return MG_SSL_ERROR;
- }
-@@ -4377,6 +4686,9 @@ static enum mg_ssl_if_result mg_use_ca_c
- const char *cert);
- static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx,
- const char *ciphers);
-+static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx,
-+ const char *identity,
-+ const char *key);
-
- enum mg_ssl_if_result mg_ssl_if_conn_init(
- struct mg_connection *nc, const struct mg_ssl_if_conn_params *params,
-@@ -4392,7 +4704,8 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- return MG_SSL_ERROR;
- }
- nc->ssl_if_data = ctx;
-- ctx->conf = MG_CALLOC(1, sizeof(*ctx->conf));
-+ ctx->conf = (mbedtls_ssl_config *) MG_CALLOC(1, sizeof(*ctx->conf));
-+ mbuf_init(&ctx->cipher_suites, 0);
- mbedtls_ssl_config_init(ctx->conf);
- mbedtls_ssl_conf_dbg(ctx->conf, mg_ssl_mbed_log, nc);
- if (mbedtls_ssl_config_defaults(
-@@ -4402,6 +4715,7 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- MG_SET_PTRPTR(err_msg, "Failed to init SSL config");
- return MG_SSL_ERROR;
- }
-+
- /* TLS 1.2 and up */
- mbedtls_ssl_conf_min_version(ctx->conf, MBEDTLS_SSL_MAJOR_VERSION_3,
- MBEDTLS_SSL_MINOR_VERSION_3);
-@@ -4418,10 +4732,19 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- return MG_SSL_ERROR;
- }
-
-- mg_set_cipher_list(ctx, NULL);
-+ if (mg_set_cipher_list(ctx, params->cipher_suites) != MG_SSL_OK) {
-+ MG_SET_PTRPTR(err_msg, "Invalid cipher suite list");
-+ return MG_SSL_ERROR;
-+ }
-+
-+ if (mg_ssl_if_mbed_set_psk(ctx, params->psk_identity, params->psk_key) !=
-+ MG_SSL_OK) {
-+ MG_SET_PTRPTR(err_msg, "Invalid PSK settings");
-+ return MG_SSL_ERROR;
-+ }
-
- if (!(nc->flags & MG_F_LISTENING)) {
-- ctx->ssl = MG_CALLOC(1, sizeof(*ctx->ssl));
-+ ctx->ssl = (mbedtls_ssl_context *) MG_CALLOC(1, sizeof(*ctx->ssl));
- mbedtls_ssl_init(ctx->ssl);
- if (mbedtls_ssl_setup(ctx->ssl, ctx->conf) != 0) {
- MG_SET_PTRPTR(err_msg, "Failed to create SSL session");
-@@ -4433,6 +4756,24 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- }
- }
-
-+#ifdef MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN
-+ if (mbedtls_ssl_conf_max_frag_len(ctx->conf,
-+#if MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 512
-+ MBEDTLS_SSL_MAX_FRAG_LEN_512
-+#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 1024
-+ MBEDTLS_SSL_MAX_FRAG_LEN_1024
-+#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 2048
-+ MBEDTLS_SSL_MAX_FRAG_LEN_2048
-+#elif MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN == 4096
-+ MBEDTLS_SSL_MAX_FRAG_LEN_4096
-+#else
-+#error Invalid MG_SSL_IF_MBEDTLS_MAX_FRAG_LEN
-+#endif
-+ ) != 0) {
-+ return MG_SSL_ERROR;
-+ }
-+#endif
-+
- nc->flags |= MG_F_SSL;
-
- return MG_SSL_OK;
-@@ -4445,7 +4786,7 @@ int ssl_socket_recv(void *ctx, unsigned
- static int ssl_socket_send(void *ctx, const unsigned char *buf, size_t len) {
- struct mg_connection *nc = (struct mg_connection *) ctx;
- int n = (int) MG_SEND_FUNC(nc->sock, buf, len, 0);
-- DBG(("%p %d -> %d", nc, (int) len, n));
-+ LOG(LL_DEBUG, ("%p %d -> %d", nc, (int) len, n));
- if (n >= 0) return n;
- n = mg_get_errno();
- return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_WRITE : -1);
-@@ -4454,7 +4795,7 @@ static int ssl_socket_send(void *ctx, co
- static int ssl_socket_recv(void *ctx, unsigned char *buf, size_t len) {
- struct mg_connection *nc = (struct mg_connection *) ctx;
- int n = (int) MG_RECV_FUNC(nc->sock, buf, len, 0);
-- DBG(("%p %d <- %d", nc, (int) len, n));
-+ LOG(LL_DEBUG, ("%p %d <- %d", nc, (int) len, n));
- if (n >= 0) return n;
- n = mg_get_errno();
- return ((n == EAGAIN || n == EINPROGRESS) ? MBEDTLS_ERR_SSL_WANT_READ : -1);
-@@ -4485,6 +4826,12 @@ static void mg_ssl_if_mbed_free_certs_an
- }
- if (ctx->ca_cert != NULL) {
- mbedtls_ssl_conf_ca_chain(ctx->conf, NULL, NULL);
-+#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
-+ if (ctx->ca_cert->ca_chain_file != NULL) {
-+ MG_FREE((void *) ctx->ca_cert->ca_chain_file);
-+ ctx->ca_cert->ca_chain_file = NULL;
-+ }
-+#endif
- mbedtls_x509_crt_free(ctx->ca_cert);
- MG_FREE(ctx->ca_cert);
- ctx->ca_cert = NULL;
-@@ -4524,7 +4871,7 @@ enum mg_ssl_if_result mg_ssl_if_handshak
-
- int mg_ssl_if_read(struct mg_connection *nc, void *buf, size_t buf_size) {
- struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
-- int n = mbedtls_ssl_read(ctx->ssl, buf, buf_size);
-+ int n = mbedtls_ssl_read(ctx->ssl, (unsigned char *) buf, buf_size);
- DBG(("%p %d -> %d", nc, (int) buf_size, n));
- if (n < 0) return mg_ssl_if_mbed_err(nc, n);
- if (n == 0) nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-@@ -4533,12 +4880,18 @@ int mg_ssl_if_read(struct mg_connection
-
- int mg_ssl_if_write(struct mg_connection *nc, const void *data, size_t len) {
- struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
-- int n = mbedtls_ssl_write(ctx->ssl, data, len);
-+ int n = mbedtls_ssl_write(ctx->ssl, (const unsigned char *) data, len);
- DBG(("%p %d -> %d", nc, (int) len, n));
- if (n < 0) return mg_ssl_if_mbed_err(nc, n);
- return n;
- }
-
-+void mg_ssl_if_conn_close_notify(struct mg_connection *nc) {
-+ struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
-+ if (ctx == NULL) return;
-+ mbedtls_ssl_close_notify(ctx->ssl);
-+}
-+
- void mg_ssl_if_conn_free(struct mg_connection *nc) {
- struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
- if (ctx == NULL) return;
-@@ -4552,6 +4905,7 @@ void mg_ssl_if_conn_free(struct mg_conne
- mbedtls_ssl_config_free(ctx->conf);
- MG_FREE(ctx->conf);
- }
-+ mbuf_free(&ctx->cipher_suites);
- memset(ctx, 0, sizeof(*ctx));
- MG_FREE(ctx);
- }
-@@ -4559,13 +4913,21 @@ void mg_ssl_if_conn_free(struct mg_conne
- static enum mg_ssl_if_result mg_use_ca_cert(struct mg_ssl_if_ctx *ctx,
- const char *ca_cert) {
- if (ca_cert == NULL || strcmp(ca_cert, "*") == 0) {
-+ mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_NONE);
- return MG_SSL_OK;
- }
-- ctx->ca_cert = MG_CALLOC(1, sizeof(*ctx->ca_cert));
-+ ctx->ca_cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->ca_cert));
- mbedtls_x509_crt_init(ctx->ca_cert);
-+#ifdef MBEDTLS_X509_CA_CHAIN_ON_DISK
-+ ca_cert = strdup(ca_cert);
-+ if (mbedtls_x509_crt_set_ca_chain_file(ctx->ca_cert, ca_cert) != 0) {
-+ return MG_SSL_ERROR;
-+ }
-+#else
- if (mbedtls_x509_crt_parse_file(ctx->ca_cert, ca_cert) != 0) {
- return MG_SSL_ERROR;
- }
-+#endif
- mbedtls_ssl_conf_ca_chain(ctx->conf, ctx->ca_cert, NULL);
- mbedtls_ssl_conf_authmode(ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
- return MG_SSL_OK;
-@@ -4578,9 +4940,9 @@ static enum mg_ssl_if_result mg_use_cert
- if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') {
- return MG_SSL_OK;
- }
-- ctx->cert = MG_CALLOC(1, sizeof(*ctx->cert));
-+ ctx->cert = (mbedtls_x509_crt *) MG_CALLOC(1, sizeof(*ctx->cert));
- mbedtls_x509_crt_init(ctx->cert);
-- ctx->key = MG_CALLOC(1, sizeof(*ctx->key));
-+ ctx->key = (mbedtls_pk_context *) MG_CALLOC(1, sizeof(*ctx->key));
- mbedtls_pk_init(ctx->key);
- if (mbedtls_x509_crt_parse_file(ctx->cert, cert) != 0) {
- MG_SET_PTRPTR(err_msg, "Invalid SSL cert");
-@@ -4598,8 +4960,11 @@ static enum mg_ssl_if_result mg_use_cert
- }
-
- static const int mg_s_cipher_list[] = {
-+#if CS_PLATFORM != CS_P_ESP8266
- MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
- MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
- MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
- MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
- MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
-@@ -4610,7 +4975,29 @@ static const int mg_s_cipher_list[] = {
- MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
- MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
- MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
-- MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, 0};
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
-+#else
-+ /*
-+ * ECDHE is way too slow on ESP8266 w/o cryptochip, this sometimes results
-+ * in WiFi STA deauths. Use weaker but faster cipher suites. Sad but true.
-+ * Disable DHE completely because it's just hopelessly slow.
-+ */
-+ MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
-+ MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
-+ MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
-+ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA,
-+#endif /* CS_PLATFORM != CS_P_ESP8266 */
-+ 0,
-+};
-
- /*
- * Ciphers can be specified as a colon-separated list of cipher suite names.
-@@ -4621,27 +5008,70 @@ static const int mg_s_cipher_list[] = {
- static enum mg_ssl_if_result mg_set_cipher_list(struct mg_ssl_if_ctx *ctx,
- const char *ciphers) {
- if (ciphers != NULL) {
-- int ids[50], n = 0, l, id;
-- const char *s = ciphers;
-- char *e, tmp[50];
-- while (s != NULL && n < (int) (sizeof(ids) / sizeof(ids[0])) - 1) {
-+ int l, id;
-+ const char *s = ciphers, *e;
-+ char tmp[50];
-+ while (s != NULL) {
- e = strchr(s, ':');
- l = (e != NULL ? (e - s) : (int) strlen(s));
- strncpy(tmp, s, l);
-+ tmp[l] = '\0';
- id = mbedtls_ssl_get_ciphersuite_id(tmp);
-- DBG(("%s -> %d", tmp, id));
-- if (id != 0) ids[n++] = id;
-+ DBG(("%s -> %04x", tmp, id));
-+ if (id != 0) {
-+ mbuf_append(&ctx->cipher_suites, &id, sizeof(id));
-+ }
- s = (e != NULL ? e + 1 : NULL);
- }
-- if (n == 0) return MG_SSL_ERROR;
-- ids[n] = 0;
-- mbedtls_ssl_conf_ciphersuites(ctx->conf, ids);
-+ if (ctx->cipher_suites.len == 0) return MG_SSL_ERROR;
-+ id = 0;
-+ mbuf_append(&ctx->cipher_suites, &id, sizeof(id));
-+ mbuf_trim(&ctx->cipher_suites);
-+ mbedtls_ssl_conf_ciphersuites(ctx->conf,
-+ (const int *) ctx->cipher_suites.buf);
- } else {
- mbedtls_ssl_conf_ciphersuites(ctx->conf, mg_s_cipher_list);
- }
- return MG_SSL_OK;
- }
-
-+static enum mg_ssl_if_result mg_ssl_if_mbed_set_psk(struct mg_ssl_if_ctx *ctx,
-+ const char *identity,
-+ const char *key_str) {
-+ unsigned char key[32];
-+ size_t key_len;
-+ if (identity == NULL && key_str == NULL) return MG_SSL_OK;
-+ if (identity == NULL || key_str == NULL) return MG_SSL_ERROR;
-+ key_len = strlen(key_str);
-+ if (key_len != 32 && key_len != 64) return MG_SSL_ERROR;
-+ size_t i = 0;
-+ memset(key, 0, sizeof(key));
-+ key_len = 0;
-+ for (i = 0; key_str[i] != '\0'; i++) {
-+ unsigned char c;
-+ char hc = tolower((int) key_str[i]);
-+ if (hc >= '0' && hc <= '9') {
-+ c = hc - '0';
-+ } else if (hc >= 'a' && hc <= 'f') {
-+ c = hc - 'a' + 0xa;
-+ } else {
-+ return MG_SSL_ERROR;
-+ }
-+ key_len = i / 2;
-+ key[key_len] <<= 4;
-+ key[key_len] |= c;
-+ }
-+ key_len++;
-+ DBG(("identity = '%s', key = (%u)", identity, (unsigned int) key_len));
-+ /* mbedTLS makes copies of psk and identity. */
-+ if (mbedtls_ssl_conf_psk(ctx->conf, (const unsigned char *) key, key_len,
-+ (const unsigned char *) identity,
-+ strlen(identity)) != 0) {
-+ return MG_SSL_ERROR;
-+ }
-+ return MG_SSL_OK;
-+}
-+
- const char *mg_set_ssl(struct mg_connection *nc, const char *cert,
- const char *ca_cert) {
- const char *err_msg = NULL;
-@@ -4666,167 +5096,37 @@ int mg_ssl_if_mbed_random(void *ctx, uns
-
- #endif /* MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_MBEDTLS */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/multithreading.c"
--#endif
--/*
-- * Copyright (c) 2014 Cesanta Software Limited
-- * All rights reserved
-- */
--
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
--
--#if MG_ENABLE_THREADS
--
--static void multithreaded_ev_handler(struct mg_connection *c, int ev, void *p);
--
--/*
-- * This thread function executes user event handler.
-- * It runs an event manager that has only one connection, until that
-- * connection is alive.
-- */
--static void *per_connection_thread_function(void *param) {
-- struct mg_connection *c = (struct mg_connection *) param;
-- struct mg_mgr m;
-- /* mgr_data can be used subsequently, store its value */
-- int poll_timeout = (intptr_t) c->mgr_data;
--
-- mg_mgr_init(&m, NULL);
-- mg_add_conn(&m, c);
-- mg_call(c, NULL, MG_EV_ACCEPT, &c->sa);
--
-- while (m.active_connections != NULL) {
-- mg_mgr_poll(&m, poll_timeout ? poll_timeout : 1000);
-- }
-- mg_mgr_free(&m);
--
-- return param;
--}
--
--static void link_conns(struct mg_connection *c1, struct mg_connection *c2) {
-- c1->priv_2 = c2;
-- c2->priv_2 = c1;
--}
--
--static void unlink_conns(struct mg_connection *c) {
-- struct mg_connection *peer = (struct mg_connection *) c->priv_2;
-- if (peer != NULL) {
-- peer->flags |= MG_F_SEND_AND_CLOSE;
-- peer->priv_2 = NULL;
-- }
-- c->priv_2 = NULL;
--}
--
--static void forwarder_ev_handler(struct mg_connection *c, int ev, void *p) {
-- (void) p;
-- if (ev == MG_EV_RECV && c->priv_2) {
-- mg_forward(c, (struct mg_connection *) c->priv_2);
-- } else if (ev == MG_EV_CLOSE) {
-- unlink_conns(c);
-- }
--}
--
--static void spawn_handling_thread(struct mg_connection *nc) {
-- struct mg_mgr dummy;
-- sock_t sp[2];
-- struct mg_connection *c[2];
-- int poll_timeout;
-- /*
-- * Create a socket pair, and wrap each socket into the connection with
-- * dummy event manager.
-- * c[0] stays in this thread, c[1] goes to another thread.
-- */
-- mg_mgr_init(&dummy, NULL);
-- mg_socketpair(sp, SOCK_STREAM);
--
-- c[0] = mg_add_sock(&dummy, sp[0], forwarder_ev_handler);
-- c[1] = mg_add_sock(&dummy, sp[1], nc->listener->priv_1.f);
--
-- /* link_conns replaces priv_2, storing its value */
-- poll_timeout = (intptr_t) nc->priv_2;
--
-- /* Interlink client connection with c[0] */
-- link_conns(c[0], nc);
--
-- /*
-- * Switch c[0] manager from the dummy one to the real one. c[1] manager
-- * will be set in another thread, allocated on stack of that thread.
-- */
-- mg_add_conn(nc->mgr, c[0]);
--
-- /*
-- * Dress c[1] as nc.
-- * TODO(lsm): code in accept_conn() looks similar. Refactor.
-- */
-- c[1]->listener = nc->listener;
-- c[1]->proto_handler = nc->proto_handler;
-- c[1]->user_data = nc->user_data;
-- c[1]->sa = nc->sa;
-- c[1]->flags = nc->flags;
--
-- /* priv_2 is used, so, put timeout to mgr_data */
-- c[1]->mgr_data = (void *) (intptr_t) poll_timeout;
--
-- mg_start_thread(per_connection_thread_function, c[1]);
--}
--
--static void multithreaded_ev_handler(struct mg_connection *c, int ev, void *p) {
-- (void) p;
-- if (ev == MG_EV_ACCEPT) {
-- spawn_handling_thread(c);
-- c->handler = forwarder_ev_handler;
-- }
--}
--
--void mg_enable_multithreading_opt(struct mg_connection *nc,
-- struct mg_multithreading_opts opts) {
-- /* Wrap user event handler into our multithreaded_ev_handler */
-- nc->priv_1.f = nc->handler;
-- /*
-- * We put timeout to `priv_2` member of the main
-- * (listening) connection, mt is not enabled yet,
-- * and this member is not used
-- */
-- nc->priv_2 = (void *) (intptr_t) opts.poll_timeout;
-- nc->handler = multithreaded_ev_handler;
--}
--
--void mg_enable_multithreading(struct mg_connection *nc) {
-- struct mg_multithreading_opts opts;
-- memset(&opts, 0, sizeof(opts));
-- mg_enable_multithreading_opt(nc, opts);
--}
--
--#endif
--#ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/uri.c"
-+#line 1 "mongoose/src/mg_uri.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/uri.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_uri.h" */
-
- /*
-- * scan string until `sep`, keeping track of component boundaries in `res`.
-+ * scan string until encountering one of `seps`, keeping track of component
-+ * boundaries in `res`.
- *
- * `p` will point to the char after the separator or it will be `end`.
- */
--static void parse_uri_component(const char **p, const char *end, char sep,
-- struct mg_str *res) {
-+static void parse_uri_component(const char **p, const char *end,
-+ const char *seps, struct mg_str *res) {
-+ const char *q;
- res->p = *p;
- for (; *p < end; (*p)++) {
-- if (**p == sep) {
-- break;
-+ for (q = seps; *q != '\0'; q++) {
-+ if (**p == *q) break;
- }
-+ if (*q != '\0') break;
- }
- res->len = (*p) - res->p;
- if (*p < end) (*p)++;
- }
-
--int mg_parse_uri(struct mg_str uri, struct mg_str *scheme,
-+int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme,
- struct mg_str *user_info, struct mg_str *host,
- unsigned int *port, struct mg_str *path, struct mg_str *query,
- struct mg_str *fragment) {
-@@ -4850,8 +5150,13 @@ int mg_parse_uri(struct mg_str uri, stru
- * expecting on of:
- * - `scheme://xxxx`
- * - `xxxx:port`
-+ * - `[a:b:c]:port`
- * - `xxxx/path`
- */
-+ if (*p == '[') {
-+ state = P_HOST;
-+ break;
-+ }
- for (; p < end; p++) {
- if (*p == ':') {
- state = P_SCHEME_OR_PORT;
-@@ -4871,7 +5176,7 @@ int mg_parse_uri(struct mg_str uri, stru
- rscheme.p = uri.p;
- rscheme.len = p - uri.p;
- state = P_USER_INFO;
-- p += 2; /* point to last separator char */
-+ p += 3;
- } else {
- rhost.p = uri.p;
- rhost.len = p - uri.p;
-@@ -4879,27 +5184,35 @@ int mg_parse_uri(struct mg_str uri, stru
- }
- break;
- case P_USER_INFO:
-- p++;
- ruser_info.p = p;
- for (; p < end; p++) {
-- if (*p == '@') {
-- state = P_HOST;
-- break;
-- } else if (*p == '/') {
-+ if (*p == '@' || *p == '[' || *p == '/') {
- break;
- }
- }
-- if (p == end || *p == '/') {
-+ if (p == end || *p == '/' || *p == '[') {
- /* backtrack and parse as host */
-- state = P_HOST;
- p = ruser_info.p;
- }
- ruser_info.len = p - ruser_info.p;
-+ state = P_HOST;
- break;
- case P_HOST:
- if (*p == '@') p++;
- rhost.p = p;
-- for (; p < end; p++) {
-+ if (*p == '[') {
-+ int found = 0;
-+ for (; !found && p < end; p++) {
-+ found = (*p == ']');
-+ }
-+ if (!found) return -1;
-+ } else {
-+ for (; p < end; p++) {
-+ if (*p == ':' || *p == '/') break;
-+ }
-+ }
-+ rhost.len = p - rhost.p;
-+ if (p < end) {
- if (*p == ':') {
- state = P_PORT;
- break;
-@@ -4908,7 +5221,6 @@ int mg_parse_uri(struct mg_str uri, stru
- break;
- }
- }
-- rhost.len = p - rhost.p;
- break;
- case P_PORT:
- p++;
-@@ -4923,9 +5235,11 @@ int mg_parse_uri(struct mg_str uri, stru
- break;
- case P_REST:
- /* `p` points to separator. `path` includes the separator */
-- parse_uri_component(&p, end, '?', &rpath);
-- parse_uri_component(&p, end, '#', &rquery);
-- parse_uri_component(&p, end, '\0', &rfragment);
-+ parse_uri_component(&p, end, "?#", &rpath);
-+ if (p < end && *(p - 1) == '?') {
-+ parse_uri_component(&p, end, "#", &rquery);
-+ }
-+ parse_uri_component(&p, end, "", &rfragment);
- break;
- }
- }
-@@ -4956,7 +5270,7 @@ int mg_normalize_uri_path(const struct m
- while (s < se) {
- const char *next = s;
- struct mg_str component;
-- parse_uri_component(&next, se, '/', &component);
-+ parse_uri_component(&next, se, "/", &component);
- if (mg_vcmp(&component, ".") == 0) {
- /* Yum. */
- } else if (mg_vcmp(&component, "..") == 0) {
-@@ -4975,8 +5289,78 @@ int mg_normalize_uri_path(const struct m
- out->len = d - cp;
- return 1;
- }
-+
-+int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info,
-+ const struct mg_str *host, unsigned int port,
-+ const struct mg_str *path, const struct mg_str *query,
-+ const struct mg_str *fragment, int normalize_path,
-+ struct mg_str *uri) {
-+ int result = -1;
-+ struct mbuf out;
-+ mbuf_init(&out, 0);
-+
-+ if (scheme != NULL && scheme->len > 0) {
-+ mbuf_append(&out, scheme->p, scheme->len);
-+ mbuf_append(&out, "://", 3);
-+ }
-+
-+ if (user_info != NULL && user_info->len > 0) {
-+ mbuf_append(&out, user_info->p, user_info->len);
-+ mbuf_append(&out, "@", 1);
-+ }
-+
-+ if (host != NULL && host->len > 0) {
-+ mbuf_append(&out, host->p, host->len);
-+ }
-+
-+ if (port != 0) {
-+ char port_str[20];
-+ int port_str_len = sprintf(port_str, ":%u", port);
-+ mbuf_append(&out, port_str, port_str_len);
-+ }
-+
-+ if (path != NULL && path->len > 0) {
-+ if (normalize_path) {
-+ struct mg_str npath = mg_strdup(*path);
-+ if (npath.len != path->len) goto out;
-+ if (!mg_normalize_uri_path(path, &npath)) {
-+ free((void *) npath.p);
-+ goto out;
-+ }
-+ mbuf_append(&out, npath.p, npath.len);
-+ free((void *) npath.p);
-+ } else {
-+ mbuf_append(&out, path->p, path->len);
-+ }
-+ } else if (normalize_path) {
-+ mbuf_append(&out, "/", 1);
-+ }
-+
-+ if (query != NULL && query->len > 0) {
-+ mbuf_append(&out, "?", 1);
-+ mbuf_append(&out, query->p, query->len);
-+ }
-+
-+ if (fragment != NULL && fragment->len > 0) {
-+ mbuf_append(&out, "#", 1);
-+ mbuf_append(&out, fragment->p, fragment->len);
-+ }
-+
-+ result = 0;
-+
-+out:
-+ if (result == 0) {
-+ uri->p = out.buf;
-+ uri->len = out.len;
-+ } else {
-+ mbuf_free(&out);
-+ uri->p = NULL;
-+ uri->len = 0;
-+ }
-+ return result;
-+}
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http.c"
-+#line 1 "mongoose/src/mg_http.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -4985,10 +5369,89 @@ int mg_normalize_uri_path(const struct m
-
- #if MG_ENABLE_HTTP
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
--/* Amalgamated: #include "common/sha1.h" */
--/* Amalgamated: #include "common/md5.h" */
-+/* Amalgamated: #include "common/cs_md5.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_util.h" */
-+
-+/* altbuf {{{ */
-+
-+/*
-+ * Alternate buffer: fills the client-provided buffer with data; and if it's
-+ * not large enough, allocates another buffer (via mbuf), similar to asprintf.
-+ */
-+struct altbuf {
-+ struct mbuf m;
-+ char *user_buf;
-+ size_t len;
-+ size_t user_buf_size;
-+};
-+
-+/*
-+ * Initializes altbuf; `buf`, `buf_size` is the client-provided buffer.
-+ */
-+MG_INTERNAL void altbuf_init(struct altbuf *ab, char *buf, size_t buf_size) {
-+ mbuf_init(&ab->m, 0);
-+ ab->user_buf = buf;
-+ ab->user_buf_size = buf_size;
-+ ab->len = 0;
-+}
-+
-+/*
-+ * Appends a single char to the altbuf.
-+ */
-+MG_INTERNAL void altbuf_append(struct altbuf *ab, char c) {
-+ if (ab->len < ab->user_buf_size) {
-+ /* The data fits into the original buffer */
-+ ab->user_buf[ab->len++] = c;
-+ } else {
-+ /* The data can't fit into the original buffer, so write it to mbuf. */
-+
-+ /*
-+ * First of all, see if that's the first byte which overflows the original
-+ * buffer: if so, copy the existing data from there to a newly allocated
-+ * mbuf.
-+ */
-+ if (ab->len > 0 && ab->m.len == 0) {
-+ mbuf_append(&ab->m, ab->user_buf, ab->len);
-+ }
-+
-+ mbuf_append(&ab->m, &c, 1);
-+ ab->len = ab->m.len;
-+ }
-+}
-+
-+/*
-+ * Resets any data previously appended to altbuf.
-+ */
-+MG_INTERNAL void altbuf_reset(struct altbuf *ab) {
-+ mbuf_free(&ab->m);
-+ ab->len = 0;
-+}
-+
-+/*
-+ * Returns whether the additional buffer was allocated (and thus the data
-+ * is in the mbuf, not the client-provided buffer)
-+ */
-+MG_INTERNAL int altbuf_reallocated(struct altbuf *ab) {
-+ return ab->len > ab->user_buf_size;
-+}
-+
-+/*
-+ * Returns the actual buffer with data, either the client-provided or a newly
-+ * allocated one. If `trim` is non-zero, mbuf-backed buffer is trimmed first.
-+ */
-+MG_INTERNAL char *altbuf_get_buf(struct altbuf *ab, int trim) {
-+ if (altbuf_reallocated(ab)) {
-+ if (trim) {
-+ mbuf_trim(&ab->m);
-+ }
-+ return ab->m.buf;
-+ } else {
-+ return ab->user_buf;
-+ }
-+}
-+
-+/* }}} */
-
- static const char *mg_version_header = "Mongoose/" MG_VERSION;
-
-@@ -5014,9 +5477,14 @@ struct mg_http_proto_data_chuncked {
-
- struct mg_http_endpoint {
- struct mg_http_endpoint *next;
-- const char *name;
-- size_t name_len;
-+ struct mg_str uri_pattern; /* owned */
-+ char *auth_domain; /* owned */
-+ char *auth_file; /* owned */
-+
- mg_event_handler_t handler;
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ void *user_data;
-+#endif
- };
-
- enum mg_http_multipart_stream_state {
-@@ -5044,6 +5512,16 @@ struct mg_reverse_proxy_data {
- struct mg_connection *linked_conn;
- };
-
-+struct mg_ws_proto_data {
-+ /*
-+ * Defragmented size of the frame so far.
-+ *
-+ * First byte of nc->recv_mbuf.buf is an op, the rest of the data is
-+ * defragmented data.
-+ */
-+ size_t reass_len;
-+};
-+
- struct mg_http_proto_data {
- #if MG_ENABLE_FILESYSTEM
- struct mg_http_proto_data_file file;
-@@ -5054,17 +5532,22 @@ struct mg_http_proto_data {
- #if MG_ENABLE_HTTP_STREAMING_MULTIPART
- struct mg_http_multipart_stream mp_stream;
- #endif
-+#if MG_ENABLE_HTTP_WEBSOCKET
-+ struct mg_ws_proto_data ws_data;
-+#endif
- struct mg_http_proto_data_chuncked chunk;
- struct mg_http_endpoint *endpoints;
- mg_event_handler_t endpoint_handler;
- struct mg_reverse_proxy_data reverse_proxy_data;
-+ size_t rcvd; /* How many bytes we have received. */
- };
-
- static void mg_http_conn_destructor(void *proto_data);
- struct mg_connection *mg_connect_http_base(
-- struct mg_mgr *mgr, mg_event_handler_t ev_handler,
-- struct mg_connect_opts opts, const char *schema, const char *schema_ssl,
-- const char *url, const char **path, char **user, char **pass, char **addr);
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ struct mg_connect_opts opts, const char *scheme1, const char *scheme2,
-+ const char *scheme_ssl1, const char *scheme_ssl2, const char *url,
-+ struct mg_str *path, struct mg_str *user_info, struct mg_str *host);
-
- static struct mg_http_proto_data *mg_http_get_proto_data(
- struct mg_connection *c) {
-@@ -5079,9 +5562,9 @@ static struct mg_http_proto_data *mg_htt
- #if MG_ENABLE_HTTP_STREAMING_MULTIPART
- static void mg_http_free_proto_data_mp_stream(
- struct mg_http_multipart_stream *mp) {
-- free((void *) mp->boundary);
-- free((void *) mp->var_name);
-- free((void *) mp->file_name);
-+ MG_FREE((void *) mp->boundary);
-+ MG_FREE((void *) mp->var_name);
-+ MG_FREE((void *) mp->file_name);
- memset(mp, 0, sizeof(*mp));
- }
- #endif
-@@ -5102,8 +5585,10 @@ static void mg_http_free_proto_data_endp
-
- while (current != NULL) {
- struct mg_http_endpoint *tmp = current->next;
-- free((void *) current->name);
-- free(current);
-+ MG_FREE((void *) current->uri_pattern.p);
-+ MG_FREE((void *) current->auth_domain);
-+ MG_FREE((void *) current->auth_file);
-+ MG_FREE(current);
- current = tmp;
- }
-
-@@ -5139,7 +5624,7 @@ static void mg_http_conn_destructor(void
- #endif
- mg_http_free_proto_data_endpoints(&pd->endpoints);
- mg_http_free_reverse_proxy_data(&pd->reverse_proxy_data);
-- free(proto_data);
-+ MG_FREE(proto_data);
- }
-
- #if MG_ENABLE_FILESYSTEM
-@@ -5380,28 +5865,33 @@ static void mg_http_transfer_file_data(s
-
- if (pd->file.type == DATA_FILE) {
- struct mbuf *io = &nc->send_mbuf;
-- if (io->len < sizeof(buf)) {
-- to_read = sizeof(buf) - io->len;
-+ if (io->len >= MG_MAX_HTTP_SEND_MBUF) {
-+ to_read = 0;
-+ } else {
-+ to_read = MG_MAX_HTTP_SEND_MBUF - io->len;
- }
--
-- if (left > 0 && to_read > left) {
-+ if (to_read > left) {
- to_read = left;
- }
--
-- if (to_read == 0) {
-- /* Rate limiting. send_mbuf is too full, wait until it's drained. */
-- } else if (pd->file.sent < pd->file.cl &&
-- (n = fread(buf, 1, to_read, pd->file.fp)) > 0) {
-- mg_send(nc, buf, n);
-- pd->file.sent += n;
-+ if (to_read > 0) {
-+ n = mg_fread(buf, 1, to_read, pd->file.fp);
-+ if (n > 0) {
-+ mg_send(nc, buf, n);
-+ pd->file.sent += n;
-+ DBG(("%p sent %d (total %d)", nc, (int) n, (int) pd->file.sent));
-+ }
- } else {
-+ /* Rate-limited */
-+ }
-+ if (pd->file.sent >= pd->file.cl) {
-+ LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent));
- if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
- mg_http_free_proto_data_file(&pd->file);
- }
- } else if (pd->file.type == DATA_PUT) {
- struct mbuf *io = &nc->recv_mbuf;
- size_t to_write = left <= 0 ? 0 : left < io->len ? (size_t) left : io->len;
-- size_t n = fwrite(io->buf, 1, to_write, pd->file.fp);
-+ size_t n = mg_fwrite(io->buf, 1, to_write, pd->file.fp);
- if (n > 0) {
- mbuf_remove(io, n);
- pd->file.sent += n;
-@@ -5498,7 +5988,7 @@ MG_INTERNAL size_t mg_handle_chunked(str
-
- /* Send MG_EV_HTTP_CHUNK event */
- nc->flags &= ~MG_F_DELETE_CHUNK;
-- mg_call(nc, nc->handler, MG_EV_HTTP_CHUNK, hm);
-+ mg_call(nc, nc->handler, nc->user_data, MG_EV_HTTP_CHUNK, hm);
-
- /* Delete processed data if user set MG_F_DELETE_CHUNK flag */
- if (nc->flags & MG_F_DELETE_CHUNK) {
-@@ -5510,17 +6000,19 @@ MG_INTERNAL size_t mg_handle_chunked(str
- }
-
- if (zero_chunk_received) {
-- hm->message.len = (size_t) pd->chunk.body_len + blen - i;
-+ /* Total message size is len(body) + len(headers) */
-+ hm->message.len =
-+ (size_t) pd->chunk.body_len + blen - i + (hm->body.p - hm->message.p);
- }
- }
-
- return body_len;
- }
-
--static mg_event_handler_t mg_http_get_endpoint_handler(
-- struct mg_connection *nc, struct mg_str *uri_path) {
-+struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc,
-+ struct mg_str *uri_path) {
- struct mg_http_proto_data *pd;
-- mg_event_handler_t ret = NULL;
-+ struct mg_http_endpoint *ret = NULL;
- int matched, matched_max = 0;
- struct mg_http_endpoint *ep;
-
-@@ -5532,11 +6024,10 @@ static mg_event_handler_t mg_http_get_en
-
- ep = pd->endpoints;
- while (ep != NULL) {
-- const struct mg_str name_s = {ep->name, ep->name_len};
-- if ((matched = mg_match_prefix_n(name_s, *uri_path)) != -1) {
-+ if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) {
- if (matched > matched_max) {
- /* Looking for the longest suitable handler */
-- ret = ep->handler;
-+ ret = ep;
- matched_max = matched;
- }
- }
-@@ -5547,20 +6038,6 @@ static mg_event_handler_t mg_http_get_en
- return ret;
- }
-
--static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
-- struct http_message *hm) {
-- struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
--
-- if (pd->endpoint_handler == NULL || ev == MG_EV_HTTP_REQUEST) {
-- pd->endpoint_handler =
-- ev == MG_EV_HTTP_REQUEST
-- ? mg_http_get_endpoint_handler(nc->listener, &hm->uri)
-- : NULL;
-- }
-- mg_call(nc, pd->endpoint_handler ? pd->endpoint_handler : nc->handler, ev,
-- hm);
--}
--
- #if MG_ENABLE_HTTP_STREAMING_MULTIPART
- static void mg_http_multipart_continue(struct mg_connection *nc);
-
-@@ -5569,26 +6046,42 @@ static void mg_http_multipart_begin(stru
-
- #endif
-
-+static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
-+ struct http_message *hm);
-+
-+static void deliver_chunk(struct mg_connection *c, struct http_message *hm,
-+ int req_len) {
-+ /* Incomplete message received. Send MG_EV_HTTP_CHUNK event */
-+ hm->body.len = c->recv_mbuf.len - req_len;
-+ c->flags &= ~MG_F_DELETE_CHUNK;
-+ mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm);
-+ /* Delete processed data if user set MG_F_DELETE_CHUNK flag */
-+ if (c->flags & MG_F_DELETE_CHUNK) c->recv_mbuf.len = req_len;
-+}
-+
- /*
- * lx106 compiler has a bug (TODO(mkm) report and insert tracking bug here)
- * If a big structure is declared in a big function, lx106 gcc will make it
- * even bigger (round up to 4k, from 700 bytes of actual size).
- */
- #ifdef __xtensa__
--static void mg_http_handler2(struct mg_connection *nc, int ev, void *ev_data,
-+static void mg_http_handler2(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data),
- struct http_message *hm) __attribute__((noinline));
-
--void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
-+void mg_http_handler(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data)) {
- struct http_message hm;
-- mg_http_handler2(nc, ev, ev_data, &hm);
-+ mg_http_handler2(nc, ev, ev_data MG_UD_ARG(user_data), &hm);
- }
-
--static void mg_http_handler2(struct mg_connection *nc, int ev, void *ev_data,
-+static void mg_http_handler2(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data),
- struct http_message *hm) {
- #else /* !__XTENSA__ */
--void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) {
-- struct http_message shm;
-- struct http_message *hm = &shm;
-+void mg_http_handler(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data)) {
-+ struct http_message shm, *hm = &shm;
- #endif /* __XTENSA__ */
- struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
- struct mbuf *io = &nc->recv_mbuf;
-@@ -5598,6 +6091,13 @@ void mg_http_handler(struct mg_connectio
- struct mg_str *vec;
- #endif
- if (ev == MG_EV_CLOSE) {
-+#if MG_ENABLE_HTTP_CGI
-+ /* Close associated CGI forwarder connection */
-+ if (pd->cgi.cgi_nc != NULL) {
-+ pd->cgi.cgi_nc->user_data = NULL;
-+ pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ }
-+#endif
- #if MG_ENABLE_HTTP_STREAMING_MULTIPART
- if (pd->mp_stream.boundary != NULL) {
- /*
-@@ -5610,14 +6110,15 @@ void mg_http_handler(struct mg_connectio
- mp.var_name = pd->mp_stream.var_name;
- mp.file_name = pd->mp_stream.file_name;
- mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler),
-- MG_EV_HTTP_PART_END, &mp);
-+ nc->user_data, MG_EV_HTTP_PART_END, &mp);
- mp.var_name = NULL;
- mp.file_name = NULL;
- mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler),
-- MG_EV_HTTP_MULTIPART_REQUEST_END, &mp);
-+ nc->user_data, MG_EV_HTTP_MULTIPART_REQUEST_END, &mp);
- } else
- #endif
-- if (io->len > 0 && mg_parse_http(io->buf, io->len, hm, is_req) > 0) {
-+ if (io->len > 0 &&
-+ (req_len = mg_parse_http(io->buf, io->len, hm, is_req)) > 0) {
- /*
- * For HTTP messages without Content-Length, always send HTTP message
- * before MG_EV_CLOSE message.
-@@ -5625,8 +6126,10 @@ void mg_http_handler(struct mg_connectio
- int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
- hm->message.len = io->len;
- hm->body.len = io->buf + io->len - hm->body.p;
-+ deliver_chunk(nc, hm, req_len);
- mg_http_call_endpoint_handler(nc, ev2, hm);
- }
-+ pd->rcvd = 0;
- }
-
- #if MG_ENABLE_FILESYSTEM
-@@ -5635,10 +6138,11 @@ void mg_http_handler(struct mg_connectio
- }
- #endif
-
-- mg_call(nc, nc->handler, ev, ev_data);
-+ mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
-
- if (ev == MG_EV_RECV) {
- struct mg_str *s;
-+ pd->rcvd += *(int *) ev_data;
-
- #if MG_ENABLE_HTTP_STREAMING_MULTIPART
- if (pd->mp_stream.boundary != NULL) {
-@@ -5680,11 +6184,12 @@ void mg_http_handler(struct mg_connectio
- mbuf_remove(io, req_len);
- nc->proto_handler = mg_ws_handler;
- nc->flags |= MG_F_IS_WEBSOCKET;
-- mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL);
-- mg_ws_handler(nc, MG_EV_RECV, ev_data);
-+ mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
-+ NULL);
-+ mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
- } else if (nc->listener != NULL &&
- (vec = mg_get_http_header(hm, "Sec-WebSocket-Key")) != NULL) {
-- mg_event_handler_t handler;
-+ struct mg_http_endpoint *ep;
-
- /* This is a websocket request. Switch protocol handlers. */
- mbuf_remove(io, req_len);
-@@ -5696,79 +6201,52 @@ void mg_http_handler(struct mg_connectio
- * deliver subsequent websocket events to this handler after the
- * protocol switch.
- */
-- handler = mg_http_get_endpoint_handler(nc->listener, &hm->uri);
-- if (handler != NULL) {
-- nc->handler = handler;
-+ ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri);
-+ if (ep != NULL) {
-+ nc->handler = ep->handler;
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ nc->user_data = ep->user_data;
-+#endif
- }
-
- /* Send handshake */
-- mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST, hm);
-+ mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_REQUEST,
-+ hm);
- if (!(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_SEND_AND_CLOSE))) {
- if (nc->send_mbuf.len == 0) {
-- mg_ws_handshake(nc, vec);
-+ mg_ws_handshake(nc, vec, hm);
- }
-- mg_call(nc, nc->handler, MG_EV_WEBSOCKET_HANDSHAKE_DONE, NULL);
-- mg_ws_handler(nc, MG_EV_RECV, ev_data);
-+ mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_HANDSHAKE_DONE,
-+ NULL);
-+ mg_ws_handler(nc, MG_EV_RECV, ev_data MG_UD_ARG(user_data));
- }
- }
- #endif /* MG_ENABLE_HTTP_WEBSOCKET */
-- else if (hm->message.len <= io->len) {
-- int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
--
--/* Whole HTTP message is fully buffered, call event handler */
--
--#if MG_ENABLE_JAVASCRIPT
-- v7_val_t v1, v2, headers, req, args, res;
-- struct v7 *v7 = nc->mgr->v7;
-- const char *ev_name = trigger_ev == MG_EV_HTTP_REPLY ? "onsnd" : "onrcv";
-- int i, js_callback_handled_request = 0;
--
-- if (v7 != NULL) {
-- /* Lookup JS callback */
-- v1 = v7_get(v7, v7_get_global(v7), "Http", ~0);
-- v2 = v7_get(v7, v1, ev_name, ~0);
--
-- /* Create callback params. TODO(lsm): own/disown those */
-- args = v7_mk_array(v7);
-- req = v7_mk_object(v7);
-- headers = v7_mk_object(v7);
--
-- /* Populate request object */
-- v7_set(v7, req, "method", ~0,
-- v7_mk_string(v7, hm->method.p, hm->method.len, 1));
-- v7_set(v7, req, "uri", ~0, v7_mk_string(v7, hm->uri.p, hm->uri.len, 1));
-- v7_set(v7, req, "body", ~0,
-- v7_mk_string(v7, hm->body.p, hm->body.len, 1));
-- v7_set(v7, req, "headers", ~0, headers);
-- for (i = 0; hm->header_names[i].len > 0; i++) {
-- const struct mg_str *name = &hm->header_names[i];
-- const struct mg_str *value = &hm->header_values[i];
-- v7_set(v7, headers, name->p, name->len,
-- v7_mk_string(v7, value->p, value->len, 1));
-- }
--
-- /* Invoke callback. TODO(lsm): report errors */
-- v7_array_push(v7, args, v7_mk_foreign(v7, nc));
-- v7_array_push(v7, args, req);
-- if (v7_apply(v7, v2, V7_UNDEFINED, args, &res) == V7_OK &&
-- v7_is_truthy(v7, res)) {
-- js_callback_handled_request++;
-- }
-- }
--
-- /* If JS callback returns true, stop request processing */
-- if (js_callback_handled_request) {
-- nc->flags |= MG_F_SEND_AND_CLOSE;
-- } else {
-- mg_http_call_endpoint_handler(nc, trigger_ev, hm);
-+ else if (hm->message.len > pd->rcvd) {
-+ /* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */
-+ deliver_chunk(nc, hm, req_len);
-+ if (nc->recv_mbuf_limit > 0 && nc->recv_mbuf.len >= nc->recv_mbuf_limit) {
-+ LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit "
-+ "%lu bytes, and not drained, closing",
-+ nc, (unsigned long) nc->recv_mbuf.len,
-+ (unsigned long) nc->recv_mbuf_limit));
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
- }
--#else
-+ } else {
-+ /* We did receive all HTTP body. */
-+ int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
-+ char addr[32];
-+ mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
-+ MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
-+ DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p,
-+ (int) hm->uri.len, hm->uri.p));
-+ deliver_chunk(nc, hm, req_len);
-+ /* Whole HTTP message is fully buffered, call event handler */
- mg_http_call_endpoint_handler(nc, trigger_ev, hm);
--#endif
- mbuf_remove(io, hm->message.len);
-+ pd->rcvd = 0;
- }
- }
-- (void) pd;
- }
-
- static size_t mg_get_line_len(const char *buf, size_t buf_len) {
-@@ -5784,14 +6262,10 @@ static void mg_http_multipart_begin(stru
- struct mg_str *ct;
- struct mbuf *io = &nc->recv_mbuf;
-
-- char boundary[100];
-+ char boundary_buf[100];
-+ char *boundary = boundary_buf;
- int boundary_len;
-
-- if (nc->listener == NULL) {
-- /* No streaming for replies now */
-- goto exit_mp;
-- }
--
- ct = mg_get_http_header(hm, "Content-Type");
- if (ct == NULL) {
- /* We need more data - or it isn't multipart mesage */
-@@ -5804,7 +6278,7 @@ static void mg_http_multipart_begin(stru
- }
-
- boundary_len =
-- mg_http_parse_header(ct, "boundary", boundary, sizeof(boundary));
-+ mg_http_parse_header2(ct, "boundary", &boundary, sizeof(boundary_buf));
- if (boundary_len == 0) {
- /*
- * Content type is multipart, but there is no boundary,
-@@ -5824,22 +6298,24 @@ static void mg_http_multipart_begin(stru
- */
- nc->flags |= MG_F_CLOSE_IMMEDIATELY;
- } else {
-+ struct mg_http_endpoint *ep = NULL;
- pd->mp_stream.state = MPS_BEGIN;
- pd->mp_stream.boundary = strdup(boundary);
- pd->mp_stream.boundary_len = strlen(boundary);
- pd->mp_stream.var_name = pd->mp_stream.file_name = NULL;
-+ pd->endpoint_handler = nc->handler;
-
-- pd->endpoint_handler = mg_http_get_endpoint_handler(nc->listener, &hm->uri);
-- if (pd->endpoint_handler == NULL) {
-- pd->endpoint_handler = nc->handler;
-+ ep = mg_http_get_endpoint_handler(nc->listener, &hm->uri);
-+ if (ep != NULL) {
-+ pd->endpoint_handler = ep->handler;
- }
-
-- mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_MULTIPART_REQUEST, hm);
-+ mg_http_call_endpoint_handler(nc, MG_EV_HTTP_MULTIPART_REQUEST, hm);
-
- mbuf_remove(io, req_len);
- }
- exit_mp:
-- ;
-+ if (boundary != boundary_buf) MG_FREE(boundary);
- }
-
- #define CONTENT_DISPOSITION "Content-Disposition: "
-@@ -5855,7 +6331,7 @@ static void mg_http_multipart_call_handl
- mp.user_data = pd->mp_stream.user_data;
- mp.data.p = data;
- mp.data.len = data_len;
-- mg_call(c, pd->endpoint_handler, ev, &mp);
-+ mg_call(c, pd->endpoint_handler, c->user_data, ev, &mp);
- pd->mp_stream.user_data = mp.user_data;
- }
-
-@@ -5876,9 +6352,9 @@ static int mg_http_multipart_finalize(st
- struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
-
- mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
-- free((void *) pd->mp_stream.file_name);
-+ MG_FREE((void *) pd->mp_stream.file_name);
- pd->mp_stream.file_name = NULL;
-- free((void *) pd->mp_stream.var_name);
-+ MG_FREE((void *) pd->mp_stream.var_name);
- pd->mp_stream.var_name = NULL;
- mg_http_multipart_call_handler(c, MG_EV_HTTP_MULTIPART_REQUEST_END, NULL, 0);
- mg_http_free_proto_data_mp_stream(&pd->mp_stream);
-@@ -5892,6 +6368,12 @@ static int mg_http_multipart_wait_for_bo
- struct mbuf *io = &c->recv_mbuf;
- struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
-
-+ if (pd->mp_stream.boundary == NULL) {
-+ pd->mp_stream.state = MPS_FINALIZE;
-+ DBG(("Invalid request: boundary not initialized"));
-+ return 0;
-+ }
-+
- if ((int) io->len < pd->mp_stream.boundary_len + 2) {
- return 0;
- }
-@@ -5915,17 +6397,24 @@ static int mg_http_multipart_wait_for_bo
- return 1;
- }
-
-+static void mg_http_parse_header_internal(struct mg_str *hdr,
-+ const char *var_name,
-+ struct altbuf *ab);
-+
- static int mg_http_multipart_process_boundary(struct mg_connection *c) {
- int data_size;
- const char *boundary, *block_begin;
- struct mbuf *io = &c->recv_mbuf;
- struct mg_http_proto_data *pd = mg_http_get_proto_data(c);
-- char file_name[100], var_name[100];
-+ struct altbuf ab_file_name, ab_var_name;
- int line_len;
- boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len);
- block_begin = boundary + pd->mp_stream.boundary_len + 2;
- data_size = io->len - (block_begin - io->buf);
-
-+ altbuf_init(&ab_file_name, NULL, 0);
-+ altbuf_init(&ab_var_name, NULL, 0);
-+
- while (data_size > 0 &&
- (line_len = mg_get_line_len(block_begin, data_size)) != 0) {
- if (line_len > (int) sizeof(CONTENT_DISPOSITION) &&
-@@ -5935,11 +6424,16 @@ static int mg_http_multipart_process_bou
-
- header.p = block_begin + sizeof(CONTENT_DISPOSITION) - 1;
- header.len = line_len - sizeof(CONTENT_DISPOSITION) - 1;
-- mg_http_parse_header(&header, "name", var_name, sizeof(var_name) - 2);
-- mg_http_parse_header(&header, "filename", file_name,
-- sizeof(file_name) - 2);
-+
-+ altbuf_reset(&ab_var_name);
-+ mg_http_parse_header_internal(&header, "name", &ab_var_name);
-+
-+ altbuf_reset(&ab_file_name);
-+ mg_http_parse_header_internal(&header, "filename", &ab_file_name);
-+
- block_begin += line_len;
- data_size -= line_len;
-+
- continue;
- }
-
-@@ -5950,10 +6444,16 @@ static int mg_http_multipart_process_bou
- mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_END, NULL, 0);
- }
-
-- free((void *) pd->mp_stream.file_name);
-- pd->mp_stream.file_name = strdup(file_name);
-- free((void *) pd->mp_stream.var_name);
-- pd->mp_stream.var_name = strdup(var_name);
-+ /* Reserve 2 bytes for "\r\n" in file_name and var_name */
-+ altbuf_append(&ab_file_name, '\0');
-+ altbuf_append(&ab_file_name, '\0');
-+ altbuf_append(&ab_var_name, '\0');
-+ altbuf_append(&ab_var_name, '\0');
-+
-+ MG_FREE((void *) pd->mp_stream.file_name);
-+ pd->mp_stream.file_name = altbuf_get_buf(&ab_file_name, 1 /* trim */);
-+ MG_FREE((void *) pd->mp_stream.var_name);
-+ pd->mp_stream.var_name = altbuf_get_buf(&ab_var_name, 1 /* trim */);
-
- mg_http_multipart_call_handler(c, MG_EV_HTTP_PART_BEGIN, NULL, 0);
- pd->mp_stream.state = MPS_WAITING_FOR_CHUNK;
-@@ -5966,6 +6466,9 @@ static int mg_http_multipart_process_bou
-
- pd->mp_stream.state = MPS_WAITING_FOR_BOUNDARY;
-
-+ altbuf_reset(&ab_var_name);
-+ altbuf_reset(&ab_file_name);
-+
- return 0;
- }
-
-@@ -6238,7 +6741,7 @@ void mg_send_head(struct mg_connection *
- void mg_http_send_error(struct mg_connection *nc, int code,
- const char *reason) {
- if (!reason) reason = mg_status_message(code);
-- DBG(("%p %d %s", nc, code, reason));
-+ LOG(LL_DEBUG, ("%p %d %s", nc, code, reason));
- mg_send_head(nc, code, strlen(reason),
- "Content-Type: text/plain\r\nConnection: close");
- mg_send(nc, reason, strlen(reason));
-@@ -6282,7 +6785,7 @@ void mg_http_serve_file(struct mg_connec
- const struct mg_str extra_headers) {
- struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
- cs_stat_t st;
-- DBG(("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p));
-+ LOG(LL_DEBUG, ("%p [%s] %.*s", nc, path, (int) mime_type.len, mime_type.p));
- if (mg_stat(path, &st) != 0 || (pd->file.fp = mg_fopen(path, "rb")) == NULL) {
- int code, err = mg_get_errno();
- switch (err) {
-@@ -6423,6 +6926,14 @@ int mg_get_http_var(const struct mg_str
- size_t name_len;
- int len;
-
-+ /*
-+ * According to the documentation function returns negative
-+ * value in case of error. For debug purposes it returns:
-+ * -1 - src is wrong (NUUL)
-+ * -2 - dst is wrong (NULL)
-+ * -3 - failed to decode url or dst is to small
-+ * -4 - name does not exist
-+ */
- if (dst == NULL || dst_len == 0) {
- len = -2;
- } else if (buf->p == NULL || name == NULL || buf->len == 0) {
-@@ -6431,7 +6942,7 @@ int mg_get_http_var(const struct mg_str
- } else {
- name_len = strlen(name);
- e = buf->p + buf->len;
-- len = -1;
-+ len = -4;
- dst[0] = '\0';
-
- for (p = buf->p; p + name_len < e; p++) {
-@@ -6443,8 +6954,9 @@ int mg_get_http_var(const struct mg_str
- s = e;
- }
- len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
-+ /* -1 means: failed to decode or dst is too small */
- if (len == -1) {
-- len = -2;
-+ len = -3;
- }
- break;
- }
-@@ -6511,18 +7023,16 @@ void mg_printf_html_escape(struct mg_con
- /* LCOV_EXCL_STOP */
- }
-
--int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
-- size_t buf_size) {
-- int ch = ' ', ch1 = ',', len = 0, n = strlen(var_name);
-+static void mg_http_parse_header_internal(struct mg_str *hdr,
-+ const char *var_name,
-+ struct altbuf *ab) {
-+ int ch = ' ', ch1 = ',', n = strlen(var_name);
- const char *p, *end = hdr ? hdr->p + hdr->len : NULL, *s = NULL;
-
-- if (buf != NULL && buf_size > 0) buf[0] = '\0';
-- if (hdr == NULL) return 0;
--
- /* Find where variable starts */
- for (s = hdr->p; s != NULL && s + n < end; s++) {
-- if ((s == hdr->p || s[-1] == ch || s[-1] == ch1) && s[n] == '=' &&
-- !strncmp(s, var_name, n))
-+ if ((s == hdr->p || s[-1] == ch || s[-1] == ch1 || s[-1] == ';') &&
-+ s[n] == '=' && !strncmp(s, var_name, n))
- break;
- }
-
-@@ -6532,19 +7042,51 @@ int mg_http_parse_header(struct mg_str *
- ch = ch1 = *s++;
- }
- p = s;
-- while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
-+ while (p < end && p[0] != ch && p[0] != ch1) {
- if (ch != ' ' && p[0] == '\\' && p[1] == ch) p++;
-- buf[len++] = *p++;
-+ altbuf_append(ab, *p++);
- }
-- if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
-- len = 0;
-- } else {
-- if (len > 0 && s[len - 1] == ',') len--;
-- if (len > 0 && s[len - 1] == ';') len--;
-- buf[len] = '\0';
-+
-+ if (ch != ' ' && *p != ch) {
-+ altbuf_reset(ab);
- }
- }
-
-+ /* If there is some data, append a NUL. */
-+ if (ab->len > 0) {
-+ altbuf_append(ab, '\0');
-+ }
-+}
-+
-+int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
-+ size_t buf_size) {
-+ struct altbuf ab;
-+ altbuf_init(&ab, *buf, buf_size);
-+ if (hdr == NULL) return 0;
-+ if (*buf != NULL && buf_size > 0) *buf[0] = '\0';
-+
-+ mg_http_parse_header_internal(hdr, var_name, &ab);
-+
-+ /*
-+ * Get a (trimmed) buffer, and return a len without a NUL byte which might
-+ * have been added.
-+ */
-+ *buf = altbuf_get_buf(&ab, 1 /* trim */);
-+ return ab.len > 0 ? ab.len - 1 : 0;
-+}
-+
-+int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
-+ size_t buf_size) {
-+ char *buf2 = buf;
-+
-+ int len = mg_http_parse_header2(hdr, var_name, &buf2, buf_size);
-+
-+ if (buf2 != buf) {
-+ /* Buffer was not enough and was reallocated: free it and just return 0 */
-+ MG_FREE(buf2);
-+ return 0;
-+ }
-+
- return len;
- }
-
-@@ -6591,12 +7133,47 @@ static int mg_is_file_hidden(const char
- }
-
- return (exclude_specials && (!strcmp(path, ".") || !strcmp(path, ".."))) ||
-- (p1 != NULL &&
-- mg_match_prefix(p1, strlen(p1), path) == (int) strlen(p1)) ||
-+ (p1 != NULL && mg_match_prefix(p1, strlen(p1), path) == strlen(p1)) ||
- (p2 != NULL && mg_match_prefix(p2, strlen(p2), path) > 0);
- }
-
- #if !MG_DISABLE_HTTP_DIGEST_AUTH
-+
-+#ifndef MG_EXT_MD5
-+void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
-+ const size_t *msg_lens, uint8_t *digest) {
-+ size_t i;
-+ cs_md5_ctx md5_ctx;
-+ cs_md5_init(&md5_ctx);
-+ for (i = 0; i < num_msgs; i++) {
-+ cs_md5_update(&md5_ctx, msgs[i], msg_lens[i]);
-+ }
-+ cs_md5_final(digest, &md5_ctx);
-+}
-+#else
-+extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
-+ const size_t *msg_lens, uint8_t *digest);
-+#endif
-+
-+void cs_md5(char buf[33], ...) {
-+ unsigned char hash[16];
-+ const uint8_t *msgs[20], *p;
-+ size_t msg_lens[20];
-+ size_t num_msgs = 0;
-+ va_list ap;
-+
-+ va_start(ap, buf);
-+ while ((p = va_arg(ap, const unsigned char *) ) != NULL) {
-+ msgs[num_msgs] = p;
-+ msg_lens[num_msgs] = va_arg(ap, size_t);
-+ num_msgs++;
-+ }
-+ va_end(ap);
-+
-+ mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
-+ cs_to_hex(buf, hash, sizeof(hash));
-+}
-+
- static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri,
- size_t uri_len, const char *ha1, size_t ha1_len,
- const char *nonce, size_t nonce_len, const char *nc,
-@@ -6605,7 +7182,6 @@ static void mg_mkmd5resp(const char *met
- static const char colon[] = ":";
- static const size_t one = 1;
- char ha2[33];
--
- cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
- cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
- nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
-@@ -6615,23 +7191,23 @@ static void mg_mkmd5resp(const char *met
- int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
- const char *method, const char *uri,
- const char *auth_domain, const char *user,
-- const char *passwd) {
-+ const char *passwd, const char *nonce) {
- static const char colon[] = ":", qop[] = "auth";
- static const size_t one = 1;
- char ha1[33], resp[33], cnonce[40];
-
-- snprintf(cnonce, sizeof(cnonce), "%x", (unsigned int) mg_time());
-+ snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
- cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
- (size_t) strlen(auth_domain), colon, one, passwd,
- (size_t) strlen(passwd), NULL);
- mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1,
-- cnonce, strlen(cnonce), "1", one, cnonce, strlen(cnonce), qop,
-+ nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
- sizeof(qop) - 1, resp);
- return snprintf(buf, buf_len,
- "Authorization: Digest username=\"%s\","
- "realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
- "nonce=%s,response=%s\r\n",
-- user, auth_domain, uri, qop, cnonce, cnonce, resp);
-+ user, auth_domain, uri, qop, cnonce, nonce, resp);
- }
-
- /*
-@@ -6643,30 +7219,67 @@ int mg_http_create_digest_auth_header(ch
- static int mg_check_nonce(const char *nonce) {
- unsigned long now = (unsigned long) mg_time();
- unsigned long val = (unsigned long) strtoul(nonce, NULL, 16);
-- return now < val || now - val < 3600;
-+ return (now >= val) && (now - val < 60 * 60);
- }
-
- int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
- FILE *fp) {
-+ int ret = 0;
- struct mg_str *hdr;
-- char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
-- char user[50], cnonce[33], response[40], uri[200], qop[20], nc[20], nonce[30];
-- char expected_response[33];
-+ char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
-+ qop_buf[20], nc_buf[20], nonce_buf[16];
-+
-+ char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
-+ *uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
-
- /* Parse "Authorization:" header, fail fast on parse error */
- if (hm == NULL || fp == NULL ||
- (hdr = mg_get_http_header(hm, "Authorization")) == NULL ||
-- mg_http_parse_header(hdr, "username", user, sizeof(user)) == 0 ||
-- mg_http_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce)) == 0 ||
-- mg_http_parse_header(hdr, "response", response, sizeof(response)) == 0 ||
-- mg_http_parse_header(hdr, "uri", uri, sizeof(uri)) == 0 ||
-- mg_http_parse_header(hdr, "qop", qop, sizeof(qop)) == 0 ||
-- mg_http_parse_header(hdr, "nc", nc, sizeof(nc)) == 0 ||
-- mg_http_parse_header(hdr, "nonce", nonce, sizeof(nonce)) == 0 ||
-+ mg_http_parse_header2(hdr, "username", &username, sizeof(username_buf)) ==
-+ 0 ||
-+ mg_http_parse_header2(hdr, "cnonce", &cnonce, sizeof(cnonce_buf)) == 0 ||
-+ mg_http_parse_header2(hdr, "response", &response, sizeof(response_buf)) ==
-+ 0 ||
-+ mg_http_parse_header2(hdr, "uri", &uri, sizeof(uri_buf)) == 0 ||
-+ mg_http_parse_header2(hdr, "qop", &qop, sizeof(qop_buf)) == 0 ||
-+ mg_http_parse_header2(hdr, "nc", &nc, sizeof(nc_buf)) == 0 ||
-+ mg_http_parse_header2(hdr, "nonce", &nonce, sizeof(nonce_buf)) == 0 ||
- mg_check_nonce(nonce) == 0) {
-- return 0;
-+ ret = 0;
-+ goto clean;
- }
-
-+ /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
-+
-+ ret = mg_check_digest_auth(
-+ hm->method,
-+ mg_mk_str_n(
-+ hm->uri.p,
-+ hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)),
-+ mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
-+ mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
-+ fp);
-+
-+clean:
-+ if (username != username_buf) MG_FREE(username);
-+ if (cnonce != cnonce_buf) MG_FREE(cnonce);
-+ if (response != response_buf) MG_FREE(response);
-+ if (uri != uri_buf) MG_FREE(uri);
-+ if (qop != qop_buf) MG_FREE(qop);
-+ if (nc != nc_buf) MG_FREE(nc);
-+ if (nonce != nonce_buf) MG_FREE(nonce);
-+
-+ return ret;
-+}
-+
-+int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
-+ struct mg_str username, struct mg_str cnonce,
-+ struct mg_str response, struct mg_str qop,
-+ struct mg_str nc, struct mg_str nonce,
-+ struct mg_str auth_domain, FILE *fp) {
-+ char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
-+ char expected_response[33];
-+
- /*
- * Read passwords file line by line. If should have htdigest format,
- * i.e. each line should be a colon-separated sequence:
-@@ -6674,16 +7287,16 @@ int mg_http_check_digest_auth(struct htt
- */
- while (fgets(buf, sizeof(buf), fp) != NULL) {
- if (sscanf(buf, "%[^:]:%[^:]:%s", f_user, f_domain, f_ha1) == 3 &&
-- strcmp(user, f_user) == 0 &&
-- /* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
-- strcmp(auth_domain, f_domain) == 0) {
-- /* User and domain matched, check the password */
-- mg_mkmd5resp(
-- hm->method.p, hm->method.len, hm->uri.p,
-- hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0),
-- f_ha1, strlen(f_ha1), nonce, strlen(nonce), nc, strlen(nc), cnonce,
-- strlen(cnonce), qop, strlen(qop), expected_response);
-- return mg_casecmp(response, expected_response) == 0;
-+ mg_vcmp(&username, f_user) == 0 &&
-+ mg_vcmp(&auth_domain, f_domain) == 0) {
-+ /* Username and domain matched, check the password */
-+ mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
-+ nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
-+ qop.p, qop.len, expected_response);
-+ LOG(LL_DEBUG,
-+ ("%.*s %s %.*s %s", (int) username.len, username.p, f_domain,
-+ (int) response.len, response.p, expected_response));
-+ return mg_ncasecmp(response.p, expected_response, response.len) == 0;
- }
- }
-
-@@ -6691,25 +7304,25 @@ int mg_http_check_digest_auth(struct htt
- return 0;
- }
-
--static int mg_is_authorized(struct http_message *hm, const char *path,
-- int is_directory, const char *domain,
-- const char *passwords_file,
-- int is_global_pass_file) {
-+int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
-+ const char *domain, const char *passwords_file,
-+ int flags) {
- char buf[MG_MAX_PATH];
- const char *p;
- FILE *fp;
- int authorized = 1;
-
- if (domain != NULL && passwords_file != NULL) {
-- if (is_global_pass_file) {
-+ if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
- fp = mg_fopen(passwords_file, "r");
-- } else if (is_directory) {
-- snprintf(buf, sizeof(buf), "%s%c%s", path, DIRSEP, passwords_file);
-+ } else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
-+ snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
-+ passwords_file);
- fp = mg_fopen(buf, "r");
- } else {
-- p = strrchr(path, DIRSEP);
-- if (p == NULL) p = path;
-- snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path), path, DIRSEP,
-+ p = strrchr(path.p, DIRSEP);
-+ if (p == NULL) p = path.p;
-+ snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
- passwords_file);
- fp = mg_fopen(buf, "r");
- }
-@@ -6717,51 +7330,29 @@ static int mg_is_authorized(struct http_
- if (fp != NULL) {
- authorized = mg_http_check_digest_auth(hm, domain, fp);
- fclose(fp);
-+ } else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
-+ authorized = 0;
- }
- }
-
-- DBG(("%s %s %d %d", path, passwords_file ? passwords_file : "",
-- is_global_pass_file, authorized));
-+ LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p,
-+ passwords_file ? passwords_file : "", flags, authorized));
- return authorized;
- }
- #else
--static int mg_is_authorized(struct http_message *hm, const char *path,
-- int is_directory, const char *domain,
-- const char *passwords_file,
-- int is_global_pass_file) {
-+int mg_http_is_authorized(struct http_message *hm, const struct mg_str path,
-+ const char *domain, const char *passwords_file,
-+ int flags) {
- (void) hm;
- (void) path;
-- (void) is_directory;
- (void) domain;
- (void) passwords_file;
-- (void) is_global_pass_file;
-+ (void) flags;
- return 1;
- }
- #endif
-
- #if MG_ENABLE_DIRECTORY_LISTING
--static size_t mg_url_encode(const char *src, size_t s_len, char *dst,
-- size_t dst_len) {
-- static const char *dont_escape = "._-$,;~()/";
-- static const char *hex = "0123456789abcdef";
-- size_t i = 0, j = 0;
--
-- for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) {
-- if (isalnum(*(const unsigned char *) (src + i)) ||
-- strchr(dont_escape, *(const unsigned char *) (src + i)) != NULL) {
-- dst[j] = src[i];
-- } else if (j + 3 < dst_len) {
-- dst[j] = '%';
-- dst[j + 1] = hex[(*(const unsigned char *) (src + i)) >> 4];
-- dst[j + 2] = hex[(*(const unsigned char *) (src + i)) & 0xf];
-- j += 2;
-- }
-- }
--
-- dst[j] = '\0';
-- return j;
--}
--
- static void mg_escape(const char *src, char *dst, size_t dst_len) {
- size_t n = 0;
- while (*src != '\0' && n + 5 < dst_len) {
-@@ -6777,10 +7368,11 @@ static void mg_escape(const char *src, c
-
- static void mg_print_dir_entry(struct mg_connection *nc, const char *file_name,
- cs_stat_t *stp) {
-- char size[64], mod[64], href[MAX_PATH_SIZE * 3], path[MAX_PATH_SIZE];
-+ char size[64], mod[64], path[MG_MAX_PATH];
- int64_t fsize = stp->st_size;
- int is_dir = S_ISDIR(stp->st_mode);
- const char *slash = is_dir ? "/" : "";
-+ struct mg_str href;
-
- if (is_dir) {
- snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
-@@ -6801,24 +7393,25 @@ static void mg_print_dir_entry(struct mg
- }
- strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime));
- mg_escape(file_name, path, sizeof(path));
-- mg_url_encode(file_name, strlen(file_name), href, sizeof(href));
-+ href = mg_url_encode(mg_mk_str(file_name));
- mg_printf_http_chunk(nc,
- "<tr><td><a href=\"%s%s\">%s%s</a></td>"
- "<td>%s</td><td name=%" INT64_FMT ">%s</td></tr>\n",
-- href, slash, path, slash, mod, is_dir ? -1 : fsize,
-+ href.p, slash, path, slash, mod, is_dir ? -1 : fsize,
- size);
-+ free((void *) href.p);
- }
-
- static void mg_scan_directory(struct mg_connection *nc, const char *dir,
- const struct mg_serve_http_opts *opts,
- void (*func)(struct mg_connection *, const char *,
- cs_stat_t *)) {
-- char path[MAX_PATH_SIZE];
-+ char path[MG_MAX_PATH];
- cs_stat_t st;
- struct dirent *dp;
- DIR *dirp;
-
-- DBG(("%p [%s]", nc, dir));
-+ LOG(LL_DEBUG, ("%p [%s]", nc, dir));
- if ((dirp = (opendir(dir))) != NULL) {
- while ((dp = readdir(dirp)) != NULL) {
- /* Do not show current dir and hidden files */
-@@ -6832,7 +7425,7 @@ static void mg_scan_directory(struct mg_
- }
- closedir(dirp);
- } else {
-- DBG(("%p opendir(%s) -> %d", nc, dir, mg_get_errno()));
-+ LOG(LL_DEBUG, ("%p opendir(%s) -> %d", nc, dir, mg_get_errno()));
- }
- }
-
-@@ -6929,7 +7522,7 @@ MG_INTERNAL void mg_find_index_file(cons
- MG_FREE(*index_file);
- *index_file = NULL;
- }
-- DBG(("[%s] [%s]", path, (*index_file ? *index_file : "")));
-+ LOG(LL_DEBUG, ("[%s] [%s]", path, (*index_file ? *index_file : "")));
- }
-
- #if MG_ENABLE_HTTP_URL_REWRITES
-@@ -6957,7 +7550,7 @@ static int mg_http_send_port_based_redir
- }
-
- static void mg_reverse_proxy_handler(struct mg_connection *nc, int ev,
-- void *ev_data) {
-+ void *ev_data MG_UD_ARG(void *user_data)) {
- struct http_message *hm = (struct http_message *) ev_data;
- struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
-
-@@ -6983,6 +7576,10 @@ static void mg_reverse_proxy_handler(str
- pd->reverse_proxy_data.linked_conn->flags |= MG_F_SEND_AND_CLOSE;
- break;
- }
-+
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ (void) user_data;
-+#endif
- }
-
- void mg_http_reverse_proxy(struct mg_connection *nc,
-@@ -6990,22 +7587,21 @@ void mg_http_reverse_proxy(struct mg_con
- struct mg_str upstream) {
- struct mg_connection *be;
- char burl[256], *purl = burl;
-- char *addr = NULL;
-- const char *path = NULL;
- int i;
- const char *error;
- struct mg_connect_opts opts;
-+ struct mg_str path = MG_NULL_STR, user_info = MG_NULL_STR, host = MG_NULL_STR;
- memset(&opts, 0, sizeof(opts));
- opts.error_string = &error;
-
- mg_asprintf(&purl, sizeof(burl), "%.*s%.*s", (int) upstream.len, upstream.p,
- (int) (hm->uri.len - mount.len), hm->uri.p + mount.len);
-
-- be = mg_connect_http_base(nc->mgr, mg_reverse_proxy_handler, opts, "http://",
-- "https://", purl, &path, NULL /* user */,
-- NULL /* pass */, &addr);
-- DBG(("Proxying %.*s to %s (rule: %.*s)", (int) hm->uri.len, hm->uri.p, purl,
-- (int) mount.len, mount.p));
-+ be = mg_connect_http_base(nc->mgr, MG_CB(mg_reverse_proxy_handler, NULL),
-+ opts, "http", NULL, "https", NULL, purl, &path,
-+ &user_info, &host);
-+ LOG(LL_DEBUG, ("Proxying %.*s to %s (rule: %.*s)", (int) hm->uri.len,
-+ hm->uri.p, purl, (int) mount.len, mount.p));
-
- if (be == NULL) {
- LOG(LL_ERROR, ("Error connecting to %s: %s", purl, error));
-@@ -7018,10 +7614,10 @@ void mg_http_reverse_proxy(struct mg_con
- mg_http_get_proto_data(nc)->reverse_proxy_data.linked_conn = be;
-
- /* send request upstream */
-- mg_printf(be, "%.*s %s HTTP/1.1\r\n", (int) hm->method.len, hm->method.p,
-- path);
-+ mg_printf(be, "%.*s %.*s HTTP/1.1\r\n", (int) hm->method.len, hm->method.p,
-+ (int) path.len, path.p);
-
-- mg_printf(be, "Host: %s\r\n", addr);
-+ mg_printf(be, "Host: %.*s\r\n", (int) host.len, host.p);
- for (i = 0; i < MG_MAX_HTTP_HEADERS && hm->header_names[i].len > 0; i++) {
- struct mg_str hn = hm->header_names[i];
- struct mg_str hv = hm->header_values[i];
-@@ -7071,7 +7667,7 @@ static int mg_http_handle_forwarding(str
-
- return 0;
- }
--#endif
-+#endif /* MG_ENABLE_FILESYSTEM */
-
- MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
- const struct mg_serve_http_opts *opts,
-@@ -7105,7 +7701,7 @@ MG_INTERNAL int mg_uri_to_local_path(str
- }
- } else {
- /* Regular rewrite, URI=directory */
-- int match_len = mg_match_prefix_n(a, hm->uri);
-+ size_t match_len = mg_match_prefix_n(a, hm->uri);
- if (match_len > 0) {
- file_uri_start = hm->uri.p + match_len;
- if (*file_uri_start == '/' || file_uri_start == cp_end) {
-@@ -7169,7 +7765,7 @@ MG_INTERNAL int mg_uri_to_local_path(str
- }
- }
- if (u >= cp_end) break;
-- parse_uri_component((const char **) &next, cp_end, '/', &component);
-+ parse_uri_component((const char **) &next, cp_end, "/", &component);
- if (component.len > 0) {
- int len;
- memmove(p + 1, component.p, component.len);
-@@ -7225,7 +7821,8 @@ MG_INTERNAL int mg_uri_to_local_path(str
- }
-
- out:
-- DBG(("'%.*s' -> '%s' + '%.*s'", (int) hm->uri.len, hm->uri.p,
-+ LOG(LL_DEBUG,
-+ ("'%.*s' -> '%s' + '%.*s'", (int) hm->uri.len, hm->uri.p,
- *local_path ? *local_path : "", (int) remainder->len, remainder->p));
- return ok;
- }
-@@ -7284,12 +7881,12 @@ MG_INTERNAL int mg_is_not_modified(struc
- }
- }
-
--static void mg_http_send_digest_auth_request(struct mg_connection *c,
-- const char *domain) {
-+void mg_http_send_digest_auth_request(struct mg_connection *c,
-+ const char *domain) {
- mg_printf(c,
- "HTTP/1.1 401 Unauthorized\r\n"
- "WWW-Authenticate: Digest qop=\"auth\", "
-- "realm=\"%s\", nonce=\"%lu\"\r\n"
-+ "realm=\"%s\", nonce=\"%lx\"\r\n"
- "Content-Length: 0\r\n\r\n",
- domain, (unsigned long) mg_time());
- }
-@@ -7331,7 +7928,8 @@ MG_INTERNAL void mg_send_http_file(struc
- (mg_match_prefix(opts->cgi_file_pattern, strlen(opts->cgi_file_pattern),
- index_file ? index_file : path) > 0);
-
-- DBG(("%p %.*s [%s] exists=%d is_dir=%d is_dav=%d is_cgi=%d index=%s", nc,
-+ LOG(LL_DEBUG,
-+ ("%p %.*s [%s] exists=%d is_dir=%d is_dav=%d is_cgi=%d index=%s", nc,
- (int) hm->method.len, hm->method.p, path, exists, is_directory, is_dav,
- is_cgi, index_file ? index_file : ""));
-
-@@ -7353,10 +7951,16 @@ MG_INTERNAL void mg_send_http_file(struc
-
- if (is_dav && opts->dav_document_root == NULL) {
- mg_http_send_error(nc, 501, NULL);
-- } else if (!mg_is_authorized(hm, path, is_directory, opts->auth_domain,
-- opts->global_auth_file, 1) ||
-- !mg_is_authorized(hm, path, is_directory, opts->auth_domain,
-- opts->per_directory_auth_file, 0)) {
-+ } else if (!mg_http_is_authorized(
-+ hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file,
-+ ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
-+ MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
-+ MG_AUTH_FLAG_ALLOW_MISSING_FILE)) ||
-+ !mg_http_is_authorized(
-+ hm, mg_mk_str(path), opts->auth_domain,
-+ opts->per_directory_auth_file,
-+ ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
-+ MG_AUTH_FLAG_ALLOW_MISSING_FILE))) {
- mg_http_send_digest_auth_request(nc, opts->auth_domain);
- } else if (is_cgi) {
- #if MG_ENABLE_HTTP_CGI
-@@ -7375,8 +7979,11 @@ MG_INTERNAL void mg_send_http_file(struc
- } else if (is_dav &&
- (opts->dav_auth_file == NULL ||
- (strcmp(opts->dav_auth_file, "-") != 0 &&
-- !mg_is_authorized(hm, path, is_directory, opts->auth_domain,
-- opts->dav_auth_file, 1)))) {
-+ !mg_http_is_authorized(
-+ hm, mg_mk_str(path), opts->auth_domain, opts->dav_auth_file,
-+ ((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
-+ MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
-+ MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) {
- mg_http_send_digest_auth_request(nc, opts->auth_domain);
- #endif
- } else if (!mg_vcmp(&hm->method, "MKCOL")) {
-@@ -7479,16 +8086,16 @@ void mg_serve_http(struct mg_connection
-
- #if MG_ENABLE_HTTP_STREAMING_MULTIPART
- void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
-- mg_fu_fname_fn local_name_fn) {
-+ mg_fu_fname_fn local_name_fn
-+ MG_UD_ARG(void *user_data)) {
- switch (ev) {
- case MG_EV_HTTP_PART_BEGIN: {
- struct mg_http_multipart_part *mp =
- (struct mg_http_multipart_part *) ev_data;
- struct file_upload_state *fus =
-- (struct file_upload_state *) calloc(1, sizeof(*fus));
-- mp->user_data = NULL;
--
-+ (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus));
- struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name));
-+ mp->user_data = NULL;
- if (lfn.p == NULL || lfn.len == 0) {
- LOG(LL_ERROR, ("%p Not allowed to upload %s", nc, mp->file_name));
- mg_printf(nc,
-@@ -7500,10 +8107,10 @@ void mg_file_upload_handler(struct mg_co
- nc->flags |= MG_F_SEND_AND_CLOSE;
- return;
- }
-- fus->lfn = (char *) malloc(lfn.len + 1);
-+ fus->lfn = (char *) MG_MALLOC(lfn.len + 1);
- memcpy(fus->lfn, lfn.p, lfn.len);
- fus->lfn[lfn.len] = '\0';
-- if (lfn.p != mp->file_name) free((char *) lfn.p);
-+ if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p);
- LOG(LL_DEBUG,
- ("%p Receiving file %s -> %s", nc, mp->file_name, fus->lfn));
- fus->fp = mg_fopen(fus->lfn, "w");
-@@ -7527,7 +8134,7 @@ void mg_file_upload_handler(struct mg_co
- struct file_upload_state *fus =
- (struct file_upload_state *) mp->user_data;
- if (fus == NULL || fus->fp == NULL) break;
-- if (fwrite(mp->data.p, 1, mp->data.len, fus->fp) != mp->data.len) {
-+ if (mg_fwrite(mp->data.p, 1, mp->data.len, fus->fp) != mp->data.len) {
- LOG(LL_ERROR, ("Failed to write to %s: %d, wrote %d", fus->lfn,
- mg_get_errno(), (int) fus->num_recd));
- if (mg_get_errno() == ENOSPC
-@@ -7585,115 +8192,60 @@ void mg_file_upload_handler(struct mg_co
- */
- }
- if (fus->fp != NULL) fclose(fus->fp);
-- free(fus->lfn);
-- free(fus);
-+ MG_FREE(fus->lfn);
-+ MG_FREE(fus);
- mp->user_data = NULL;
- nc->flags |= MG_F_SEND_AND_CLOSE;
- break;
- }
- }
-+
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ (void) user_data;
-+#endif
- }
-
- #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
- #endif /* MG_ENABLE_FILESYSTEM */
-
--/* returns 0 on success, -1 on error */
--MG_INTERNAL int mg_http_common_url_parse(const char *url, const char *schema,
-- const char *schema_tls, int *use_ssl,
-- char **user, char **pass, char **addr,
-- int *port_i, const char **path) {
-- int addr_len = 0;
-- int auth_sep_pos = -1;
-- int user_sep_pos = -1;
-- int port_pos = -1;
-- (void) user;
-- (void) pass;
--
-- if (strncmp(url, schema, strlen(schema)) == 0) {
-- url += strlen(schema);
-- } else if (strncmp(url, schema_tls, strlen(schema_tls)) == 0) {
-- url += strlen(schema_tls);
-- *use_ssl = 1;
--#if !MG_ENABLE_SSL
-- return -1; /* SSL is not enabled, cannot do HTTPS URLs */
--#endif
-- }
--
-- while (*url != '\0') {
-- *addr = (char *) MG_REALLOC(*addr, addr_len + 6 /* space for port too. */);
-- if (*addr == NULL) {
-- DBG(("OOM"));
-- return -1;
-- }
-- if (*url == '/') {
-- break;
-- }
-- if (*url == '@') {
-- auth_sep_pos = addr_len;
-- user_sep_pos = port_pos;
-- port_pos = -1;
-- }
-- if (*url == ':') port_pos = addr_len;
-- (*addr)[addr_len++] = *url;
-- (*addr)[addr_len] = '\0';
-- url++;
-- }
--
-- if (addr_len == 0) goto cleanup;
-- if (port_pos < 0) {
-- *port_i = addr_len;
-- addr_len += sprintf(*addr + addr_len, ":%d", *use_ssl ? 443 : 80);
-- } else {
-- *port_i = -1;
-- }
--
-- if (*path == NULL) *path = url;
--
-- if (**path == '\0') *path = "/";
--
-- if (user != NULL && pass != NULL) {
-- if (auth_sep_pos == -1) {
-- *user = NULL;
-- *pass = NULL;
-- } else {
-- /* user is from 0 to user_sep_pos */
-- *user = (char *) MG_MALLOC(user_sep_pos + 1);
-- memcpy(*user, *addr, user_sep_pos);
-- (*user)[user_sep_pos] = '\0';
-- /* pass is from user_sep_pos + 1 to auth_sep_pos */
-- *pass = (char *) MG_MALLOC(auth_sep_pos - user_sep_pos - 1 + 1);
-- memcpy(*pass, *addr + user_sep_pos + 1, auth_sep_pos - user_sep_pos - 1);
-- (*pass)[auth_sep_pos - user_sep_pos - 1] = '\0';
--
-- /* move address proper to the front */
-- memmove(*addr, *addr + auth_sep_pos + 1, addr_len - auth_sep_pos);
-- }
-- }
--
-- DBG(("%s %s", *addr, *path));
--
-- return 0;
--
--cleanup:
-- MG_FREE(*addr);
-- return -1;
--}
--
- struct mg_connection *mg_connect_http_base(
-- struct mg_mgr *mgr, mg_event_handler_t ev_handler,
-- struct mg_connect_opts opts, const char *schema, const char *schema_ssl,
-- const char *url, const char **path, char **user, char **pass, char **addr) {
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ struct mg_connect_opts opts, const char *scheme1, const char *scheme2,
-+ const char *scheme_ssl1, const char *scheme_ssl2, const char *url,
-+ struct mg_str *path, struct mg_str *user_info, struct mg_str *host) {
- struct mg_connection *nc = NULL;
-- int port_i = -1;
-+ unsigned int port_i = 0;
- int use_ssl = 0;
-+ struct mg_str scheme, query, fragment;
-+ char conn_addr_buf[2];
-+ char *conn_addr = conn_addr_buf;
-
-- if (mg_http_common_url_parse(url, schema, schema_ssl, &use_ssl, user, pass,
-- addr, &port_i, path) < 0) {
-+ if (mg_parse_uri(mg_mk_str(url), &scheme, user_info, host, &port_i, path,
-+ &query, &fragment) != 0) {
- MG_SET_PTRPTR(opts.error_string, "cannot parse url");
-- return NULL;
-+ goto out;
-+ }
-+
-+ /* If query is present, do not strip it. Pass to the caller. */
-+ if (query.len > 0) path->len += query.len + 1;
-+
-+ if (scheme.len == 0 || mg_vcmp(&scheme, scheme1) == 0 ||
-+ (scheme2 != NULL && mg_vcmp(&scheme, scheme2) == 0)) {
-+ use_ssl = 0;
-+ if (port_i == 0) port_i = 80;
-+ } else if (mg_vcmp(&scheme, scheme_ssl1) == 0 ||
-+ (scheme2 != NULL && mg_vcmp(&scheme, scheme_ssl2) == 0)) {
-+ use_ssl = 1;
-+ if (port_i == 0) port_i = 443;
-+ } else {
-+ goto out;
- }
-
-- LOG(LL_DEBUG, ("%s use_ssl? %d", url, use_ssl));
-+ mg_asprintf(&conn_addr, sizeof(conn_addr_buf), "tcp://%.*s:%u",
-+ (int) host->len, host->p, port_i);
-+ if (conn_addr == NULL) goto out;
-+
-+ LOG(LL_DEBUG, ("%s use_ssl? %d %s", url, use_ssl, conn_addr));
- if (use_ssl) {
- #if MG_ENABLE_SSL
- /*
-@@ -7706,68 +8258,62 @@ struct mg_connection *mg_connect_http_ba
- }
- #else
- MG_SET_PTRPTR(opts.error_string, "ssl is disabled");
-- if (user != NULL) MG_FREE(*user);
-- if (pass != NULL) MG_FREE(*pass);
-- MG_FREE(*addr);
-- return NULL;
-+ goto out;
- #endif
- }
-
-- if ((nc = mg_connect_opt(mgr, *addr, ev_handler, opts)) != NULL) {
-+ if ((nc = mg_connect_opt(mgr, conn_addr, MG_CB(ev_handler, user_data),
-+ opts)) != NULL) {
- mg_set_protocol_http_websocket(nc);
-- /* If the port was addred by us, restore the original host. */
-- if (port_i >= 0) (*addr)[port_i] = '\0';
- }
-
-+out:
-+ if (conn_addr != NULL && conn_addr != conn_addr_buf) MG_FREE(conn_addr);
- return nc;
- }
-
--struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr,
-- mg_event_handler_t ev_handler,
-- struct mg_connect_opts opts,
-- const char *url,
-- const char *extra_headers,
-- const char *post_data) {
-- char *user = NULL, *pass = NULL, *addr = NULL;
-- const char *path = NULL;
-+struct mg_connection *mg_connect_http_opt(
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ struct mg_connect_opts opts, const char *url, const char *extra_headers,
-+ const char *post_data) {
-+ struct mg_str user = MG_NULL_STR, null_str = MG_NULL_STR;
-+ struct mg_str host = MG_NULL_STR, path = MG_NULL_STR;
- struct mbuf auth;
- struct mg_connection *nc =
-- mg_connect_http_base(mgr, ev_handler, opts, "http://", "https://", url,
-- &path, &user, &pass, &addr);
-+ mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http",
-+ NULL, "https", NULL, url, &path, &user, &host);
-
- if (nc == NULL) {
- return NULL;
- }
-
- mbuf_init(&auth, 0);
-- if (user != NULL) {
-- mg_basic_auth_header(user, pass, &auth);
-+ if (user.len > 0) {
-+ mg_basic_auth_header(user, null_str, &auth);
- }
-
-- mg_printf(nc, "%s %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %" SIZE_T_FMT
-+ if (post_data == NULL) post_data = "";
-+ if (extra_headers == NULL) extra_headers = "";
-+ if (path.len == 0) path = mg_mk_str("/");
-+ if (host.len == 0) host = mg_mk_str("");
-+
-+ mg_printf(nc, "%s %.*s HTTP/1.1\r\nHost: %.*s\r\nContent-Length: %" SIZE_T_FMT
- "\r\n%.*s%s\r\n%s",
-- post_data == NULL ? "GET" : "POST", path, addr,
-- post_data == NULL ? 0 : strlen(post_data), (int) auth.len,
-- (auth.buf == NULL ? "" : auth.buf),
-- extra_headers == NULL ? "" : extra_headers,
-- post_data == NULL ? "" : post_data);
-+ (post_data[0] == '\0' ? "GET" : "POST"), (int) path.len, path.p,
-+ (int) (path.p - host.p), host.p, strlen(post_data), (int) auth.len,
-+ (auth.buf == NULL ? "" : auth.buf), extra_headers, post_data);
-
- mbuf_free(&auth);
-- MG_FREE(user);
-- MG_FREE(pass);
-- MG_FREE(addr);
- return nc;
- }
-
--struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
-- mg_event_handler_t ev_handler,
-- const char *url,
-- const char *extra_headers,
-- const char *post_data) {
-+struct mg_connection *mg_connect_http(
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ const char *url, const char *extra_headers, const char *post_data) {
- struct mg_connect_opts opts;
- memset(&opts, 0, sizeof(opts));
-- return mg_connect_http_opt(mgr, ev_handler, opts, url, extra_headers,
-- post_data);
-+ return mg_connect_http_opt(mgr, MG_CB(ev_handler, user_data), opts, url,
-+ extra_headers, post_data);
- }
-
- size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
-@@ -7776,9 +8322,11 @@ size_t mg_parse_multipart(const char *bu
- size_t *data_len) {
- static const char cd[] = "Content-Disposition: ";
- size_t hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
-+ int shl;
-
- if (buf == NULL || buf_len <= 0) return 0;
-- if ((hl = mg_http_get_request_len(buf, buf_len)) <= 0) return 0;
-+ if ((shl = mg_http_get_request_len(buf, buf_len)) <= 0) return 0;
-+ hl = shl;
- if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
-
- /* Get boundary length */
-@@ -7791,8 +8339,24 @@ size_t mg_parse_multipart(const char *bu
- struct mg_str header;
- header.p = buf + n + cdl;
- header.len = ll - (cdl + 2);
-- mg_http_parse_header(&header, "name", var_name, var_name_len);
-- mg_http_parse_header(&header, "filename", file_name, file_name_len);
-+ {
-+ char *var_name2 = var_name;
-+ mg_http_parse_header2(&header, "name", &var_name2, var_name_len);
-+ /* TODO: handle reallocated buffer correctly */
-+ if (var_name2 != var_name) {
-+ MG_FREE(var_name2);
-+ var_name[0] = '\0';
-+ }
-+ }
-+ {
-+ char *file_name2 = file_name;
-+ mg_http_parse_header2(&header, "filename", &file_name2, file_name_len);
-+ /* TODO: handle reallocated buffer correctly */
-+ if (file_name2 != file_name) {
-+ MG_FREE(file_name2);
-+ file_name[0] = '\0';
-+ }
-+ }
- }
- }
-
-@@ -7808,32 +8372,85 @@ size_t mg_parse_multipart(const char *bu
- return 0;
- }
-
--void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path,
-- mg_event_handler_t handler) {
-+void mg_register_http_endpoint_opt(struct mg_connection *nc,
-+ const char *uri_path,
-+ mg_event_handler_t handler,
-+ struct mg_http_endpoint_opts opts) {
- struct mg_http_proto_data *pd = NULL;
- struct mg_http_endpoint *new_ep = NULL;
-
- if (nc == NULL) return;
-- new_ep = (struct mg_http_endpoint *) calloc(1, sizeof(*new_ep));
-+ new_ep = (struct mg_http_endpoint *) MG_CALLOC(1, sizeof(*new_ep));
- if (new_ep == NULL) return;
-
- pd = mg_http_get_proto_data(nc);
-- new_ep->name = strdup(uri_path);
-- new_ep->name_len = strlen(new_ep->name);
-+ new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path));
-+ if (opts.auth_domain != NULL && opts.auth_file != NULL) {
-+ new_ep->auth_domain = strdup(opts.auth_domain);
-+ new_ep->auth_file = strdup(opts.auth_file);
-+ }
- new_ep->handler = handler;
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ new_ep->user_data = opts.user_data;
-+#endif
- new_ep->next = pd->endpoints;
- pd->endpoints = new_ep;
- }
-
-+static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
-+ struct http_message *hm) {
-+ struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
-+ void *user_data = nc->user_data;
-+
-+ if (ev == MG_EV_HTTP_REQUEST
-+#if MG_ENABLE_HTTP_STREAMING_MULTIPART
-+ || ev == MG_EV_HTTP_MULTIPART_REQUEST
-+#endif
-+ ) {
-+ struct mg_http_endpoint *ep =
-+ mg_http_get_endpoint_handler(nc->listener, &hm->uri);
-+ if (ep != NULL) {
-+#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
-+ if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
-+ MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) {
-+ mg_http_send_digest_auth_request(nc, ep->auth_domain);
-+ return;
-+ }
-+#endif
-+ pd->endpoint_handler = ep->handler;
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ user_data = ep->user_data;
-+#endif
-+ }
-+ }
-+ mg_call(nc, pd->endpoint_handler ? pd->endpoint_handler : nc->handler,
-+ user_data, ev, hm);
-+}
-+
-+void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path,
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data)) {
-+ struct mg_http_endpoint_opts opts;
-+ memset(&opts, 0, sizeof(opts));
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ opts.user_data = user_data;
-+#endif
-+ mg_register_http_endpoint_opt(nc, uri_path, handler, opts);
-+}
-+
- #endif /* MG_ENABLE_HTTP */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http_cgi.c"
-+#line 1 "mongoose/src/mg_http_cgi.c"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
- * All rights reserved
- */
-
-+#ifndef _WIN32
-+#include <signal.h>
-+#endif
-+
- #if MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI
-
- #ifndef MG_MAX_CGI_ENVIR_VARS
-@@ -7844,6 +8461,8 @@ void mg_register_http_endpoint(struct mg
- #define MG_ENV_EXPORT_TO_CGI "MONGOOSE_CGI"
- #endif
-
-+#define MG_F_HTTP_CGI_PARSE_HEADERS MG_F_USER_1
-+
- /*
- * This structure helps to create an environment for the spawned CGI program.
- * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
-@@ -7926,7 +8545,7 @@ static void mg_spawn_stdio_thread(sock_t
- }
-
- static void mg_abs_path(const char *utf8_path, char *abs_path, size_t len) {
-- wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE];
-+ wchar_t buf[MG_MAX_PATH], buf2[MG_MAX_PATH];
- to_wchar(utf8_path, buf, ARRAY_SIZE(buf));
- GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL);
- WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0);
-@@ -7938,9 +8557,9 @@ static int mg_start_process(const char *
- STARTUPINFOW si;
- PROCESS_INFORMATION pi;
- HANDLE a[2], b[2], me = GetCurrentProcess();
-- wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE];
-- char buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE],
-- buf4[MAX_PATH_SIZE], cmdline[MAX_PATH_SIZE];
-+ wchar_t wcmd[MG_MAX_PATH], full_dir[MG_MAX_PATH];
-+ char buf[MG_MAX_PATH], buf2[MG_MAX_PATH], buf5[MG_MAX_PATH],
-+ buf4[MG_MAX_PATH], cmdline[MG_MAX_PATH];
- DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
- FILE *fp;
-
-@@ -8044,7 +8663,7 @@ static int mg_start_process(const char *
- interp == NULL ? "" : interp, interp == NULL ? "" : " ", cmd,
- strerror(errno));
- send(1, buf, strlen(buf), 0);
-- exit(EXIT_FAILURE); /* exec call failed */
-+ _exit(EXIT_FAILURE); /* exec call failed */
- }
-
- return (pid != 0);
-@@ -8097,6 +8716,7 @@ static void mg_prepare_cgi_environment(s
- char *p;
- size_t i;
- char buf[100];
-+ size_t path_info_len = path_info != NULL ? path_info->len : 0;
-
- blk->len = blk->nvars = 0;
- blk->nc = nc;
-@@ -8128,7 +8748,7 @@ static void mg_prepare_cgi_environment(s
- mg_conn_addr_to_str(nc, buf, sizeof(buf), MG_SOCK_STRINGIFY_PORT);
- mg_addenv(blk, "SERVER_PORT=%s", buf);
-
-- s = hm->uri.p + hm->uri.len - path_info->len - 1;
-+ s = hm->uri.p + hm->uri.len - path_info_len - 1;
- if (*s == '/') {
- const char *base_name = strrchr(prog, DIRSEP);
- mg_addenv(blk, "SCRIPT_NAME=%.*s/%s", (int) (s - hm->uri.p), hm->uri.p,
-@@ -8201,11 +8821,18 @@ static void mg_prepare_cgi_environment(s
- }
-
- static void mg_cgi_ev_handler(struct mg_connection *cgi_nc, int ev,
-- void *ev_data) {
-- struct mg_connection *nc = (struct mg_connection *) cgi_nc->user_data;
-+ void *ev_data MG_UD_ARG(void *user_data)) {
-+#if !MG_ENABLE_CALLBACK_USERDATA
-+ void *user_data = cgi_nc->user_data;
-+#endif
-+ struct mg_connection *nc = (struct mg_connection *) user_data;
- (void) ev_data;
-
-- if (nc == NULL) return;
-+ if (nc == NULL) {
-+ /* The corresponding network connection was closed. */
-+ cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ return;
-+ }
-
- switch (ev) {
- case MG_EV_RECV:
-@@ -8222,7 +8849,7 @@ static void mg_cgi_ev_handler(struct mg_
- * been received, send appropriate reply line, and forward all
- * received headers to the client.
- */
-- if (nc->flags & MG_F_USER_1) {
-+ if (nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS) {
- struct mbuf *io = &cgi_nc->recv_mbuf;
- int len = mg_http_get_request_len(io->buf, io->len);
-
-@@ -8242,14 +8869,15 @@ static void mg_cgi_ev_handler(struct mg_
- mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\n");
- }
- }
-- nc->flags &= ~MG_F_USER_1;
-+ nc->flags &= ~MG_F_HTTP_CGI_PARSE_HEADERS;
- }
-- if (!(nc->flags & MG_F_USER_1)) {
-+ if (!(nc->flags & MG_F_HTTP_CGI_PARSE_HEADERS)) {
- mg_forward(cgi_nc, nc);
- }
- break;
- case MG_EV_CLOSE:
-- mg_http_free_proto_data_cgi(&mg_http_get_proto_data(cgi_nc)->cgi);
-+ DBG(("%p CLOSE", cgi_nc));
-+ mg_http_free_proto_data_cgi(&mg_http_get_proto_data(nc)->cgi);
- nc->flags |= MG_F_SEND_AND_CLOSE;
- break;
- }
-@@ -8260,7 +8888,7 @@ MG_INTERNAL void mg_handle_cgi(struct mg
- const struct http_message *hm,
- const struct mg_serve_http_opts *opts) {
- struct mg_cgi_env_block blk;
-- char dir[MAX_PATH_SIZE];
-+ char dir[MG_MAX_PATH];
- const char *p;
- sock_t fds[2];
-
-@@ -8278,24 +8906,31 @@ MG_INTERNAL void mg_handle_cgi(struct mg
- prog = p + 1;
- }
-
-- /*
-- * Try to create socketpair in a loop until success. mg_socketpair()
-- * can be interrupted by a signal and fail.
-- * TODO(lsm): use sigaction to restart interrupted syscall
-- */
-- do {
-- mg_socketpair(fds, SOCK_STREAM);
-- } while (fds[0] == INVALID_SOCKET);
-+ if (!mg_socketpair(fds, SOCK_STREAM)) {
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ return;
-+ }
-+
-+#ifndef _WIN32
-+ struct sigaction sa;
-+
-+ sigemptyset(&sa.sa_mask);
-+ sa.sa_handler = SIG_IGN;
-+ sa.sa_flags = 0;
-+ sigaction(SIGCHLD, &sa, NULL);
-+#endif
-
- if (mg_start_process(opts->cgi_interpreter, prog, blk.buf, blk.vars, dir,
- fds[1]) != 0) {
- size_t n = nc->recv_mbuf.len - (hm->message.len - hm->body.len);
- struct mg_connection *cgi_nc =
-- mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler);
-- struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(cgi_nc);
-+ mg_add_sock(nc->mgr, fds[0], mg_cgi_ev_handler MG_UD_ARG(nc));
-+ struct mg_http_proto_data *cgi_pd = mg_http_get_proto_data(nc);
- cgi_pd->cgi.cgi_nc = cgi_nc;
-+#if !MG_ENABLE_CALLBACK_USERDATA
- cgi_pd->cgi.cgi_nc->user_data = nc;
-- nc->flags |= MG_F_USER_1;
-+#endif
-+ nc->flags |= MG_F_HTTP_CGI_PARSE_HEADERS;
- /* Push POST data to the CGI */
- if (n > 0 && n < nc->recv_mbuf.len) {
- mg_send(cgi_pd->cgi.cgi_nc, hm->body.p, n);
-@@ -8312,15 +8947,17 @@ MG_INTERNAL void mg_handle_cgi(struct mg
- }
-
- MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) {
-- if (d != NULL) {
-- if (d->cgi_nc != NULL) d->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-- memset(d, 0, sizeof(struct mg_http_proto_data_cgi));
-+ if (d == NULL) return;
-+ if (d->cgi_nc != NULL) {
-+ d->cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ d->cgi_nc->user_data = NULL;
- }
-+ memset(d, 0, sizeof(*d));
- }
-
- #endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_CGI */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http_ssi.c"
-+#line 1 "mongoose/src/mg_http_ssi.c"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -8336,7 +8973,7 @@ static void mg_send_ssi_file(struct mg_c
- static void mg_send_file_data(struct mg_connection *nc, FILE *fp) {
- char buf[BUFSIZ];
- size_t n;
-- while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
-+ while ((n = mg_fread(buf, 1, sizeof(buf), fp)) > 0) {
- mg_send(nc, buf, n);
- }
- }
-@@ -8344,7 +8981,7 @@ static void mg_send_file_data(struct mg_
- static void mg_do_ssi_include(struct mg_connection *nc, struct http_message *hm,
- const char *ssi, char *tag, int include_level,
- const struct mg_serve_http_opts *opts) {
-- char file_name[BUFSIZ], path[MAX_PATH_SIZE], *p;
-+ char file_name[MG_MAX_PATH], path[MG_MAX_PATH], *p;
- FILE *fp;
-
- /*
-@@ -8447,9 +9084,9 @@ static void mg_send_ssi_file(struct mg_c
- cctx.req = hm;
- cctx.file = mg_mk_str(path);
- cctx.arg = mg_mk_str(p + d_call.len + 1);
-- mg_call(nc, NULL, MG_EV_SSI_CALL,
-+ mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL,
- (void *) cctx.arg.p); /* NUL added above */
-- mg_call(nc, NULL, MG_EV_SSI_CALL_CTX, &cctx);
-+ mg_call(nc, NULL, nc->user_data, MG_EV_SSI_CALL_CTX, &cctx);
- #if MG_ENABLE_HTTP_SSI_EXEC
- } else if (strncmp(p, d_exec.p, d_exec.len) == 0) {
- do_ssi_exec(nc, p + d_exec.len + 1);
-@@ -8516,7 +9153,7 @@ MG_INTERNAL void mg_handle_ssi_request(s
-
- #endif /* MG_ENABLE_HTTP_SSI && MG_ENABLE_HTTP && MG_ENABLE_FILESYSTEM */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http_webdav.c"
-+#line 1 "mongoose/src/mg_http_webdav.c"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -8560,10 +9197,10 @@ static int mg_mkdir(const char *path, ui
-
- static void mg_print_props(struct mg_connection *nc, const char *name,
- cs_stat_t *stp) {
-- char mtime[64], buf[MAX_PATH_SIZE * 3];
-+ char mtime[64];
- time_t t = stp->st_mtime; /* store in local variable for NDK compile */
-+ struct mg_str name_esc = mg_url_encode(mg_mk_str(name));
- mg_gmt_time_string(mtime, sizeof(mtime), &t);
-- mg_url_encode(name, strlen(name), buf, sizeof(buf));
- mg_printf(nc,
- "<d:response>"
- "<d:href>%s</d:href>"
-@@ -8577,8 +9214,9 @@ static void mg_print_props(struct mg_con
- "<d:status>HTTP/1.1 200 OK</d:status>"
- "</d:propstat>"
- "</d:response>\n",
-- buf, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
-+ name_esc.p, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
- (int64_t) stp->st_size, mtime);
-+ free((void *) name_esc.p);
- }
-
- MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path,
-@@ -8598,7 +9236,7 @@ MG_INTERNAL void mg_handle_propfind(stru
- strcmp(opts->enable_directory_listing, "yes") != 0) {
- mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
- } else {
-- char uri[MAX_PATH_SIZE];
-+ char uri[MG_MAX_PATH];
- mg_send(nc, header, sizeof(header) - 1);
- snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p);
- mg_print_props(nc, uri, stp);
-@@ -8665,7 +9303,7 @@ MG_INTERNAL void mg_handle_mkcol(struct
-
- static int mg_remove_directory(const struct mg_serve_http_opts *opts,
- const char *dir) {
-- char path[MAX_PATH_SIZE];
-+ char path[MG_MAX_PATH];
- struct dirent *dp;
- cs_stat_t st;
- DIR *dirp;
-@@ -8700,7 +9338,7 @@ MG_INTERNAL void mg_handle_move(struct m
- const char *p = (char *) memchr(dest->p, '/', dest->len);
- if (p != NULL && p[1] == '/' &&
- (p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) {
-- char buf[MAX_PATH_SIZE];
-+ char buf[MG_MAX_PATH];
- snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root,
- (int) (dest->p + dest->len - p), p);
- if (rename(path, buf) == 0) {
-@@ -8737,7 +9375,7 @@ static int mg_create_itermediate_directo
- /* Create intermediate directories if they do not exist */
- for (s = path + 1; *s != '\0'; s++) {
- if (*s == '/') {
-- char buf[MAX_PATH_SIZE];
-+ char buf[MG_MAX_PATH];
- cs_stat_t st;
- snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path);
- buf[sizeof(buf) - 1] = '\0';
-@@ -8787,7 +9425,7 @@ MG_INTERNAL void mg_handle_put(struct mg
-
- #endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http_websocket.c"
-+#line 1 "mongoose/src/mg_http_websocket.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -8796,109 +9434,205 @@ MG_INTERNAL void mg_handle_put(struct mg
-
- #if MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET
-
-+/* Amalgamated: #include "common/cs_sha1.h" */
-+
- #ifndef MG_WEBSOCKET_PING_INTERVAL_SECONDS
- #define MG_WEBSOCKET_PING_INTERVAL_SECONDS 5
- #endif
-
--#define MG_WS_NO_HOST_HEADER_MAGIC ((char *) 0x1)
-+#define FLAGS_MASK_FIN (1 << 7)
-+#define FLAGS_MASK_OP 0x0f
-
- static int mg_is_ws_fragment(unsigned char flags) {
-- return (flags & 0x80) == 0 || (flags & 0x0f) == 0;
-+ return (flags & FLAGS_MASK_FIN) == 0 ||
-+ (flags & FLAGS_MASK_OP) == WEBSOCKET_OP_CONTINUE;
- }
-
- static int mg_is_ws_first_fragment(unsigned char flags) {
-- return (flags & 0x80) == 0 && (flags & 0x0f) != 0;
-+ return (flags & FLAGS_MASK_FIN) == 0 &&
-+ (flags & FLAGS_MASK_OP) != WEBSOCKET_OP_CONTINUE;
-+}
-+
-+static int mg_is_ws_control_frame(unsigned char flags) {
-+ unsigned char op = (flags & FLAGS_MASK_OP);
-+ return op == WEBSOCKET_OP_CLOSE || op == WEBSOCKET_OP_PING ||
-+ op == WEBSOCKET_OP_PONG;
- }
-
- static void mg_handle_incoming_websocket_frame(struct mg_connection *nc,
- struct websocket_message *wsm) {
- if (wsm->flags & 0x8) {
-- mg_call(nc, nc->handler, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm);
-+ mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_CONTROL_FRAME, wsm);
- } else {
-- mg_call(nc, nc->handler, MG_EV_WEBSOCKET_FRAME, wsm);
-+ mg_call(nc, nc->handler, nc->user_data, MG_EV_WEBSOCKET_FRAME, wsm);
-+ }
-+}
-+
-+static struct mg_ws_proto_data *mg_ws_get_proto_data(struct mg_connection *nc) {
-+ struct mg_http_proto_data *htd = mg_http_get_proto_data(nc);
-+ return (htd != NULL ? &htd->ws_data : NULL);
-+}
-+
-+/*
-+ * Sends a Close websocket frame with the given data, and closes the underlying
-+ * connection. If `len` is ~0, strlen(data) is used.
-+ */
-+static void mg_ws_close(struct mg_connection *nc, const void *data,
-+ size_t len) {
-+ if ((int) len == ~0) {
-+ len = strlen((const char *) data);
- }
-+ mg_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, data, len);
-+ nc->flags |= MG_F_SEND_AND_CLOSE;
- }
-
- static int mg_deliver_websocket_data(struct mg_connection *nc) {
- /* Using unsigned char *, cause of integer arithmetic below */
-- uint64_t i, data_len = 0, frame_len = 0, buf_len = nc->recv_mbuf.len, len,
-- mask_len = 0, header_len = 0;
-- unsigned char *p = (unsigned char *) nc->recv_mbuf.buf, *buf = p,
-- *e = p + buf_len;
-- unsigned *sizep = (unsigned *) &p[1]; /* Size ptr for defragmented frames */
-- int ok, reass = buf_len > 0 && mg_is_ws_fragment(p[0]) &&
-- !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG);
--
-- /* If that's a continuation frame that must be reassembled, handle it */
-- if (reass && !mg_is_ws_first_fragment(p[0]) &&
-- buf_len >= 1 + sizeof(*sizep) && buf_len >= 1 + sizeof(*sizep) + *sizep) {
-- buf += 1 + sizeof(*sizep) + *sizep;
-- buf_len -= 1 + sizeof(*sizep) + *sizep;
-- }
--
-- if (buf_len >= 2) {
-- len = buf[1] & 127;
-- mask_len = buf[1] & 128 ? 4 : 0;
-- if (len < 126 && buf_len >= mask_len) {
-+ uint64_t i, data_len = 0, frame_len = 0, new_data_len = nc->recv_mbuf.len,
-+ len, mask_len = 0, header_len = 0;
-+ struct mg_ws_proto_data *wsd = mg_ws_get_proto_data(nc);
-+ unsigned char *new_data = (unsigned char *) nc->recv_mbuf.buf,
-+ *e = (unsigned char *) nc->recv_mbuf.buf + nc->recv_mbuf.len;
-+ uint8_t flags;
-+ int ok, reass;
-+
-+ if (wsd->reass_len > 0) {
-+ /*
-+ * We already have some previously received data which we need to
-+ * reassemble and deliver to the client code when we get the final
-+ * fragment.
-+ *
-+ * NOTE: it doesn't mean that the current message must be a continuation:
-+ * it might be a control frame (Close, Ping or Pong), which should be
-+ * handled without breaking the fragmented message.
-+ */
-+
-+ size_t existing_len = wsd->reass_len;
-+ assert(new_data_len >= existing_len);
-+
-+ new_data += existing_len;
-+ new_data_len -= existing_len;
-+ }
-+
-+ flags = new_data[0];
-+
-+ reass = new_data_len > 0 && mg_is_ws_fragment(flags) &&
-+ !(nc->flags & MG_F_WEBSOCKET_NO_DEFRAG);
-+
-+ if (reass && mg_is_ws_control_frame(flags)) {
-+ /*
-+ * Control frames can't be fragmented, so if we encounter fragmented
-+ * control frame, close connection immediately.
-+ */
-+ mg_ws_close(nc, "fragmented control frames are illegal", ~0);
-+ return 0;
-+ } else if (new_data_len > 0 && !reass && !mg_is_ws_control_frame(flags) &&
-+ wsd->reass_len > 0) {
-+ /*
-+ * When in the middle of a fragmented message, only the continuations
-+ * and control frames are allowed.
-+ */
-+ mg_ws_close(nc, "non-continuation in the middle of a fragmented message",
-+ ~0);
-+ return 0;
-+ }
-+
-+ if (new_data_len >= 2) {
-+ len = new_data[1] & 0x7f;
-+ mask_len = new_data[1] & FLAGS_MASK_FIN ? 4 : 0;
-+ if (len < 126 && new_data_len >= mask_len) {
- data_len = len;
- header_len = 2 + mask_len;
-- } else if (len == 126 && buf_len >= 4 + mask_len) {
-+ } else if (len == 126 && new_data_len >= 4 + mask_len) {
- header_len = 4 + mask_len;
-- data_len = ntohs(*(uint16_t *) &buf[2]);
-- } else if (buf_len >= 10 + mask_len) {
-+ data_len = ntohs(*(uint16_t *) &new_data[2]);
-+ } else if (new_data_len >= 10 + mask_len) {
- header_len = 10 + mask_len;
-- data_len = (((uint64_t) ntohl(*(uint32_t *) &buf[2])) << 32) +
-- ntohl(*(uint32_t *) &buf[6]);
-+ data_len = (((uint64_t) ntohl(*(uint32_t *) &new_data[2])) << 32) +
-+ ntohl(*(uint32_t *) &new_data[6]);
- }
- }
-
- frame_len = header_len + data_len;
-- ok = frame_len > 0 && frame_len <= buf_len;
-+ ok = (frame_len > 0 && frame_len <= new_data_len);
-+
-+ /* Check for overflow */
-+ if (frame_len < header_len || frame_len < data_len) {
-+ ok = 0;
-+ mg_ws_close(nc, "overflowed message", ~0);
-+ }
-
- if (ok) {
-+ size_t cleanup_len = 0;
- struct websocket_message wsm;
-
- wsm.size = (size_t) data_len;
-- wsm.data = buf + header_len;
-- wsm.flags = buf[0];
-+ wsm.data = new_data + header_len;
-+ wsm.flags = flags;
-
- /* Apply mask if necessary */
- if (mask_len > 0) {
- for (i = 0; i < data_len; i++) {
-- buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4];
-+ new_data[i + header_len] ^= (new_data + header_len - mask_len)[i % 4];
- }
- }
-
- if (reass) {
-- /* On first fragmented frame, nullify size */
-- if (mg_is_ws_first_fragment(wsm.flags)) {
-- mbuf_resize(&nc->recv_mbuf, nc->recv_mbuf.size + sizeof(*sizep));
-- p[0] &= ~0x0f; /* Next frames will be treated as continuation */
-- buf = p + 1 + sizeof(*sizep);
-- *sizep = 0; /* TODO(lsm): fix. this can stomp over frame data */
-+ /* This is a message fragment */
-+
-+ if (mg_is_ws_first_fragment(flags)) {
-+ /*
-+ * On the first fragmented frame, skip the first byte (op) and also
-+ * reset size to 1 (op), it'll be incremented with the data len below.
-+ */
-+ new_data += 1;
-+ wsd->reass_len = 1 /* op */;
- }
-
- /* Append this frame to the reassembled buffer */
-- memmove(buf, wsm.data, e - wsm.data);
-- (*sizep) += wsm.size;
-- nc->recv_mbuf.len -= wsm.data - buf;
--
-- /* On last fragmented frame - call user handler and remove data */
-- if (wsm.flags & 0x80) {
-- wsm.data = p + 1 + sizeof(*sizep);
-- wsm.size = *sizep;
-+ memmove(new_data, wsm.data, e - wsm.data);
-+ wsd->reass_len += wsm.size;
-+ nc->recv_mbuf.len -= wsm.data - new_data;
-+
-+ if (flags & FLAGS_MASK_FIN) {
-+ /* On last fragmented frame - call user handler and remove data */
-+ wsm.flags = FLAGS_MASK_FIN | nc->recv_mbuf.buf[0];
-+ wsm.data = (unsigned char *) nc->recv_mbuf.buf + 1 /* op */;
-+ wsm.size = wsd->reass_len - 1 /* op */;
-+ cleanup_len = wsd->reass_len;
-+ wsd->reass_len = 0;
-+
-+ /* Pass reassembled message to the client code. */
- mg_handle_incoming_websocket_frame(nc, &wsm);
-- mbuf_remove(&nc->recv_mbuf, 1 + sizeof(*sizep) + *sizep);
-+ mbuf_remove(&nc->recv_mbuf, cleanup_len); /* Cleanup frame */
- }
- } else {
-- /* TODO(lsm): properly handle OOB control frames during defragmentation */
-+ /*
-+ * This is a complete message, not a fragment. It might happen in between
-+ * of a fragmented message (in this case, WebSocket protocol requires
-+ * current message to be a control frame).
-+ */
-+ cleanup_len = (size_t) frame_len;
-+
-+ /* First of all, check if we need to react on a control frame. */
-+ switch (flags & FLAGS_MASK_OP) {
-+ case WEBSOCKET_OP_PING:
-+ mg_send_websocket_frame(nc, WEBSOCKET_OP_PONG, wsm.data, wsm.size);
-+ break;
-+
-+ case WEBSOCKET_OP_CLOSE:
-+ mg_ws_close(nc, wsm.data, wsm.size);
-+ break;
-+ }
-+
-+ /* Pass received message to the client code. */
- mg_handle_incoming_websocket_frame(nc, &wsm);
-- mbuf_remove(&nc->recv_mbuf, (size_t) frame_len); /* Cleanup frame */
-- }
-
-- /* If client closes, close too */
-- if ((buf[0] & 0x0f) == WEBSOCKET_OP_CLOSE) {
-- nc->flags |= MG_F_SEND_AND_CLOSE;
-+ /* Cleanup frame */
-+ memmove(nc->recv_mbuf.buf + wsd->reass_len,
-+ nc->recv_mbuf.buf + wsd->reass_len + cleanup_len,
-+ nc->recv_mbuf.len - wsd->reass_len - cleanup_len);
-+ nc->recv_mbuf.len -= cleanup_len;
- }
- }
-
-@@ -8943,7 +9677,8 @@ static void mg_send_ws_header(struct mg_
- int header_len;
- unsigned char header[10];
-
-- header[0] = (op & WEBSOCKET_DONT_FIN ? 0x0 : 0x80) + (op & 0x0f);
-+ header[0] =
-+ (op & WEBSOCKET_DONT_FIN ? 0x0 : FLAGS_MASK_FIN) | (op & FLAGS_MASK_OP);
- if (len < 126) {
- header[1] = (unsigned char) len;
- header_len = 2;
-@@ -9037,8 +9772,8 @@ void mg_printf_websocket_frame(struct mg
- }
-
- MG_INTERNAL void mg_ws_handler(struct mg_connection *nc, int ev,
-- void *ev_data) {
-- mg_call(nc, nc->handler, ev, ev_data);
-+ void *ev_data MG_UD_ARG(void *user_data)) {
-+ mg_call(nc, nc->handler, nc->user_data, ev, ev_data);
-
- switch (ev) {
- case MG_EV_RECV:
-@@ -9058,11 +9793,14 @@ MG_INTERNAL void mg_ws_handler(struct mg
- default:
- break;
- }
-+#if MG_ENABLE_CALLBACK_USERDATA
-+ (void) user_data;
-+#endif
- }
-
- #ifndef MG_EXT_SHA1
--static void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
-- const size_t *msg_lens, uint8_t *digest) {
-+void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
-+ const size_t *msg_lens, uint8_t *digest) {
- size_t i;
- cs_sha1_ctx sha_ctx;
- cs_sha1_init(&sha_ctx);
-@@ -9077,21 +9815,28 @@ extern void mg_hash_sha1_v(size_t num_ms
- #endif
-
- MG_INTERNAL void mg_ws_handshake(struct mg_connection *nc,
-- const struct mg_str *key) {
-+ const struct mg_str *key,
-+ struct http_message *hm) {
- static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- const uint8_t *msgs[2] = {(const uint8_t *) key->p, (const uint8_t *) magic};
- const size_t msg_lens[2] = {key->len, 36};
- unsigned char sha[20];
- char b64_sha[30];
-+ struct mg_str *s;
-
- mg_hash_sha1_v(2, msgs, msg_lens, sha);
- mg_base64_encode(sha, sizeof(sha), b64_sha);
-- mg_printf(nc, "%s%s%s",
-+ mg_printf(nc, "%s",
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
-- "Connection: Upgrade\r\n"
-- "Sec-WebSocket-Accept: ",
-- b64_sha, "\r\n\r\n");
-+ "Connection: Upgrade\r\n");
-+
-+ s = mg_get_http_header(hm, "Sec-WebSocket-Protocol");
-+ if (s != NULL) {
-+ mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) s->len, s->p);
-+ }
-+ mg_printf(nc, "Sec-WebSocket-Accept: %s%s", b64_sha, "\r\n\r\n");
-+
- DBG(("%p %.*s %s", nc, (int) key->len, key->p, b64_sha));
- }
-
-@@ -9106,6 +9851,18 @@ void mg_send_websocket_handshake3(struct
- const char *host, const char *protocol,
- const char *extra_headers, const char *user,
- const char *pass) {
-+ mg_send_websocket_handshake3v(nc, mg_mk_str(path), mg_mk_str(host),
-+ mg_mk_str(protocol), mg_mk_str(extra_headers),
-+ mg_mk_str(user), mg_mk_str(pass));
-+}
-+
-+void mg_send_websocket_handshake3v(struct mg_connection *nc,
-+ const struct mg_str path,
-+ const struct mg_str host,
-+ const struct mg_str protocol,
-+ const struct mg_str extra_headers,
-+ const struct mg_str user,
-+ const struct mg_str pass) {
- struct mbuf auth;
- char key[25];
- uint32_t nonce[4];
-@@ -9116,7 +9873,7 @@ void mg_send_websocket_handshake3(struct
- mg_base64_encode((unsigned char *) &nonce, sizeof(nonce), key);
-
- mbuf_init(&auth, 0);
-- if (user != NULL) {
-+ if (user.len > 0) {
- mg_basic_auth_header(user, pass, &auth);
- }
-
-@@ -9127,23 +9884,26 @@ void mg_send_websocket_handshake3(struct
- * because it handles NULL specially (and incorrectly).
- */
- mg_printf(nc,
-- "GET %s HTTP/1.1\r\n"
-+ "GET %.*s HTTP/1.1\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "%.*s"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: %s\r\n",
-- path, (int) auth.len, (auth.buf == NULL ? "" : auth.buf), key);
-+ (int) path.len, path.p, (int) auth.len,
-+ (auth.buf == NULL ? "" : auth.buf), key);
-
- /* TODO(mkm): take default hostname from http proto data if host == NULL */
-- if (host != MG_WS_NO_HOST_HEADER_MAGIC) {
-- mg_printf(nc, "Host: %s\r\n", host);
-- }
-- if (protocol != NULL) {
-- mg_printf(nc, "Sec-WebSocket-Protocol: %s\r\n", protocol);
-+ if (host.len > 0) {
-+ int host_len = (int) (path.p - host.p); /* Account for possible :PORT */
-+ mg_printf(nc, "Host: %.*s\r\n", host_len, host.p);
-+ }
-+ if (protocol.len > 0) {
-+ mg_printf(nc, "Sec-WebSocket-Protocol: %.*s\r\n", (int) protocol.len,
-+ protocol.p);
- }
-- if (extra_headers != NULL) {
-- mg_printf(nc, "%s", extra_headers);
-+ if (extra_headers.len > 0) {
-+ mg_printf(nc, "%.*s", (int) extra_headers.len, extra_headers.p);
- }
- mg_printf(nc, "\r\n");
-
-@@ -9152,52 +9912,49 @@ void mg_send_websocket_handshake3(struct
-
- void mg_send_websocket_handshake(struct mg_connection *nc, const char *path,
- const char *extra_headers) {
-- mg_send_websocket_handshake2(nc, path, MG_WS_NO_HOST_HEADER_MAGIC, NULL,
-- extra_headers);
-+ struct mg_str null_str = MG_NULL_STR;
-+ mg_send_websocket_handshake3v(
-+ nc, mg_mk_str(path), null_str /* host */, null_str /* protocol */,
-+ mg_mk_str(extra_headers), null_str /* user */, null_str /* pass */);
- }
-
--struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr,
-- mg_event_handler_t ev_handler,
-- struct mg_connect_opts opts,
-- const char *url, const char *protocol,
-- const char *extra_headers) {
-- char *user = NULL, *pass = NULL, *addr = NULL;
-- const char *path = NULL;
-+struct mg_connection *mg_connect_ws_opt(
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ struct mg_connect_opts opts, const char *url, const char *protocol,
-+ const char *extra_headers) {
-+ struct mg_str null_str = MG_NULL_STR;
-+ struct mg_str host = MG_NULL_STR, path = MG_NULL_STR, user_info = MG_NULL_STR;
- struct mg_connection *nc =
-- mg_connect_http_base(mgr, ev_handler, opts, "ws://", "wss://", url, &path,
-- &user, &pass, &addr);
--
-+ mg_connect_http_base(mgr, MG_CB(ev_handler, user_data), opts, "http",
-+ "ws", "https", "wss", url, &path, &user_info, &host);
- if (nc != NULL) {
-- mg_send_websocket_handshake3(nc, path, addr, protocol, extra_headers, user,
-- pass);
-+ mg_send_websocket_handshake3v(nc, path, host, mg_mk_str(protocol),
-+ mg_mk_str(extra_headers), user_info,
-+ null_str);
- }
--
-- MG_FREE(addr);
-- MG_FREE(user);
-- MG_FREE(pass);
- return nc;
- }
-
--struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
-- mg_event_handler_t ev_handler,
-- const char *url, const char *protocol,
-- const char *extra_headers) {
-+struct mg_connection *mg_connect_ws(
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ const char *url, const char *protocol, const char *extra_headers) {
- struct mg_connect_opts opts;
- memset(&opts, 0, sizeof(opts));
-- return mg_connect_ws_opt(mgr, ev_handler, opts, url, protocol, extra_headers);
-+ return mg_connect_ws_opt(mgr, MG_CB(ev_handler, user_data), opts, url,
-+ protocol, extra_headers);
- }
- #endif /* MG_ENABLE_HTTP && MG_ENABLE_HTTP_WEBSOCKET */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/util.c"
-+#line 1 "mongoose/src/mg_util.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
-
--/* Amalgamated: #include "common/base64.h" */
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
-+/* Amalgamated: #include "common/cs_base64.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_util.h" */
-
- /* For platforms with limited libc */
- #ifndef MAX
-@@ -9213,14 +9970,10 @@ const char *mg_skip(const char *s, const
- return s;
- }
-
--static int lowercase(const char *s) {
-- return tolower(*(const unsigned char *) s);
--}
--
--#if MG_ENABLE_FILESYSTEM
-+#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS)
- int mg_stat(const char *path, cs_stat_t *st) {
- #ifdef _WIN32
-- wchar_t wpath[MAX_PATH_SIZE];
-+ wchar_t wpath[MG_MAX_PATH];
- to_wchar(path, wpath, ARRAY_SIZE(wpath));
- DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st)));
- return _wstati64(wpath, st);
-@@ -9231,7 +9984,7 @@ int mg_stat(const char *path, cs_stat_t
-
- FILE *mg_fopen(const char *path, const char *mode) {
- #ifdef _WIN32
-- wchar_t wpath[MAX_PATH_SIZE], wmode[10];
-+ wchar_t wpath[MG_MAX_PATH], wmode[10];
- to_wchar(path, wpath, ARRAY_SIZE(wpath));
- to_wchar(mode, wmode, ARRAY_SIZE(wmode));
- return _wfopen(wpath, wmode);
-@@ -9242,13 +9995,21 @@ FILE *mg_fopen(const char *path, const c
-
- int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */
- #if defined(_WIN32) && !defined(WINCE)
-- wchar_t wpath[MAX_PATH_SIZE];
-+ wchar_t wpath[MG_MAX_PATH];
- to_wchar(path, wpath, ARRAY_SIZE(wpath));
- return _wopen(wpath, flag, mode);
- #else
- return open(path, flag, mode); /* LCOV_EXCL_LINE */
- #endif
- }
-+
-+size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f) {
-+ return fread(ptr, size, count, f);
-+}
-+
-+size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) {
-+ return fwrite(ptr, size, count, f);
-+}
- #endif
-
- void mg_base64_encode(const unsigned char *src, int src_len, char *dst) {
-@@ -9295,11 +10056,11 @@ void mg_set_close_on_exec(sock_t sock) {
- #endif
- }
-
--void mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
-- int flags) {
-+int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
-+ int flags) {
- int is_v6;
-- if (buf == NULL || len <= 0) return;
-- buf[0] = '\0';
-+ if (buf == NULL || len <= 0) return 0;
-+ memset(buf, 0, len);
- #if MG_ENABLE_IPV6
- is_v6 = sa->sa.sa_family == AF_INET6;
- #else
-@@ -9321,36 +10082,51 @@ void mg_sock_addr_to_str(const union soc
- }
- }
- if (inet_ntop(sa->sa.sa_family, addr, start, capacity) == NULL) {
-- *buf = '\0';
-+ goto cleanup;
- }
- #elif defined(_WIN32) || MG_LWIP || (MG_NET_IF == MG_NET_IF_PIC32)
- /* Only Windoze Vista (and newer) have inet_ntop() */
-- strncpy(buf, inet_ntoa(sa->sin.sin_addr), len);
-+ char *addr_str = inet_ntoa(sa->sin.sin_addr);
-+ if (addr_str != NULL) {
-+ strncpy(buf, inet_ntoa(sa->sin.sin_addr), len - 1);
-+ } else {
-+ goto cleanup;
-+ }
- #else
-- inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len);
-+ if (inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len - 1) == NULL) {
-+ goto cleanup;
-+ }
- #endif
- }
- if (flags & MG_SOCK_STRINGIFY_PORT) {
- int port = ntohs(sa->sin.sin_port);
- if (flags & MG_SOCK_STRINGIFY_IP) {
-- snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s:%d",
-- (is_v6 ? "]" : ""), port);
-+ int buf_len = strlen(buf);
-+ snprintf(buf + buf_len, len - (buf_len + 1), "%s:%d", (is_v6 ? "]" : ""),
-+ port);
- } else {
- snprintf(buf, len, "%d", port);
- }
- }
-+
-+ return strlen(buf);
-+
-+cleanup:
-+ *buf = '\0';
-+ return 0;
- }
-
--void mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len,
-- int flags) {
-+int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len,
-+ int flags) {
- union socket_address sa;
- memset(&sa, 0, sizeof(sa));
- mg_if_get_conn_addr(nc, flags & MG_SOCK_STRINGIFY_REMOTE, &sa);
-- mg_sock_addr_to_str(&sa, buf, len, flags);
-+ return mg_sock_addr_to_str(&sa, buf, len, flags);
- }
-
- #if MG_ENABLE_HEXDUMP
--int mg_hexdump(const void *buf, int len, char *dst, int dst_len) {
-+static int mg_hexdump_n(const void *buf, int len, char *dst, int dst_len,
-+ int offset) {
- const unsigned char *p = (const unsigned char *) buf;
- char ascii[17] = "";
- int i, idx, n = 0;
-@@ -9359,7 +10135,7 @@ int mg_hexdump(const void *buf, int len,
- idx = i % 16;
- if (idx == 0) {
- if (i > 0) n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii);
-- n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i);
-+ n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i + offset);
- }
- if (dst_len - n < 0) {
- return n;
-@@ -9370,16 +10146,50 @@ int mg_hexdump(const void *buf, int len,
- }
-
- while (i++ % 16) n += snprintf(dst + n, MAX(dst_len - n, 0), "%s", " ");
-- n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n\n", ascii);
-+ n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii);
-
- return n;
- }
-
-+int mg_hexdump(const void *buf, int len, char *dst, int dst_len) {
-+ return mg_hexdump_n(buf, len, dst, dst_len, 0);
-+}
-+
-+void mg_hexdumpf(FILE *fp, const void *buf, int len) {
-+ char tmp[80];
-+ int offset = 0, n;
-+ while (len > 0) {
-+ n = (len < 16 ? len : 16);
-+ mg_hexdump_n(((const char *) buf) + offset, n, tmp, sizeof(tmp), offset);
-+ fputs(tmp, fp);
-+ offset += n;
-+ len -= n;
-+ }
-+}
-+
- void mg_hexdump_connection(struct mg_connection *nc, const char *path,
- const void *buf, int num_bytes, int ev) {
- FILE *fp = NULL;
-- char *hexbuf, src[60], dst[60];
-- int buf_size = num_bytes * 5 + 100;
-+ char src[60], dst[60];
-+ const char *tag = NULL;
-+ switch (ev) {
-+ case MG_EV_RECV:
-+ tag = "<-";
-+ break;
-+ case MG_EV_SEND:
-+ tag = "->";
-+ break;
-+ case MG_EV_ACCEPT:
-+ tag = "<A";
-+ break;
-+ case MG_EV_CONNECT:
-+ tag = "C>";
-+ break;
-+ case MG_EV_CLOSE:
-+ tag = "XX";
-+ break;
-+ }
-+ if (tag == NULL) return; /* Don't log MG_EV_TIMER, etc */
-
- if (strcmp(path, "-") == 0) {
- fp = stdout;
-@@ -9397,20 +10207,12 @@ void mg_hexdump_connection(struct mg_con
- mg_conn_addr_to_str(nc, dst, sizeof(dst), MG_SOCK_STRINGIFY_IP |
- MG_SOCK_STRINGIFY_PORT |
- MG_SOCK_STRINGIFY_REMOTE);
-- fprintf(
-- fp, "%lu %p %s %s %s %d\n", (unsigned long) mg_time(), (void *) nc, src,
-- ev == MG_EV_RECV ? "<-" : ev == MG_EV_SEND
-- ? "->"
-- : ev == MG_EV_ACCEPT
-- ? "<A"
-- : ev == MG_EV_CONNECT ? "C>" : "XX",
-- dst, num_bytes);
-- if (num_bytes > 0 && (hexbuf = (char *) MG_MALLOC(buf_size)) != NULL) {
-- mg_hexdump(buf, num_bytes, hexbuf, buf_size);
-- fprintf(fp, "%s", hexbuf);
-- MG_FREE(hexbuf);
-+ fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) mg_time(), (void *) nc,
-+ src, tag, dst, (int) num_bytes);
-+ if (num_bytes > 0) {
-+ mg_hexdumpf(fp, buf, num_bytes);
- }
-- if (fp != stdin && fp != stdout) fclose(fp);
-+ if (fp != stdout && fp != stderr) fclose(fp);
- }
- #endif
-
-@@ -9420,90 +10222,6 @@ int mg_is_big_endian(void) {
- return ((char *) &n)[0] == 0;
- }
-
--const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
-- struct mg_str *eq_val) {
-- if (list == NULL || *list == '\0') {
-- /* End of the list */
-- list = NULL;
-- } else {
-- val->p = list;
-- if ((list = strchr(val->p, ',')) != NULL) {
-- /* Comma found. Store length and shift the list ptr */
-- val->len = list - val->p;
-- list++;
-- } else {
-- /* This value is the last one */
-- list = val->p + strlen(val->p);
-- val->len = list - val->p;
-- }
--
-- 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->p = (const char *) memchr(val->p, '=', val->len);
-- if (eq_val->p != NULL) {
-- eq_val->p++; /* Skip over '=' character */
-- eq_val->len = val->p + val->len - eq_val->p;
-- val->len = (eq_val->p - val->p) - 1;
-- }
-- }
-- }
--
-- return list;
--}
--
--int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str) {
-- const char *or_str;
-- size_t len, i = 0, j = 0;
-- int res;
--
-- if ((or_str = (const char *) memchr(pattern.p, '|', pattern.len)) != NULL) {
-- struct mg_str pstr = {pattern.p, (size_t)(or_str - pattern.p)};
-- res = mg_match_prefix_n(pstr, str);
-- if (res > 0) return res;
-- pstr.p = or_str + 1;
-- pstr.len = (pattern.p + pattern.len) - (or_str + 1);
-- return mg_match_prefix_n(pstr, str);
-- }
--
-- for (; i < pattern.len; i++, j++) {
-- if (pattern.p[i] == '?' && j != str.len) {
-- continue;
-- } else if (pattern.p[i] == '$') {
-- return j == str.len ? (int) j : -1;
-- } else if (pattern.p[i] == '*') {
-- i++;
-- if (pattern.p[i] == '*') {
-- i++;
-- len = str.len - j;
-- } else {
-- len = 0;
-- while (j + len != str.len && str.p[j + len] != '/') {
-- len++;
-- }
-- }
-- if (i == pattern.len) {
-- return j + len;
-- }
-- do {
-- const struct mg_str pstr = {pattern.p + i, pattern.len - i};
-- const struct mg_str sstr = {str.p + j + len, str.len - j - len};
-- res = mg_match_prefix_n(pstr, sstr);
-- } while (res == -1 && len-- > 0);
-- return res == -1 ? -1 : (int) (j + res + len);
-- } else if (lowercase(&pattern.p[i]) != lowercase(&str.p[j])) {
-- return -1;
-- }
-- }
-- return j;
--}
--
--int mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
-- const struct mg_str pstr = {pattern, (size_t) pattern_len};
-- return mg_match_prefix_n(pstr, mg_mk_str(str));
--}
--
- DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) {
- #ifndef WINCE
- return errno;
-@@ -9525,7 +10243,7 @@ void mg_mbuf_append_base64(struct mbuf *
- cs_base64_finish(&ctx);
- }
-
--void mg_basic_auth_header(const char *user, const char *pass,
-+void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass,
- struct mbuf *buf) {
- const char *header_prefix = "Authorization: Basic ";
- const char *header_suffix = "\r\n";
-@@ -9535,16 +10253,38 @@ void mg_basic_auth_header(const char *us
-
- mbuf_append(buf, header_prefix, strlen(header_prefix));
-
-- cs_base64_update(&ctx, user, strlen(user));
-- if (pass != NULL) {
-+ cs_base64_update(&ctx, user.p, user.len);
-+ if (pass.len > 0) {
- cs_base64_update(&ctx, ":", 1);
-- cs_base64_update(&ctx, pass, strlen(pass));
-+ cs_base64_update(&ctx, pass.p, pass.len);
- }
- cs_base64_finish(&ctx);
- mbuf_append(buf, header_suffix, strlen(header_suffix));
- }
-+
-+struct mg_str mg_url_encode(const struct mg_str src) {
-+ static const char *dont_escape = "._-$,;~()/";
-+ static const char *hex = "0123456789abcdef";
-+ size_t i = 0;
-+ struct mbuf mb;
-+ mbuf_init(&mb, src.len);
-+
-+ for (i = 0; i < src.len; i++) {
-+ const unsigned char c = *((const unsigned char *) src.p + i);
-+ if (isalnum(c) || strchr(dont_escape, c) != NULL) {
-+ mbuf_append(&mb, &c, 1);
-+ } else {
-+ mbuf_append(&mb, "%", 1);
-+ mbuf_append(&mb, &hex[c >> 4], 1);
-+ mbuf_append(&mb, &hex[c & 15], 1);
-+ }
-+ }
-+ mbuf_append(&mb, "", 1);
-+ mbuf_trim(&mb);
-+ return mg_mk_str_n(mb.buf, mb.len - 1);
-+}
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/mqtt.c"
-+#line 1 "mongoose/src/mg_mqtt.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -9555,8 +10295,8 @@ void mg_basic_auth_header(const char *us
-
- #include <string.h>
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/mqtt.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_mqtt.h" */
-
- static uint16_t getu16(const char *p) {
- const uint8_t *up = (const uint8_t *) p;
-@@ -9571,22 +10311,29 @@ static const char *scanto(const char *p,
-
- MG_INTERNAL int parse_mqtt(struct mbuf *io, struct mg_mqtt_message *mm) {
- uint8_t header;
-- size_t len = 0;
-+ size_t len = 0, len_len = 0;
-+ const char *p, *end;
-+ unsigned char lc = 0;
- int cmd;
-- const char *p = &io->buf[1], *end;
-
-- if (io->len < 2) return -1;
-+ if (io->len < 2) return MG_MQTT_ERROR_INCOMPLETE_MSG;
- header = io->buf[0];
- cmd = header >> 4;
-
- /* decode mqtt variable length */
-- do {
-- len += (*p & 127) << 7 * (p - &io->buf[1]);
-- } while ((*p++ & 128) != 0 && ((size_t)(p - io->buf) <= io->len));
-+ len = len_len = 0;
-+ p = io->buf + 1;
-+ while ((size_t)(p - io->buf) < io->len) {
-+ lc = *((const unsigned char *) p++);
-+ len += (lc & 0x7f) << 7 * len_len;
-+ len_len++;
-+ if (!(lc & 0x80)) break;
-+ if (len_len > 4) return MG_MQTT_ERROR_MALFORMED_MSG;
-+ }
-
- end = p + len;
-- if (end > io->buf + io->len + 1) {
-- return -1;
-+ if (lc & 0x80 || len > (io->len - (p - io->buf))) {
-+ return MG_MQTT_ERROR_INCOMPLETE_MSG;
- }
-
- mm->cmd = cmd;
-@@ -9595,24 +10342,36 @@ MG_INTERNAL int parse_mqtt(struct mbuf *
- switch (cmd) {
- case MG_MQTT_CMD_CONNECT: {
- p = scanto(p, &mm->protocol_name);
-+ if (p > end - 4) return MG_MQTT_ERROR_MALFORMED_MSG;
- mm->protocol_version = *(uint8_t *) p++;
- mm->connect_flags = *(uint8_t *) p++;
- mm->keep_alive_timer = getu16(p);
- p += 2;
-- if (p < end) p = scanto(p, &mm->client_id);
-- if (p < end && (mm->connect_flags & MG_MQTT_HAS_WILL))
-+ if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
-+ p = scanto(p, &mm->client_id);
-+ if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG;
-+ if (mm->connect_flags & MG_MQTT_HAS_WILL) {
-+ if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
- p = scanto(p, &mm->will_topic);
-- if (p < end && (mm->connect_flags & MG_MQTT_HAS_WILL))
-+ }
-+ if (mm->connect_flags & MG_MQTT_HAS_WILL) {
-+ if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
- p = scanto(p, &mm->will_message);
-- if (p < end && (mm->connect_flags & MG_MQTT_HAS_USER_NAME))
-+ }
-+ if (mm->connect_flags & MG_MQTT_HAS_USER_NAME) {
-+ if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
- p = scanto(p, &mm->user_name);
-- if (p < end && (mm->connect_flags & MG_MQTT_HAS_PASSWORD))
-+ }
-+ if (mm->connect_flags & MG_MQTT_HAS_PASSWORD) {
-+ if (p >= end) return MG_MQTT_ERROR_MALFORMED_MSG;
- p = scanto(p, &mm->password);
-+ }
-+ if (p != end) return MG_MQTT_ERROR_MALFORMED_MSG;
-
- LOG(LL_DEBUG,
- ("%d %2x %d proto [%.*s] client_id [%.*s] will_topic [%.*s] "
- "will_msg [%.*s] user_name [%.*s] password [%.*s]",
-- len, (int) mm->connect_flags, (int) mm->keep_alive_timer,
-+ (int) len, (int) mm->connect_flags, (int) mm->keep_alive_timer,
- (int) mm->protocol_name.len, mm->protocol_name.p,
- (int) mm->client_id.len, mm->client_id.p, (int) mm->will_topic.len,
- mm->will_topic.p, (int) mm->will_message.len, mm->will_message.p,
-@@ -9621,6 +10380,7 @@ MG_INTERNAL int parse_mqtt(struct mbuf *
- break;
- }
- case MG_MQTT_CMD_CONNACK:
-+ if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
- mm->connack_ret_code = p[1];
- break;
- case MG_MQTT_CMD_PUBACK:
-@@ -9631,17 +10391,19 @@ MG_INTERNAL int parse_mqtt(struct mbuf *
- mm->message_id = getu16(p);
- break;
- case MG_MQTT_CMD_PUBLISH: {
-- if (MG_MQTT_GET_QOS(header) > 0) {
-+ p = scanto(p, &mm->topic);
-+ if (p > end) return MG_MQTT_ERROR_MALFORMED_MSG;
-+ if (mm->qos > 0) {
-+ if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
- mm->message_id = getu16(p);
- p += 2;
- }
-- p = scanto(p, &mm->topic);
--
- mm->payload.p = p;
- mm->payload.len = end - p;
- break;
- }
- case MG_MQTT_CMD_SUBSCRIBE:
-+ if (end - p < 2) return MG_MQTT_ERROR_MALFORMED_MSG;
- mm->message_id = getu16(p);
- p += 2;
- /*
-@@ -9656,24 +10418,65 @@ MG_INTERNAL int parse_mqtt(struct mbuf *
- break;
- }
-
-- return end - io->buf;
-+ mm->len = end - io->buf;
-+ return mm->len;
- }
-
--static void mqtt_handler(struct mg_connection *nc, int ev, void *ev_data) {
-- int len;
-+static void mqtt_handler(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data)) {
- struct mbuf *io = &nc->recv_mbuf;
- struct mg_mqtt_message mm;
- memset(&mm, 0, sizeof(mm));
-
-- nc->handler(nc, ev, ev_data);
-+ nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
-
- switch (ev) {
-- case MG_EV_RECV:
-- len = parse_mqtt(io, &mm);
-- if (len == -1) break; /* not fully buffered */
-- nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm);
-- mbuf_remove(io, len);
-+ case MG_EV_ACCEPT:
-+ if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc);
-+ break;
-+ case MG_EV_RECV: {
-+ /* There can be multiple messages in the buffer, process them all. */
-+ while (1) {
-+ int len = parse_mqtt(io, &mm);
-+ if (len < 0) {
-+ if (len == MG_MQTT_ERROR_MALFORMED_MSG) {
-+ /* Protocol error. */
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ } else if (len == MG_MQTT_ERROR_INCOMPLETE_MSG) {
-+ /* Not fully buffered, let's check if we have a chance to get more
-+ * data later */
-+ if (nc->recv_mbuf_limit > 0 &&
-+ nc->recv_mbuf.len >= nc->recv_mbuf_limit) {
-+ LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit "
-+ "%lu bytes, and not drained, closing",
-+ nc, (unsigned long) nc->recv_mbuf.len,
-+ (unsigned long) nc->recv_mbuf_limit));
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ }
-+ } else {
-+ /* Should never be here */
-+ LOG(LL_ERROR, ("%p invalid len: %d, closing", nc, len));
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ }
-+ break;
-+ }
-+
-+ nc->handler(nc, MG_MQTT_EVENT_BASE + mm.cmd, &mm MG_UD_ARG(user_data));
-+ mbuf_remove(io, len);
-+ }
- break;
-+ }
-+ case MG_EV_POLL: {
-+ struct mg_mqtt_proto_data *pd =
-+ (struct mg_mqtt_proto_data *) nc->proto_data;
-+ double now = mg_time();
-+ if (pd->keep_alive > 0 && pd->last_control_time > 0 &&
-+ (now - pd->last_control_time) > pd->keep_alive) {
-+ LOG(LL_DEBUG, ("Send PINGREQ"));
-+ mg_mqtt_ping(nc);
-+ }
-+ break;
-+ }
- }
- }
-
-@@ -9681,12 +10484,63 @@ static void mg_mqtt_proto_data_destructo
- MG_FREE(proto_data);
- }
-
-+int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic) {
-+ /* TODO(mkm): implement real matching */
-+ if (memchr(exp.p, '#', exp.len)) {
-+ /* exp `foo/#` will become `foo/` */
-+ exp.len -= 1;
-+ /*
-+ * topic should be longer than the expression: e.g. topic `foo/bar` does
-+ * match `foo/#`, but neither `foo` nor `foo/` do.
-+ */
-+ if (topic.len <= exp.len) {
-+ return 0;
-+ }
-+
-+ /* Truncate topic so that it'll pass the next length check */
-+ topic.len = exp.len;
-+ }
-+ if (topic.len != exp.len) {
-+ return 0;
-+ }
-+ return strncmp(topic.p, exp.p, exp.len) == 0;
-+}
-+
-+int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic) {
-+ return mg_mqtt_match_topic_expression(mg_mk_str(exp), topic);
-+}
-+
- void mg_set_protocol_mqtt(struct mg_connection *nc) {
- nc->proto_handler = mqtt_handler;
- nc->proto_data = MG_CALLOC(1, sizeof(struct mg_mqtt_proto_data));
- nc->proto_data_destructor = mg_mqtt_proto_data_destructor;
- }
-
-+static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd,
-+ uint8_t flags, size_t len) {
-+ struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data;
-+ size_t off = nc->send_mbuf.len - len;
-+ uint8_t header = cmd << 4 | (uint8_t) flags;
-+
-+ uint8_t buf[1 + sizeof(size_t)];
-+ uint8_t *vlen = &buf[1];
-+
-+ assert(nc->send_mbuf.len >= len);
-+
-+ buf[0] = header;
-+
-+ /* mqtt variable length encoding */
-+ do {
-+ *vlen = len % 0x80;
-+ len /= 0x80;
-+ if (len > 0) *vlen |= 0x80;
-+ vlen++;
-+ } while (len > 0);
-+
-+ mbuf_insert(&nc->send_mbuf, off, buf, vlen - buf);
-+ pd->last_control_time = mg_time();
-+}
-+
- void mg_send_mqtt_handshake(struct mg_connection *nc, const char *client_id) {
- static struct mg_send_mqtt_handshake_opts opts;
- mg_send_mqtt_handshake_opt(nc, client_id, opts);
-@@ -9694,98 +10548,74 @@ void mg_send_mqtt_handshake(struct mg_co
-
- void mg_send_mqtt_handshake_opt(struct mg_connection *nc, const char *client_id,
- struct mg_send_mqtt_handshake_opts opts) {
-- uint8_t header = MG_MQTT_CMD_CONNECT << 4;
-- uint8_t rem_len;
-- uint16_t keep_alive;
-- uint16_t len;
-+ uint16_t hlen, nlen, rem_len = 0;
- struct mg_mqtt_proto_data *pd = (struct mg_mqtt_proto_data *) nc->proto_data;
-
-- /*
-- * 9: version_header(len, magic_string, version_number), 1: flags, 2:
-- * keep-alive timer,
-- * 2: client_identifier_len, n: client_id
-- */
-- rem_len = 9 + 1 + 2 + 2 + (uint8_t) strlen(client_id);
-+ mg_send(nc, "\00\04MQTT\04", 7);
-+ rem_len += 7;
-
- if (opts.user_name != NULL) {
- opts.flags |= MG_MQTT_HAS_USER_NAME;
-- rem_len += (uint8_t) strlen(opts.user_name) + 2;
- }
- if (opts.password != NULL) {
- opts.flags |= MG_MQTT_HAS_PASSWORD;
-- rem_len += (uint8_t) strlen(opts.password) + 2;
- }
- if (opts.will_topic != NULL && opts.will_message != NULL) {
- opts.flags |= MG_MQTT_HAS_WILL;
-- rem_len += (uint8_t) strlen(opts.will_topic) + 2;
-- rem_len += (uint8_t) strlen(opts.will_message) + 2;
- }
--
-- mg_send(nc, &header, 1);
-- mg_send(nc, &rem_len, 1);
-- mg_send(nc, "\00\06MQIsdp\03", 9);
-- mg_send(nc, &opts.flags, 1);
--
- if (opts.keep_alive == 0) {
- opts.keep_alive = 60;
- }
-
-- keep_alive = htons(opts.keep_alive);
-- mg_send(nc, &keep_alive, 2);
-+ mg_send(nc, &opts.flags, 1);
-+ rem_len += 1;
-
-- len = htons((uint16_t) strlen(client_id));
-- mg_send(nc, &len, 2);
-- mg_send(nc, client_id, strlen(client_id));
-+ nlen = htons(opts.keep_alive);
-+ mg_send(nc, &nlen, 2);
-+ rem_len += 2;
-+
-+ hlen = strlen(client_id);
-+ nlen = htons((uint16_t) hlen);
-+ mg_send(nc, &nlen, 2);
-+ mg_send(nc, client_id, hlen);
-+ rem_len += 2 + hlen;
-
- if (opts.flags & MG_MQTT_HAS_WILL) {
-- len = htons((uint16_t) strlen(opts.will_topic));
-- mg_send(nc, &len, 2);
-- mg_send(nc, opts.will_topic, strlen(opts.will_topic));
--
-- len = htons((uint16_t) strlen(opts.will_message));
-- mg_send(nc, &len, 2);
-- mg_send(nc, opts.will_message, strlen(opts.will_message));
-+ hlen = strlen(opts.will_topic);
-+ nlen = htons((uint16_t) hlen);
-+ mg_send(nc, &nlen, 2);
-+ mg_send(nc, opts.will_topic, hlen);
-+ rem_len += 2 + hlen;
-+
-+ hlen = strlen(opts.will_message);
-+ nlen = htons((uint16_t) hlen);
-+ mg_send(nc, &nlen, 2);
-+ mg_send(nc, opts.will_message, hlen);
-+ rem_len += 2 + hlen;
- }
-
- if (opts.flags & MG_MQTT_HAS_USER_NAME) {
-- len = htons((uint16_t) strlen(opts.user_name));
-- mg_send(nc, &len, 2);
-- mg_send(nc, opts.user_name, strlen(opts.user_name));
-+ hlen = strlen(opts.user_name);
-+ nlen = htons((uint16_t) hlen);
-+ mg_send(nc, &nlen, 2);
-+ mg_send(nc, opts.user_name, hlen);
-+ rem_len += 2 + hlen;
- }
- if (opts.flags & MG_MQTT_HAS_PASSWORD) {
-- len = htons((uint16_t) strlen(opts.password));
-- mg_send(nc, &len, 2);
-- mg_send(nc, opts.password, strlen(opts.password));
-+ hlen = strlen(opts.password);
-+ nlen = htons((uint16_t) hlen);
-+ mg_send(nc, &nlen, 2);
-+ mg_send(nc, opts.password, hlen);
-+ rem_len += 2 + hlen;
- }
-
-+ mg_mqtt_prepend_header(nc, MG_MQTT_CMD_CONNECT, 0, rem_len);
-+
- if (pd != NULL) {
- pd->keep_alive = opts.keep_alive;
- }
- }
-
--static void mg_mqtt_prepend_header(struct mg_connection *nc, uint8_t cmd,
-- uint8_t flags, size_t len) {
-- size_t off = nc->send_mbuf.len - len;
-- uint8_t header = cmd << 4 | (uint8_t) flags;
--
-- uint8_t buf[1 + sizeof(size_t)];
-- uint8_t *vlen = &buf[1];
--
-- assert(nc->send_mbuf.len >= len);
--
-- buf[0] = header;
--
-- /* mqtt variable length encoding */
-- do {
-- *vlen = len % 0x80;
-- len /= 0x80;
-- if (len > 0) *vlen |= 0x80;
-- vlen++;
-- } while (len > 0);
--
-- mbuf_insert(&nc->send_mbuf, off, buf, vlen - buf);
--}
--
- void mg_mqtt_publish(struct mg_connection *nc, const char *topic,
- uint16_t message_id, int flags, const void *data,
- size_t len) {
-@@ -9828,15 +10658,16 @@ void mg_mqtt_subscribe(struct mg_connect
- int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg,
- struct mg_str *topic, uint8_t *qos, int pos) {
- unsigned char *buf = (unsigned char *) msg->payload.p + pos;
-+ int new_pos;
-
-- if ((size_t) pos >= msg->payload.len) {
-- return -1;
-- }
-+ if ((size_t) pos >= msg->payload.len) return -1;
-
- topic->len = buf[0] << 8 | buf[1];
- topic->p = (char *) buf + 2;
-+ new_pos = pos + 2 + topic->len + 1;
-+ if ((size_t) new_pos > msg->payload.len) return -1;
- *qos = buf[2 + topic->len];
-- return pos + 2 + topic->len + 1;
-+ return new_pos;
- }
-
- void mg_mqtt_unsubscribe(struct mg_connection *nc, char **topics,
-@@ -9872,8 +10703,9 @@ void mg_mqtt_connack(struct mg_connectio
- static void mg_send_mqtt_short_command(struct mg_connection *nc, uint8_t cmd,
- uint16_t message_id) {
- uint16_t message_id_net = htons(message_id);
-+ uint8_t flags = (cmd == MG_MQTT_CMD_PUBREL ? 2 : 0);
- mg_send(nc, &message_id_net, 2);
-- mg_mqtt_prepend_header(nc, cmd, MG_MQTT_QOS(1), 2);
-+ mg_mqtt_prepend_header(nc, cmd, flags, 2 /* len */);
- }
-
- void mg_mqtt_puback(struct mg_connection *nc, uint16_t message_id) {
-@@ -9921,15 +10753,15 @@ void mg_mqtt_disconnect(struct mg_connec
-
- #endif /* MG_ENABLE_MQTT */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/mqtt_server.c"
-+#line 1 "mongoose/src/mg_mqtt_server.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/mqtt-server.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_mqtt_server.h" */
-
- #if MG_ENABLE_MQTT_BROKER
-
-@@ -9971,7 +10803,8 @@ void mg_mqtt_broker_init(struct mg_mqtt_
-
- static void mg_mqtt_broker_handle_connect(struct mg_mqtt_broker *brk,
- struct mg_connection *nc) {
-- struct mg_mqtt_session *s = (struct mg_mqtt_session *) calloc(1, sizeof *s);
-+ struct mg_mqtt_session *s =
-+ (struct mg_mqtt_session *) MG_CALLOC(1, sizeof *s);
- if (s == NULL) {
- /* LCOV_EXCL_START */
- mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_SERVER_UNAVAILABLE);
-@@ -9982,8 +10815,7 @@ static void mg_mqtt_broker_handle_connec
- /* TODO(mkm): check header (magic and version) */
-
- mg_mqtt_session_init(brk, s, nc);
-- s->user_data = nc->user_data;
-- nc->user_data = s;
-+ nc->priv_2 = s;
- mg_mqtt_add_session(s);
-
- mg_mqtt_connack(nc, MG_EV_MQTT_CONNACK_ACCEPTED);
-@@ -9991,9 +10823,9 @@ static void mg_mqtt_broker_handle_connec
-
- static void mg_mqtt_broker_handle_subscribe(struct mg_connection *nc,
- struct mg_mqtt_message *msg) {
-- struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->user_data;
-- uint8_t qoss[512];
-- size_t qoss_len = 0;
-+ struct mg_mqtt_session *ss = (struct mg_mqtt_session *) nc->priv_2;
-+ uint8_t qoss[MG_MQTT_MAX_SESSION_SUBSCRIPTIONS];
-+ size_t num_subs = 0;
- struct mg_str topic;
- uint8_t qos;
- int pos;
-@@ -10001,41 +10833,42 @@ static void mg_mqtt_broker_handle_subscr
-
- for (pos = 0;
- (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;) {
-- qoss[qoss_len++] = qos;
-+ if (num_subs >= sizeof(MG_MQTT_MAX_SESSION_SUBSCRIPTIONS) ||
-+ (ss->num_subscriptions + num_subs >=
-+ MG_MQTT_MAX_SESSION_SUBSCRIPTIONS)) {
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ return;
-+ }
-+ qoss[num_subs++] = qos;
- }
-
-- ss->subscriptions = (struct mg_mqtt_topic_expression *) realloc(
-- ss->subscriptions, sizeof(*ss->subscriptions) * qoss_len);
-- for (pos = 0;
-- (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;
-- ss->num_subscriptions++) {
-- te = &ss->subscriptions[ss->num_subscriptions];
-- te->topic = (char *) malloc(topic.len + 1);
-- te->qos = qos;
-- strncpy((char *) te->topic, topic.p, topic.len + 1);
-+ if (num_subs > 0) {
-+ te = (struct mg_mqtt_topic_expression *) MG_REALLOC(
-+ ss->subscriptions,
-+ sizeof(*ss->subscriptions) * (ss->num_subscriptions + num_subs));
-+ if (te == NULL) {
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ return;
-+ }
-+ ss->subscriptions = te;
-+ for (pos = 0;
-+ pos < (int) msg->payload.len &&
-+ (pos = mg_mqtt_next_subscribe_topic(msg, &topic, &qos, pos)) != -1;
-+ ss->num_subscriptions++) {
-+ te = &ss->subscriptions[ss->num_subscriptions];
-+ te->topic = (char *) MG_MALLOC(topic.len + 1);
-+ te->qos = qos;
-+ memcpy((char *) te->topic, topic.p, topic.len);
-+ ((char *) te->topic)[topic.len] = '\0';
-+ }
- }
-
-- mg_mqtt_suback(nc, qoss, qoss_len, msg->message_id);
--}
--
--/*
-- * Matches a topic against a topic expression
-- *
-- * See http://goo.gl/iWk21X
-- *
-- * Returns 1 if it matches; 0 otherwise.
-- */
--static int mg_mqtt_match_topic_expression(const char *exp,
-- const struct mg_str *topic) {
-- /* TODO(mkm): implement real matching */
-- size_t len = strlen(exp);
-- if (strchr(exp, '#')) {
-- len -= 2;
-- if (topic->len < len) {
-- len = topic->len;
-- }
-+ if (pos == (int) msg->payload.len) {
-+ mg_mqtt_suback(nc, qoss, num_subs, msg->message_id);
-+ } else {
-+ /* We did not fully parse the payload, something must be wrong. */
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
- }
-- return strncmp(topic->p, exp, len) == 0;
- }
-
- static void mg_mqtt_broker_handle_publish(struct mg_mqtt_broker *brk,
-@@ -10045,8 +10878,8 @@ static void mg_mqtt_broker_handle_publis
-
- for (s = mg_mqtt_next(brk, NULL); s != NULL; s = mg_mqtt_next(brk, s)) {
- for (i = 0; i < s->num_subscriptions; i++) {
-- if (mg_mqtt_match_topic_expression(s->subscriptions[i].topic,
-- &msg->topic)) {
-+ if (mg_mqtt_vmatch_topic_expression(s->subscriptions[i].topic,
-+ msg->topic)) {
- char buf[100], *p = buf;
- mg_asprintf(&p, sizeof(buf), "%.*s", (int) msg->topic.len,
- msg->topic.p);
-@@ -10068,28 +10901,43 @@ void mg_mqtt_broker(struct mg_connection
- struct mg_mqtt_broker *brk;
-
- if (nc->listener) {
-- brk = (struct mg_mqtt_broker *) nc->listener->user_data;
-+ brk = (struct mg_mqtt_broker *) nc->listener->priv_2;
- } else {
-- brk = (struct mg_mqtt_broker *) nc->user_data;
-+ brk = (struct mg_mqtt_broker *) nc->priv_2;
- }
-
- switch (ev) {
- case MG_EV_ACCEPT:
-- mg_set_protocol_mqtt(nc);
-- nc->user_data = NULL; /* Clear up the inherited pointer to broker */
-+ if (nc->proto_data == NULL) mg_set_protocol_mqtt(nc);
-+ nc->priv_2 = NULL; /* Clear up the inherited pointer to broker */
- break;
- case MG_EV_MQTT_CONNECT:
-- mg_mqtt_broker_handle_connect(brk, nc);
-+ if (nc->priv_2 == NULL) {
-+ mg_mqtt_broker_handle_connect(brk, nc);
-+ } else {
-+ /* Repeated CONNECT */
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ }
- break;
- case MG_EV_MQTT_SUBSCRIBE:
-- mg_mqtt_broker_handle_subscribe(nc, msg);
-+ if (nc->priv_2 != NULL) {
-+ mg_mqtt_broker_handle_subscribe(nc, msg);
-+ } else {
-+ /* Subscribe before CONNECT */
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ }
- break;
- case MG_EV_MQTT_PUBLISH:
-- mg_mqtt_broker_handle_publish(brk, msg);
-+ if (nc->priv_2 != NULL) {
-+ mg_mqtt_broker_handle_publish(brk, msg);
-+ } else {
-+ /* Publish before CONNECT */
-+ nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ }
- break;
- case MG_EV_CLOSE:
-- if (nc->listener && nc->user_data != NULL) {
-- mg_mqtt_close_session((struct mg_mqtt_session *) nc->user_data);
-+ if (nc->listener && nc->priv_2 != NULL) {
-+ mg_mqtt_close_session((struct mg_mqtt_session *) nc->priv_2);
- }
- break;
- }
-@@ -10102,7 +10950,7 @@ struct mg_mqtt_session *mg_mqtt_next(str
-
- #endif /* MG_ENABLE_MQTT_BROKER */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/dns.c"
-+#line 1 "mongoose/src/mg_dns.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -10111,8 +10959,8 @@ struct mg_mqtt_session *mg_mqtt_next(str
-
- #if MG_ENABLE_DNS
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/dns.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_dns.h" */
-
- static int mg_dns_tid = 0xa0;
-
-@@ -10390,7 +11238,7 @@ int mg_parse_dns(const char *buf, int le
-
- size_t mg_dns_uncompress_name(struct mg_dns_message *msg, struct mg_str *name,
- char *dst, int dst_len) {
-- int chunk_len;
-+ int chunk_len, num_ptrs = 0;
- char *old_dst = dst;
- const unsigned char *data = (unsigned char *) name->p;
- const unsigned char *end = (unsigned char *) msg->pkt.p + msg->pkt.len;
-@@ -10405,14 +11253,21 @@ size_t mg_dns_uncompress_name(struct mg_
- return 0;
- }
-
-- if (chunk_len & 0xc0) {
-+ if ((chunk_len & 0xc0) == 0xc0) {
- uint16_t off = (data[-1] & (~0xc0)) << 8 | data[0];
- if (off >= msg->pkt.len) {
- return 0;
- }
-+ /* Basic circular loop avoidance: allow up to 16 pointer hops. */
-+ if (++num_ptrs > 15) {
-+ return 0;
-+ }
- data = (unsigned char *) msg->pkt.p + off;
- continue;
- }
-+ if (chunk_len > 63) {
-+ return 0;
-+ }
- if (chunk_len > leeway) {
- chunk_len = leeway;
- }
-@@ -10437,12 +11292,13 @@ size_t mg_dns_uncompress_name(struct mg_
- return dst - old_dst;
- }
-
--static void dns_handler(struct mg_connection *nc, int ev, void *ev_data) {
-+static void dns_handler(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data)) {
- struct mbuf *io = &nc->recv_mbuf;
- struct mg_dns_message msg;
-
- /* Pass low-level events to the user handler */
-- nc->handler(nc, ev, ev_data);
-+ nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
-
- switch (ev) {
- case MG_EV_RECV:
-@@ -10461,7 +11317,7 @@ static void dns_handler(struct mg_connec
- mg_send(nc, io->buf, io->len);
- } else {
- /* Call user handler with parsed message */
-- nc->handler(nc, MG_DNS_MESSAGE, &msg);
-+ nc->handler(nc, MG_DNS_MESSAGE, &msg MG_UD_ARG(user_data));
- }
- mbuf_remove(io, io->len);
- break;
-@@ -10474,7 +11330,7 @@ void mg_set_protocol_dns(struct mg_conne
-
- #endif /* MG_ENABLE_DNS */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/dns_server.c"
-+#line 1 "mongoose/src/mg_dns_server.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -10483,8 +11339,8 @@ void mg_set_protocol_dns(struct mg_conne
-
- #if MG_ENABLE_DNS_SERVER
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/dns-server.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "dns-server.h" */
-
- struct mg_dns_reply mg_dns_create_reply(struct mbuf *io,
- struct mg_dns_message *msg) {
-@@ -10548,7 +11404,7 @@ int mg_dns_reply_record(struct mg_dns_re
-
- #endif /* MG_ENABLE_DNS_SERVER */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/resolv.c"
-+#line 1 "mongoose/src/mg_resolv.c"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -10557,17 +11413,13 @@ int mg_dns_reply_record(struct mg_dns_re
-
- #if MG_ENABLE_ASYNC_RESOLVER
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/resolv.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_resolv.h" */
-
- #ifndef MG_DEFAULT_NAMESERVER
- #define MG_DEFAULT_NAMESERVER "8.8.8.8"
- #endif
-
--static const char *mg_default_dns_server = "udp://" MG_DEFAULT_NAMESERVER ":53";
--
--MG_INTERNAL char mg_dns_server[256];
--
- struct mg_resolve_async_request {
- char name[1024];
- int query;
-@@ -10609,10 +11461,12 @@ static int mg_get_ip_address_of_nameserv
- break;
- }
- if (RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hSub) == ERROR_SUCCESS &&
-- (RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value,
-- &len) == ERROR_SUCCESS ||
-- RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value,
-- &len) == ERROR_SUCCESS)) {
-+ ((RegQueryValueExW(hSub, L"NameServer", 0, &type, (void *) value,
-+ &len) == ERROR_SUCCESS &&
-+ value[0] != '\0') ||
-+ (RegQueryValueExW(hSub, L"DhcpNameServer", 0, &type, (void *) value,
-+ &len) == ERROR_SUCCESS &&
-+ value[0] != '\0'))) {
- /*
- * See https://github.com/cesanta/mongoose/issues/176
- * The value taken from the registry can be empty, a single
-@@ -10621,13 +11475,11 @@ static int mg_get_ip_address_of_nameserv
- * If it's multiple IP addresses, take the first one.
- */
- wchar_t *comma = wcschr(value, ',');
-- if (value[0] == '\0') {
-- continue;
-- }
- if (comma != NULL) {
- *comma = '\0';
- }
-- snprintf(name, name_len, "udp://%S:53", value);
-+ /* %S will convert wchar_t -> char */
-+ snprintf(name, name_len, "%S", value);
- ret = 0;
- RegCloseKey(hSub);
- break;
-@@ -10635,18 +11487,18 @@ static int mg_get_ip_address_of_nameserv
- }
- RegCloseKey(hKey);
- }
--#elif MG_ENABLE_FILESYSTEM
-+#elif MG_ENABLE_FILESYSTEM && defined(MG_RESOLV_CONF_FILE_NAME)
- FILE *fp;
- char line[512];
-
-- if ((fp = mg_fopen("/etc/resolv.conf", "r")) == NULL) {
-+ if ((fp = mg_fopen(MG_RESOLV_CONF_FILE_NAME, "r")) == NULL) {
- ret = -1;
- } else {
- /* Try to figure out what nameserver to use */
- for (ret = -1; fgets(line, sizeof(line), fp) != NULL;) {
- unsigned int a, b, c, d;
- if (sscanf(line, "nameserver %u.%u.%u.%u", &a, &b, &c, &d) == 4) {
-- snprintf(name, name_len, "udp://%u.%u.%u.%u:53", a, b, c, d);
-+ snprintf(name, name_len, "%u.%u.%u.%u", a, b, c, d);
- ret = 0;
- break;
- }
-@@ -10654,14 +11506,14 @@ static int mg_get_ip_address_of_nameserv
- (void) fclose(fp);
- }
- #else
-- snprintf(name, name_len, "%s", mg_default_dns_server);
-+ snprintf(name, name_len, "%s", MG_DEFAULT_NAMESERVER);
- #endif /* _WIN32 */
-
- return ret;
- }
-
- int mg_resolve_from_hosts_file(const char *name, union socket_address *usa) {
--#if MG_ENABLE_FILESYSTEM
-+#if MG_ENABLE_FILESYSTEM && defined(MG_HOSTS_FILE_NAME)
- /* TODO(mkm) cache /etc/hosts */
- FILE *fp;
- char line[1024];
-@@ -10670,7 +11522,7 @@ int mg_resolve_from_hosts_file(const cha
- unsigned int a, b, c, d;
- int len = 0;
-
-- if ((fp = mg_fopen("/etc/hosts", "r")) == NULL) {
-+ if ((fp = mg_fopen(MG_HOSTS_FILE_NAME, "r")) == NULL) {
- return -1;
- }
-
-@@ -10699,15 +11551,19 @@ int mg_resolve_from_hosts_file(const cha
- return -1;
- }
-
--static void mg_resolve_async_eh(struct mg_connection *nc, int ev, void *data) {
-+static void mg_resolve_async_eh(struct mg_connection *nc, int ev,
-+ void *data MG_UD_ARG(void *user_data)) {
- time_t now = (time_t) mg_time();
- struct mg_resolve_async_request *req;
- struct mg_dns_message *msg;
- int first = 0;
-+#if !MG_ENABLE_CALLBACK_USERDATA
-+ void *user_data = nc->user_data;
-+#endif
-
-- DBG(("ev=%d user_data=%p", ev, nc->user_data));
-+ if (ev != MG_EV_POLL) DBG(("ev=%d user_data=%p", ev, user_data));
-
-- req = (struct mg_resolve_async_request *) nc->user_data;
-+ req = (struct mg_resolve_async_request *) user_data;
-
- if (req == NULL) {
- return;
-@@ -10758,6 +11614,11 @@ static void mg_resolve_async_eh(struct m
- case MG_EV_CLOSE:
- /* If we got here with request still not done, fire an error callback. */
- if (req != NULL) {
-+ char addr[32];
-+ mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP);
-+#ifdef MG_LOG_DNS_FAILURES
-+ LOG(LL_ERROR, ("Failed to resolve '%s', server %s", req->name, addr));
-+#endif
- req->callback(NULL, req->data, req->err);
- nc->user_data = NULL;
- MG_FREE(req);
-@@ -10778,7 +11639,12 @@ int mg_resolve_async_opt(struct mg_mgr *
- struct mg_resolve_async_opts opts) {
- struct mg_resolve_async_request *req;
- struct mg_connection *dns_nc;
-- const char *nameserver = opts.nameserver_url;
-+ const char *nameserver = opts.nameserver;
-+ char dns_server_buff[17], nameserver_url[26];
-+
-+ if (nameserver == NULL) {
-+ nameserver = mgr->nameserver;
-+ }
-
- DBG(("%s %d %p", name, query, opts.dns_conn));
-
-@@ -10789,6 +11655,8 @@ int mg_resolve_async_opt(struct mg_mgr *
- }
-
- strncpy(req->name, name, sizeof(req->name));
-+ req->name[sizeof(req->name) - 1] = '\0';
-+
- req->query = query;
- req->callback = cb;
- req->data = data;
-@@ -10797,19 +11665,20 @@ int mg_resolve_async_opt(struct mg_mgr *
- req->timeout = opts.timeout ? opts.timeout : 5;
-
- /* Lazily initialize dns server */
-- if (nameserver == NULL && mg_dns_server[0] == '\0' &&
-- mg_get_ip_address_of_nameserver(mg_dns_server, sizeof(mg_dns_server)) ==
-- -1) {
-- strncpy(mg_dns_server, mg_default_dns_server, sizeof(mg_dns_server));
-- }
--
- if (nameserver == NULL) {
-- nameserver = mg_dns_server;
-+ if (mg_get_ip_address_of_nameserver(dns_server_buff,
-+ sizeof(dns_server_buff)) != -1) {
-+ nameserver = dns_server_buff;
-+ } else {
-+ nameserver = MG_DEFAULT_NAMESERVER;
-+ }
- }
-
-- dns_nc = mg_connect(mgr, nameserver, mg_resolve_async_eh);
-+ snprintf(nameserver_url, sizeof(nameserver_url), "udp://%s:53", nameserver);
-+
-+ dns_nc = mg_connect(mgr, nameserver_url, MG_CB(mg_resolve_async_eh, NULL));
- if (dns_nc == NULL) {
-- free(req);
-+ MG_FREE(req);
- return -1;
- }
- dns_nc->user_data = req;
-@@ -10820,9 +11689,17 @@ int mg_resolve_async_opt(struct mg_mgr *
- return 0;
- }
-
-+void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver) {
-+ MG_FREE((char *) mgr->nameserver);
-+ mgr->nameserver = NULL;
-+ if (nameserver != NULL) {
-+ mgr->nameserver = strdup(nameserver);
-+ }
-+}
-+
- #endif /* MG_ENABLE_ASYNC_RESOLVER */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/coap.c"
-+#line 1 "mongoose/src/mg_coap.c"
- #endif
- /*
- * Copyright (c) 2015 Cesanta Software Limited
-@@ -10841,8 +11718,8 @@ int mg_resolve_async_opt(struct mg_mgr *
- * license, as set out in <https://www.cesanta.com/license>.
- */
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/coap.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_coap.h" */
-
- #if MG_ENABLE_COAP
-
-@@ -11285,7 +12162,7 @@ uint32_t mg_coap_compose(struct mg_coap_
-
- /* saving previous lenght to handle non-empty mbuf */
- prev_io_len = io->len;
-- mbuf_append(io, NULL, packet_size);
-+ if (mbuf_append(io, NULL, packet_size) == 0) return MG_COAP_ERROR;
- ptr = io->buf + prev_io_len;
-
- /*
-@@ -11369,14 +12246,15 @@ uint32_t mg_coap_send_ack(struct mg_conn
- return mg_coap_send_message(nc, &cm);
- }
-
--static void coap_handler(struct mg_connection *nc, int ev, void *ev_data) {
-+static void coap_handler(struct mg_connection *nc, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data)) {
- struct mbuf *io = &nc->recv_mbuf;
- struct mg_coap_message cm;
- uint32_t parse_res;
-
- memset(&cm, 0, sizeof(cm));
-
-- nc->handler(nc, ev, ev_data);
-+ nc->handler(nc, ev, ev_data MG_UD_ARG(user_data));
-
- switch (ev) {
- case MG_EV_RECV:
-@@ -11389,7 +12267,8 @@ static void coap_handler(struct mg_conne
- */
- cm.flags |= MG_COAP_FORMAT_ERROR; /* LCOV_EXCL_LINE */
- } /* LCOV_EXCL_LINE */
-- nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type, &cm);
-+ nc->handler(nc, MG_COAP_EVENT_BASE + cm.msg_type,
-+ &cm MG_UD_ARG(user_data));
- }
-
- mg_coap_free_options(&cm);
-@@ -11420,316 +12299,16 @@ int mg_set_protocol_coap(struct mg_conne
-
- #endif /* MG_ENABLE_COAP */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/tun.c"
--#endif
--/*
-- * Copyright (c) 2014 Cesanta Software Limited
-- * All rights reserved
-- */
--
--#if MG_ENABLE_TUN
--
--/* Amalgamated: #include "common/cs_dbg.h" */
--/* Amalgamated: #include "mongoose/src/http.h" */
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/net.h" */
--/* Amalgamated: #include "mongoose/src/net_if_tun.h" */
--/* Amalgamated: #include "mongoose/src/tun.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
--
--static void mg_tun_reconnect(struct mg_tun_client *client, int timeout);
--
--static void mg_tun_init_client(struct mg_tun_client *client, struct mg_mgr *mgr,
-- struct mg_iface *iface, const char *dispatcher,
-- struct mg_tun_ssl_opts ssl) {
-- client->mgr = mgr;
-- client->iface = iface;
-- client->disp_url = dispatcher;
-- client->last_stream_id = 0;
-- client->ssl = ssl;
--
-- client->disp = NULL; /* will be set by mg_tun_reconnect */
-- client->listener = NULL; /* will be set by mg_do_bind */
-- client->reconnect = NULL; /* will be set by mg_tun_reconnect */
--}
--
--void mg_tun_log_frame(struct mg_tun_frame *frame) {
-- LOG(LL_DEBUG, ("Got TUN frame: type=0x%x, flags=0x%x stream_id=0x%lx, "
-- "len=%zu",
-- frame->type, frame->flags, frame->stream_id, frame->body.len));
--#if MG_ENABLE_HEXDUMP
-- {
-- char hex[512];
-- mg_hexdump(frame->body.p, frame->body.len, hex, sizeof(hex) - 1);
-- hex[sizeof(hex) - 1] = '\0';
-- LOG(LL_DEBUG, ("body:\n%s", hex));
-- }
--#else
-- LOG(LL_DEBUG, ("body: '%.*s'", (int) frame->body.len, frame->body.p));
--#endif
--}
--
--static void mg_tun_close_all(struct mg_tun_client *client) {
-- struct mg_connection *nc;
-- for (nc = client->mgr->active_connections; nc != NULL; nc = nc->next) {
-- if (nc->iface == client->iface && !(nc->flags & MG_F_LISTENING)) {
-- LOG(LL_DEBUG, ("Closing tunneled connection %p", nc));
-- nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-- /* mg_close_conn(nc); */
-- }
-- }
--}
--
--static void mg_tun_client_handler(struct mg_connection *nc, int ev,
-- void *ev_data) {
-- struct mg_tun_client *client = (struct mg_tun_client *) nc->user_data;
--
-- switch (ev) {
-- case MG_EV_CONNECT: {
-- int err = *(int *) ev_data;
--
-- if (err) {
-- LOG(LL_ERROR, ("Cannot connect to the tunnel dispatcher: %d", err));
-- } else {
-- LOG(LL_INFO, ("Connected to the tunnel dispatcher"));
-- }
-- break;
-- }
-- case MG_EV_HTTP_REPLY: {
-- struct http_message *hm = (struct http_message *) ev_data;
--
-- if (hm->resp_code != 200) {
-- LOG(LL_ERROR,
-- ("Tunnel dispatcher reply non-OK status code %d", hm->resp_code));
-- }
-- break;
-- }
-- case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
-- LOG(LL_INFO, ("Tunnel dispatcher handshake done"));
-- break;
-- }
-- case MG_EV_WEBSOCKET_FRAME: {
-- struct websocket_message *wm = (struct websocket_message *) ev_data;
-- struct mg_connection *tc;
-- struct mg_tun_frame frame;
--
-- if (mg_tun_parse_frame(wm->data, wm->size, &frame) == -1) {
-- LOG(LL_ERROR, ("Got invalid tun frame dropping", wm->size));
-- break;
-- }
--
-- mg_tun_log_frame(&frame);
--
-- tc = mg_tun_if_find_conn(client, frame.stream_id);
-- if (tc == NULL) {
-- if (frame.body.len > 0) {
-- LOG(LL_DEBUG, ("Got frame after receiving end has been closed"));
-- }
-- break;
-- }
-- if (frame.body.len > 0) {
-- mg_if_recv_tcp_cb(tc, (void *) frame.body.p, frame.body.len,
-- 0 /* own */);
-- }
-- if (frame.flags & MG_TUN_F_END_STREAM) {
-- LOG(LL_DEBUG, ("Closing tunneled connection because got end of stream "
-- "from other end"));
-- tc->flags |= MG_F_CLOSE_IMMEDIATELY;
-- mg_close_conn(tc);
-- }
-- break;
-- }
-- case MG_EV_CLOSE: {
-- LOG(LL_DEBUG, ("Closing all tunneled connections"));
-- /*
-- * The client might have been already freed when the listening socket is
-- * closed.
-- */
-- if (client != NULL) {
-- mg_tun_close_all(client);
-- client->disp = NULL;
-- LOG(LL_INFO, ("Dispatcher connection is no more, reconnecting"));
-- /* TODO(mkm): implement exp back off */
-- mg_tun_reconnect(client, MG_TUN_RECONNECT_INTERVAL);
-- }
-- break;
-- }
-- default:
-- break;
-- }
--}
--
--static void mg_tun_do_reconnect(struct mg_tun_client *client) {
-- struct mg_connection *dc;
-- struct mg_connect_opts opts;
-- memset(&opts, 0, sizeof(opts));
--#if MG_ENABLE_SSL
-- opts.ssl_cert = client->ssl.ssl_cert;
-- opts.ssl_key = client->ssl.ssl_key;
-- opts.ssl_ca_cert = client->ssl.ssl_ca_cert;
--#endif
-- /* HTTP/Websocket listener */
-- if ((dc = mg_connect_ws_opt(client->mgr, mg_tun_client_handler, opts,
-- client->disp_url, MG_TUN_PROTO_NAME, NULL)) ==
-- NULL) {
-- LOG(LL_ERROR,
-- ("Cannot connect to WS server on addr [%s]\n", client->disp_url));
-- return;
-- }
--
-- client->disp = dc;
-- dc->user_data = client;
--}
--
--void mg_tun_reconnect_ev_handler(struct mg_connection *nc, int ev,
-- void *ev_data) {
-- struct mg_tun_client *client = (struct mg_tun_client *) nc->user_data;
-- (void) ev_data;
--
-- switch (ev) {
-- case MG_EV_TIMER:
-- if (!(client->listener->flags & MG_F_TUN_DO_NOT_RECONNECT)) {
-- mg_tun_do_reconnect(client);
-- } else {
-- /* Reconnecting is suppressed, we'll check again at the next poll */
-- mg_tun_reconnect(client, 0);
-- }
-- break;
-- }
--}
--
--static void mg_tun_reconnect(struct mg_tun_client *client, int timeout) {
-- if (client->reconnect == NULL) {
-- client->reconnect =
-- mg_add_sock(client->mgr, INVALID_SOCKET, mg_tun_reconnect_ev_handler);
-- client->reconnect->user_data = client;
-- }
-- client->reconnect->ev_timer_time = mg_time() + timeout;
--}
--
--static struct mg_tun_client *mg_tun_create_client(struct mg_mgr *mgr,
-- const char *dispatcher,
-- struct mg_tun_ssl_opts ssl) {
-- struct mg_tun_client *client = NULL;
-- struct mg_iface *iface = mg_find_iface(mgr, &mg_tun_iface_vtable, NULL);
-- if (iface == NULL) {
-- LOG(LL_ERROR, ("The tun feature requires the manager to have a tun "
-- "interface enabled"));
-- return NULL;
-- }
--
-- client = (struct mg_tun_client *) MG_MALLOC(sizeof(*client));
-- mg_tun_init_client(client, mgr, iface, dispatcher, ssl);
-- iface->data = client;
--
-- /*
-- * We need to give application a chance to set MG_F_TUN_DO_NOT_RECONNECT on a
-- * listening connection right after mg_tun_bind_opt() returned it, so we
-- * should use mg_tun_reconnect() here, instead of mg_tun_do_reconnect()
-- */
-- mg_tun_reconnect(client, 0);
-- return client;
--}
--
--void mg_tun_destroy_client(struct mg_tun_client *client) {
-- /*
-- * NOTE:
-- * `client` is NULL in case of OOM
-- * `client->disp` is NULL if connection failed
-- * `client->iface is NULL is `mg_find_iface` failed
-- */
--
-- if (client != NULL && client->disp != NULL) {
-- /* the dispatcher connection handler will in turn close all tunnels */
-- client->disp->flags |= MG_F_CLOSE_IMMEDIATELY;
-- /* this is used as a signal to other tun handlers that the party is over */
-- client->disp->user_data = NULL;
-- }
--
-- if (client != NULL && client->reconnect != NULL) {
-- client->reconnect->flags |= MG_F_CLOSE_IMMEDIATELY;
-- }
--
-- if (client != NULL && client->iface != NULL) {
-- client->iface->data = NULL;
-- }
--
-- MG_FREE(client);
--}
--
--static struct mg_connection *mg_tun_do_bind(struct mg_tun_client *client,
-- mg_event_handler_t handler,
-- struct mg_bind_opts opts) {
-- struct mg_connection *lc;
-- opts.iface = client->iface;
-- lc = mg_bind_opt(client->mgr, ":1234" /* dummy port */, handler, opts);
-- client->listener = lc;
-- return lc;
--}
--
--struct mg_connection *mg_tun_bind_opt(struct mg_mgr *mgr,
-- const char *dispatcher,
-- mg_event_handler_t handler,
-- struct mg_bind_opts opts) {
--#if MG_ENABLE_SSL
-- struct mg_tun_ssl_opts ssl = {opts.ssl_cert, opts.ssl_key, opts.ssl_ca_cert};
--#else
-- struct mg_tun_ssl_opts ssl = {0};
--#endif
-- struct mg_tun_client *client = mg_tun_create_client(mgr, dispatcher, ssl);
-- if (client == NULL) {
-- return NULL;
-- }
--#if MG_ENABLE_SSL
-- /* these options don't make sense in the local mouth of the tunnel */
-- opts.ssl_cert = NULL;
-- opts.ssl_key = NULL;
-- opts.ssl_ca_cert = NULL;
--#endif
-- return mg_tun_do_bind(client, handler, opts);
--}
--
--int mg_tun_parse_frame(void *data, size_t len, struct mg_tun_frame *frame) {
-- const size_t header_size = sizeof(uint32_t) + sizeof(uint8_t) * 2;
-- if (len < header_size) {
-- return -1;
-- }
--
-- frame->type = *(uint8_t *) (data);
-- frame->flags = *(uint8_t *) ((char *) data + 1);
-- memcpy(&frame->stream_id, (char *) data + 2, sizeof(uint32_t));
-- frame->stream_id = ntohl(frame->stream_id);
-- frame->body.p = (char *) data + header_size;
-- frame->body.len = len - header_size;
-- return 0;
--}
--
--void mg_tun_send_frame(struct mg_connection *ws, uint32_t stream_id,
-- uint8_t type, uint8_t flags, struct mg_str msg) {
-- stream_id = htonl(stream_id);
-- {
-- struct mg_str parts[] = {
-- {(char *) &type, sizeof(type)},
-- {(char *) &flags, sizeof(flags)},
-- {(char *) &stream_id, sizeof(stream_id)},
-- {msg.p, msg.len} /* vc6 doesn't like just `msg` here */};
-- mg_send_websocket_framev(ws, WEBSOCKET_OP_BINARY, parts,
-- sizeof(parts) / sizeof(parts[0]));
-- }
--}
--
--#endif /* MG_ENABLE_TUN */
--#ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/sntp.c"
-+#line 1 "mongoose/src/mg_sntp.c"
- #endif
- /*
- * Copyright (c) 2016 Cesanta Software Limited
- * All rights reserved
- */
-
--/* Amalgamated: #include "mongoose/src/internal.h" */
--/* Amalgamated: #include "mongoose/src/sntp.h" */
--/* Amalgamated: #include "mongoose/src/util.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+/* Amalgamated: #include "mg_sntp.h" */
-+/* Amalgamated: #include "mg_util.h" */
-
- #if MG_ENABLE_SNTP
-
-@@ -11773,7 +12352,7 @@ static void mg_get_ntp_ts(const char *nt
- }
-
- void mg_sntp_send_request(struct mg_connection *c) {
-- char buf[48] = {0};
-+ uint8_t buf[48] = {0};
- /*
- * header - 8 bit:
- * LI (2 bit) - 3 (not in sync), VN (3 bit) - 4 (version),
-@@ -11801,16 +12380,16 @@ void mg_sntp_send_request(struct mg_conn
- * but if local clock is absolutely broken (and doesn't work even
- * as simple timer), it is better to disable it
- */
--#ifndef MG_SNMP_NO_DELAY_CORRECTION
-+#ifndef MG_SNTP_NO_DELAY_CORRECTION
- uint32_t sec;
-- sec = htonl(mg_time() + SNTP_TIME_OFFSET);
-+ sec = htonl((uint32_t)(mg_time() + SNTP_TIME_OFFSET));
- memcpy(&buf[40], &sec, sizeof(sec));
- #endif
-
- mg_send(c, buf, sizeof(buf));
- }
-
--#ifndef MG_SNMP_NO_DELAY_CORRECTION
-+#ifndef MG_SNTP_NO_DELAY_CORRECTION
- static uint64_t mg_calculate_delay(uint64_t t1, uint64_t t2, uint64_t t3) {
- /* roundloop delay = (T4 - T1) - (T3 - T2) */
- uint64_t d1 = ((mg_time() + SNTP_TIME_OFFSET) * 1000000) -
-@@ -11825,12 +12404,10 @@ static uint64_t mg_calculate_delay(uint6
- MG_INTERNAL int mg_sntp_parse_reply(const char *buf, int len,
- struct mg_sntp_message *msg) {
- uint8_t hdr;
-- uint64_t orig_ts_T1, recv_ts_T2, trsm_ts_T3, delay = 0;
-+ uint64_t trsm_ts_T3, delay = 0;
- int mode;
- struct timeval tv;
-
-- (void) orig_ts_T1;
-- (void) recv_ts_T2;
- if (len < 48) {
- return -1;
- }
-@@ -11854,10 +12431,13 @@ MG_INTERNAL int mg_sntp_parse_reply(cons
-
- mg_get_ntp_ts(&buf[40], &trsm_ts_T3);
-
--#ifndef MG_SNMP_NO_DELAY_CORRECTION
-- mg_get_ntp_ts(&buf[24], &orig_ts_T1);
-- mg_get_ntp_ts(&buf[32], &recv_ts_T2);
-- delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3);
-+#ifndef MG_SNTP_NO_DELAY_CORRECTION
-+ {
-+ uint64_t orig_ts_T1, recv_ts_T2;
-+ mg_get_ntp_ts(&buf[24], &orig_ts_T1);
-+ mg_get_ntp_ts(&buf[32], &recv_ts_T2);
-+ delay = mg_calculate_delay(orig_ts_T1, recv_ts_T2, trsm_ts_T3);
-+ }
- #endif
-
- mg_ntp_to_tv(trsm_ts_T3, &tv);
-@@ -11867,19 +12447,20 @@ MG_INTERNAL int mg_sntp_parse_reply(cons
- return 0;
- }
-
--static void mg_sntp_handler(struct mg_connection *c, int ev, void *ev_data) {
-+static void mg_sntp_handler(struct mg_connection *c, int ev,
-+ void *ev_data MG_UD_ARG(void *user_data)) {
- struct mbuf *io = &c->recv_mbuf;
- struct mg_sntp_message msg;
-
-- c->handler(c, ev, ev_data);
-+ c->handler(c, ev, ev_data MG_UD_ARG(user_data));
-
- switch (ev) {
- case MG_EV_RECV: {
- if (mg_sntp_parse_reply(io->buf, io->len, &msg) < 0) {
- DBG(("Invalid SNTP packet received (%d)", (int) io->len));
-- c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL);
-+ c->handler(c, MG_SNTP_MALFORMED_REPLY, NULL MG_UD_ARG(user_data));
- } else {
-- c->handler(c, MG_SNTP_REPLY, (void *) &msg);
-+ c->handler(c, MG_SNTP_REPLY, (void *) &msg MG_UD_ARG(user_data));
- }
-
- mbuf_remove(io, io->len);
-@@ -11899,7 +12480,8 @@ int mg_set_protocol_sntp(struct mg_conne
- }
-
- struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
-- mg_event_handler_t event_handler,
-+ MG_CB(mg_event_handler_t event_handler,
-+ void *user_data),
- const char *sntp_server_name) {
- struct mg_connection *c = NULL;
- char url[100], *p_url = url;
-@@ -11922,7 +12504,7 @@ struct mg_connection *mg_sntp_connect(st
-
- mg_asprintf(&p_url, sizeof(url), "%s%s%s", proto, sntp_server_name, port);
-
-- c = mg_connect(mgr, p_url, event_handler);
-+ c = mg_connect(mgr, p_url, event_handler MG_UD_ARG(user_data));
-
- if (c == NULL) {
- goto cleanup;
-@@ -11944,13 +12526,16 @@ struct sntp_data {
- };
-
- static void mg_sntp_util_ev_handler(struct mg_connection *c, int ev,
-- void *ev_data) {
-- struct sntp_data *sd = (struct sntp_data *) c->user_data;
-+ void *ev_data MG_UD_ARG(void *user_data)) {
-+#if !MG_ENABLE_CALLBACK_USERDATA
-+ void *user_data = c->user_data;
-+#endif
-+ struct sntp_data *sd = (struct sntp_data *) user_data;
-
- switch (ev) {
- case MG_EV_CONNECT:
- if (*(int *) ev_data != 0) {
-- mg_call(c, sd->hander, MG_SNTP_FAILED, NULL);
-+ mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL);
- break;
- }
- /* fallthrough */
-@@ -11960,20 +12545,20 @@ static void mg_sntp_util_ev_handler(stru
- mg_set_timer(c, mg_time() + 10);
- sd->count++;
- } else {
-- mg_call(c, sd->hander, MG_SNTP_FAILED, NULL);
-+ mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL);
- c->flags |= MG_F_CLOSE_IMMEDIATELY;
- }
- break;
- case MG_SNTP_MALFORMED_REPLY:
-- mg_call(c, sd->hander, MG_SNTP_FAILED, NULL);
-+ mg_call(c, sd->hander, c->user_data, MG_SNTP_FAILED, NULL);
- c->flags |= MG_F_CLOSE_IMMEDIATELY;
- break;
- case MG_SNTP_REPLY:
-- mg_call(c, sd->hander, MG_SNTP_REPLY, ev_data);
-+ mg_call(c, sd->hander, c->user_data, MG_SNTP_REPLY, ev_data);
- c->flags |= MG_F_CLOSE_IMMEDIATELY;
- break;
- case MG_EV_CLOSE:
-- MG_FREE(c->user_data);
-+ MG_FREE(user_data);
- c->user_data = NULL;
- break;
- }
-@@ -11988,20 +12573,185 @@ struct mg_connection *mg_sntp_get_time(s
- return NULL;
- }
-
-- c = mg_sntp_connect(mgr, mg_sntp_util_ev_handler, sntp_server_name);
-+ c = mg_sntp_connect(mgr, MG_CB(mg_sntp_util_ev_handler, sd),
-+ sntp_server_name);
- if (c == NULL) {
- MG_FREE(sd);
- return NULL;
- }
-
- sd->hander = event_handler;
-+#if !MG_ENABLE_CALLBACK_USERDATA
- c->user_data = sd;
-+#endif
-
- return c;
- }
-
- #endif /* MG_ENABLE_SNTP */
- #ifdef MG_MODULE_LINES
-+#line 1 "mongoose/src/mg_socks.c"
-+#endif
-+/*
-+ * Copyright (c) 2017 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#if MG_ENABLE_SOCKS
-+
-+/* Amalgamated: #include "mg_socks.h" */
-+/* Amalgamated: #include "mg_internal.h" */
-+
-+/*
-+ * https://www.ietf.org/rfc/rfc1928.txt paragraph 3, handle client handshake
-+ *
-+ * +----+----------+----------+
-+ * |VER | NMETHODS | METHODS |
-+ * +----+----------+----------+
-+ * | 1 | 1 | 1 to 255 |
-+ * +----+----------+----------+
-+ */
-+static void mg_socks5_handshake(struct mg_connection *c) {
-+ struct mbuf *r = &c->recv_mbuf;
-+ if (r->buf[0] != MG_SOCKS_VERSION) {
-+ c->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ } else if (r->len > 2 && (size_t) r->buf[1] + 2 <= r->len) {
-+ /* https://www.ietf.org/rfc/rfc1928.txt paragraph 3 */
-+ unsigned char reply[2] = {MG_SOCKS_VERSION, MG_SOCKS_HANDSHAKE_FAILURE};
-+ int i;
-+ for (i = 2; i < r->buf[1] + 2; i++) {
-+ /* TODO(lsm): support other auth methods */
-+ if (r->buf[i] == MG_SOCKS_HANDSHAKE_NOAUTH) reply[1] = r->buf[i];
-+ }
-+ mbuf_remove(r, 2 + r->buf[1]);
-+ mg_send(c, reply, sizeof(reply));
-+ c->flags |= MG_SOCKS_HANDSHAKE_DONE; /* Mark handshake done */
-+ }
-+}
-+
-+static void disband(struct mg_connection *c) {
-+ struct mg_connection *c2 = (struct mg_connection *) c->user_data;
-+ if (c2 != NULL) {
-+ c2->flags |= MG_F_SEND_AND_CLOSE;
-+ c2->user_data = NULL;
-+ }
-+ c->flags |= MG_F_SEND_AND_CLOSE;
-+ c->user_data = NULL;
-+}
-+
-+static void relay_data(struct mg_connection *c) {
-+ struct mg_connection *c2 = (struct mg_connection *) c->user_data;
-+ if (c2 != NULL) {
-+ mg_send(c2, c->recv_mbuf.buf, c->recv_mbuf.len);
-+ mbuf_remove(&c->recv_mbuf, c->recv_mbuf.len);
-+ } else {
-+ c->flags |= MG_F_SEND_AND_CLOSE;
-+ }
-+}
-+
-+static void serv_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
-+ if (ev == MG_EV_CLOSE) {
-+ disband(c);
-+ } else if (ev == MG_EV_RECV) {
-+ relay_data(c);
-+ } else if (ev == MG_EV_CONNECT) {
-+ int res = *(int *) ev_data;
-+ if (res != 0) LOG(LL_ERROR, ("connect error: %d", res));
-+ }
-+}
-+
-+static void mg_socks5_connect(struct mg_connection *c, const char *addr) {
-+ struct mg_connection *serv = mg_connect(c->mgr, addr, serv_ev_handler);
-+ serv->user_data = c;
-+ c->user_data = serv;
-+}
-+
-+/*
-+ * Request, https://www.ietf.org/rfc/rfc1928.txt paragraph 4
-+ *
-+ * +----+-----+-------+------+----------+----------+
-+ * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
-+ * +----+-----+-------+------+----------+----------+
-+ * | 1 | 1 | X'00' | 1 | Variable | 2 |
-+ * +----+-----+-------+------+----------+----------+
-+ */
-+static void mg_socks5_handle_request(struct mg_connection *c) {
-+ struct mbuf *r = &c->recv_mbuf;
-+ unsigned char *p = (unsigned char *) r->buf;
-+ unsigned char addr_len = 4, reply = MG_SOCKS_SUCCESS;
-+ int ver, cmd, atyp;
-+ char addr[300];
-+
-+ if (r->len < 8) return; /* return if not fully buffered. min DST.ADDR is 2 */
-+ ver = p[0];
-+ cmd = p[1];
-+ atyp = p[3];
-+
-+ /* TODO(lsm): support other commands */
-+ if (ver != MG_SOCKS_VERSION || cmd != MG_SOCKS_CMD_CONNECT) {
-+ reply = MG_SOCKS_CMD_NOT_SUPPORTED;
-+ } else if (atyp == MG_SOCKS_ADDR_IPV4) {
-+ addr_len = 4;
-+ if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
-+ snprintf(addr, sizeof(addr), "%d.%d.%d.%d:%d", p[4], p[5], p[6], p[7],
-+ p[8] << 8 | p[9]);
-+ mg_socks5_connect(c, addr);
-+ } else if (atyp == MG_SOCKS_ADDR_IPV6) {
-+ addr_len = 16;
-+ if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
-+ snprintf(addr, sizeof(addr), "[%x:%x:%x:%x:%x:%x:%x:%x]:%d",
-+ p[4] << 8 | p[5], p[6] << 8 | p[7], p[8] << 8 | p[9],
-+ p[10] << 8 | p[11], p[12] << 8 | p[13], p[14] << 8 | p[15],
-+ p[16] << 8 | p[17], p[18] << 8 | p[19], p[20] << 8 | p[21]);
-+ mg_socks5_connect(c, addr);
-+ } else if (atyp == MG_SOCKS_ADDR_DOMAIN) {
-+ addr_len = p[4] + 1;
-+ if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
-+ snprintf(addr, sizeof(addr), "%.*s:%d", p[4], p + 5,
-+ p[4 + addr_len] << 8 | p[4 + addr_len + 1]);
-+ mg_socks5_connect(c, addr);
-+ } else {
-+ reply = MG_SOCKS_ADDR_NOT_SUPPORTED;
-+ }
-+
-+ /*
-+ * Reply, https://www.ietf.org/rfc/rfc1928.txt paragraph 5
-+ *
-+ * +----+-----+-------+------+----------+----------+
-+ * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
-+ * +----+-----+-------+------+----------+----------+
-+ * | 1 | 1 | X'00' | 1 | Variable | 2 |
-+ * +----+-----+-------+------+----------+----------+
-+ */
-+ {
-+ unsigned char buf[] = {MG_SOCKS_VERSION, reply, 0};
-+ mg_send(c, buf, sizeof(buf));
-+ }
-+ mg_send(c, r->buf + 3, addr_len + 1 + 2);
-+
-+ mbuf_remove(r, 6 + addr_len); /* Remove request from the input stream */
-+ c->flags |= MG_SOCKS_CONNECT_DONE; /* Mark ourselves as connected */
-+}
-+
-+static void socks_handler(struct mg_connection *c, int ev, void *ev_data) {
-+ if (ev == MG_EV_RECV) {
-+ if (!(c->flags & MG_SOCKS_HANDSHAKE_DONE)) mg_socks5_handshake(c);
-+ if (c->flags & MG_SOCKS_HANDSHAKE_DONE &&
-+ !(c->flags & MG_SOCKS_CONNECT_DONE)) {
-+ mg_socks5_handle_request(c);
-+ }
-+ if (c->flags & MG_SOCKS_CONNECT_DONE) relay_data(c);
-+ } else if (ev == MG_EV_CLOSE) {
-+ disband(c);
-+ }
-+ (void) ev_data;
-+}
-+
-+void mg_set_protocol_socks(struct mg_connection *c) {
-+ c->proto_handler = socks_handler;
-+}
-+#endif
-+#ifdef MG_MODULE_LINES
- #line 1 "common/platforms/cc3200/cc3200_libc.c"
- #endif
- /*
-@@ -12011,6 +12761,7 @@ struct mg_connection *mg_sntp_get_time(s
-
- #if CS_PLATFORM == CS_P_CC3200
-
-+/* Amalgamated: #include "common/mg_mem.h" */
- #include <stdio.h>
- #include <string.h>
-
-@@ -12036,7 +12787,7 @@ int asprintf(char **strp, const char *fm
- va_list ap;
- int len;
-
-- *strp = malloc(BUFSIZ);
-+ *strp = MG_MALLOC(BUFSIZ);
- if (*strp == NULL) return -1;
-
- va_start(ap, fmt);
-@@ -12044,7 +12795,7 @@ int asprintf(char **strp, const char *fm
- va_end(ap);
-
- if (len > 0) {
-- *strp = realloc(*strp, len + 1);
-+ *strp = MG_REALLOC(*strp, len + 1);
- if (*strp == NULL) return -1;
- }
-
-@@ -12067,25 +12818,6 @@ time_t HOSTtime() {
-
- #endif /* __TI_COMPILER_VERSION__ */
-
--#ifndef __TI_COMPILER_VERSION__
--int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
--#else
--int gettimeofday(struct timeval *tp, void *tzp) {
--#endif
-- unsigned long long r1 = 0, r2;
-- /* Achieve two consecutive reads of the same value. */
-- do {
-- r2 = r1;
-- r1 = PRCMSlowClkCtrFastGet();
-- } while (r1 != r2);
-- /* This is a 32768 Hz counter. */
-- tp->tv_sec = (r1 >> 15);
-- /* 1/32768-th of a second is 30.517578125 microseconds, approx. 31,
-- * but we round down so it doesn't overflow at 32767 */
-- tp->tv_usec = (r1 & 0x7FFF) * 30;
-- return 0;
--}
--
- void fprint_str(FILE *fp, const char *str) {
- while (*str != '\0') {
- if (*str == '\n') MAP_UARTCharPut(CONSOLE_UART, '\r');
-@@ -12219,14 +12951,25 @@ void fs_slfs_set_new_file_size(const cha
- #if CS_PLATFORM == CS_P_CC3200
- #include <inc/hw_types.h>
- #endif
--#include <simplelink/include/simplelink.h>
--#include <simplelink/include/fs.h>
-
- /* Amalgamated: #include "common/cs_dbg.h" */
-+/* Amalgamated: #include "common/mg_mem.h" */
-+
-+#if SL_MAJOR_VERSION_NUM < 2
-+int slfs_open(const unsigned char *fname, uint32_t flags) {
-+ _i32 fh;
-+ _i32 r = sl_FsOpen(fname, flags, NULL /* token */, &fh);
-+ return (r < 0 ? r : fh);
-+}
-+#else /* SL_MAJOR_VERSION_NUM >= 2 */
-+int slfs_open(const unsigned char *fname, uint32_t flags) {
-+ return sl_FsOpen(fname, flags, NULL /* token */);
-+}
-+#endif
-
- /* From sl_fs.c */
--extern int set_errno(int e);
--static const char *drop_dir(const char *fname, bool *is_slfs);
-+int set_errno(int e);
-+const char *drop_dir(const char *fname, bool *is_slfs);
-
- /*
- * With SLFS, you have to pre-declare max file size. Yes. Really.
-@@ -12255,18 +12998,18 @@ static int sl_fs_to_errno(_i32 r) {
- switch (r) {
- case SL_FS_OK:
- return 0;
-- case SL_FS_FILE_NAME_EXIST:
-+ case SL_ERROR_FS_FILE_NAME_EXIST:
- return EEXIST;
-- case SL_FS_WRONG_FILE_NAME:
-+ case SL_ERROR_FS_WRONG_FILE_NAME:
- return EINVAL;
-- case SL_FS_ERR_NO_AVAILABLE_NV_INDEX:
-- case SL_FS_ERR_NO_AVAILABLE_BLOCKS:
-+ case SL_ERROR_FS_NO_AVAILABLE_NV_INDEX:
-+ case SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE:
- return ENOSPC;
-- case SL_FS_ERR_FAILED_TO_ALLOCATE_MEM:
-+ case SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM:
- return ENOMEM;
-- case SL_FS_ERR_FILE_NOT_EXISTS:
-+ case SL_ERROR_FS_FILE_NOT_EXISTS:
- return ENOENT;
-- case SL_FS_ERR_NOT_SUPPORTED:
-+ case SL_ERROR_FS_NOT_SUPPORTED:
- return ENOTSUP;
- }
- return ENXIO;
-@@ -12289,13 +13032,14 @@ int fs_slfs_open(const char *pathname, i
- _u32 am = 0;
- fi->size = (size_t) -1;
- int rw = (flags & 3);
-+ size_t new_size = FS_SLFS_MAX_FILE_SIZE;
- if (rw == O_RDONLY) {
- SlFsFileInfo_t sl_fi;
- _i32 r = sl_FsGetInfo((const _u8 *) pathname, 0, &sl_fi);
- if (r == SL_FS_OK) {
-- fi->size = sl_fi.FileLen;
-+ fi->size = SL_FI_FILE_SIZE(sl_fi);
- }
-- am = FS_MODE_OPEN_READ;
-+ am = SL_FS_READ;
- } else {
- if (!(flags & O_TRUNC) || (flags & O_APPEND)) {
- // FailFS files cannot be opened for append and will be truncated
-@@ -12303,31 +13047,30 @@ int fs_slfs_open(const char *pathname, i
- return set_errno(ENOTSUP);
- }
- if (flags & O_CREAT) {
-- size_t i, size = FS_SLFS_MAX_FILE_SIZE;
-+ size_t i;
- for (i = 0; i < MAX_OPEN_SLFS_FILES; i++) {
- if (s_sl_file_size_hints[i].name != NULL &&
- strcmp(s_sl_file_size_hints[i].name, pathname) == 0) {
-- size = s_sl_file_size_hints[i].size;
-- free(s_sl_file_size_hints[i].name);
-+ new_size = s_sl_file_size_hints[i].size;
-+ MG_FREE(s_sl_file_size_hints[i].name);
- s_sl_file_size_hints[i].name = NULL;
- break;
- }
- }
-- DBG(("creating %s with max size %d", pathname, (int) size));
-- am = FS_MODE_OPEN_CREATE(size, 0);
-+ am = FS_MODE_OPEN_CREATE(new_size, 0);
- } else {
-- am = FS_MODE_OPEN_WRITE;
-+ am = SL_FS_WRITE;
- }
- }
-- _i32 r = sl_FsOpen((_u8 *) pathname, am, NULL, &fi->fh);
-- DBG(("sl_FsOpen(%s, 0x%x) = %d, %d", pathname, (int) am, (int) r,
-- (int) fi->fh));
-- if (r == SL_FS_OK) {
-+ fi->fh = slfs_open((_u8 *) pathname, am);
-+ LOG(LL_DEBUG, ("sl_FsOpen(%s, 0x%x) sz %u = %d", pathname, (int) am,
-+ (unsigned int) new_size, (int) fi->fh));
-+ int r;
-+ if (fi->fh >= 0) {
- fi->pos = 0;
- r = fd;
- } else {
-- fi->fh = -1;
-- r = set_errno(sl_fs_to_errno(r));
-+ r = set_errno(sl_fs_to_errno(fi->fh));
- }
- return r;
- }
-@@ -12336,7 +13079,7 @@ int fs_slfs_close(int fd) {
- struct sl_fd_info *fi = &s_sl_fds[fd];
- if (fi->fh <= 0) return set_errno(EBADF);
- _i32 r = sl_FsClose(fi->fh, NULL, NULL, 0);
-- DBG(("sl_FsClose(%d) = %d", (int) fi->fh, (int) r));
-+ LOG(LL_DEBUG, ("sl_FsClose(%d) = %d", (int) fi->fh, (int) r));
- s_sl_fds[fd].fh = -1;
- return set_errno(sl_fs_to_errno(r));
- }
-@@ -12381,7 +13124,7 @@ int fs_slfs_stat(const char *pathname, s
- if (r == SL_FS_OK) {
- s->st_mode = S_IFREG | 0666;
- s->st_nlink = 1;
-- s->st_size = sl_fi.FileLen;
-+ s->st_size = SL_FI_FILE_SIZE(sl_fi);
- return 0;
- }
- return set_errno(sl_fs_to_errno(r));
-@@ -12449,6 +13192,32 @@ void fs_slfs_set_new_file_size(const cha
- #if MG_NET_IF == MG_NET_IF_SIMPLELINK && \
- (defined(MG_FS_SLFS) || defined(MG_FS_SPIFFS))
-
-+int set_errno(int e) {
-+ errno = e;
-+ return (e == 0 ? 0 : -1);
-+}
-+
-+const char *drop_dir(const char *fname, bool *is_slfs) {
-+ if (is_slfs != NULL) {
-+ *is_slfs = (strncmp(fname, "SL:", 3) == 0);
-+ if (*is_slfs) fname += 3;
-+ }
-+ /* Drop "./", if any */
-+ if (fname[0] == '.' && fname[1] == '/') {
-+ fname += 2;
-+ }
-+ /*
-+ * Drop / if it is the only one in the path.
-+ * This allows use of /pretend/directories but serves /file.txt as normal.
-+ */
-+ if (fname[0] == '/' && strchr(fname + 1, '/') == NULL) {
-+ fname++;
-+ }
-+ return fname;
-+}
-+
-+#if !defined(MG_FS_NO_VFS)
-+
- #include <errno.h>
- #include <stdbool.h>
- #include <stdio.h>
-@@ -12473,7 +13242,7 @@ void fs_slfs_set_new_file_size(const cha
- #define SPIFFS_FD_BASE 10
- #define SLFS_FD_BASE 100
-
--#ifndef MG_UART_CHAR_PUT
-+#if !defined(MG_UART_CHAR_PUT) && !defined(MG_UART_WRITE)
- #if CS_PLATFORM == CS_P_CC3200
- #include <inc/hw_types.h>
- #include <inc/hw_memmap.h>
-@@ -12482,34 +13251,10 @@ void fs_slfs_set_new_file_size(const cha
- #include <driverlib/uart.h>
- #define MG_UART_CHAR_PUT(fd, c) MAP_UARTCharPut(UARTA0_BASE, c);
- #else
--#define MG_UART_CHAR_PUT(fd, c)
-+#define MG_UART_WRITE(fd, buf, len)
- #endif /* CS_PLATFORM == CS_P_CC3200 */
- #endif /* !MG_UART_CHAR_PUT */
-
--int set_errno(int e) {
-- errno = e;
-- return (e == 0 ? 0 : -1);
--}
--
--static const char *drop_dir(const char *fname, bool *is_slfs) {
-- if (is_slfs != NULL) {
-- *is_slfs = (strncmp(fname, "SL:", 3) == 0);
-- if (*is_slfs) fname += 3;
-- }
-- /* Drop "./", if any */
-- if (fname[0] == '.' && fname[1] == '/') {
-- fname += 2;
-- }
-- /*
-- * Drop / if it is the only one in the path.
-- * This allows use of /pretend/directories but serves /file.txt as normal.
-- */
-- if (fname[0] == '/' && strchr(fname + 1, '/') == NULL) {
-- fname++;
-- }
-- return fname;
--}
--
- enum fd_type {
- FD_INVALID,
- FD_SYS,
-@@ -12711,7 +13456,6 @@ int write(int fd, const char *buf, unsig
- ssize_t _write(int fd, const void *buf, size_t count) {
- #endif
- int r = -1;
-- size_t i = 0;
- switch (fd_type(fd)) {
- case FD_INVALID:
- r = set_errno(EBADF);
-@@ -12721,11 +13465,18 @@ ssize_t _write(int fd, const void *buf,
- r = set_errno(EACCES);
- break;
- }
-- for (i = 0; i < count; i++) {
-- const char c = ((const char *) buf)[i];
-- if (c == '\n') MG_UART_CHAR_PUT(fd, '\r');
-- MG_UART_CHAR_PUT(fd, c);
-+#ifdef MG_UART_WRITE
-+ MG_UART_WRITE(fd, buf, count);
-+#elif defined(MG_UART_CHAR_PUT)
-+ {
-+ size_t i;
-+ for (i = 0; i < count; i++) {
-+ const char c = ((const char *) buf)[i];
-+ if (c == '\n') MG_UART_CHAR_PUT(fd, '\r');
-+ MG_UART_CHAR_PUT(fd, c);
-+ }
- }
-+#endif
- r = count;
- break;
- }
-@@ -12841,6 +13592,7 @@ int sl_fs_init(void) {
- return ret;
- }
-
-+#endif /* !defined(MG_FS_NO_VFS) */
- #endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && (defined(MG_FS_SLFS) || \
- defined(MG_FS_SPIFFS)) */
- #ifdef MG_MODULE_LINES
-@@ -12862,7 +13614,7 @@ const char *inet_ntop(int af, const void
- int res;
- struct in_addr *in = (struct in_addr *) src;
- if (af != AF_INET) {
-- errno = EAFNOSUPPORT;
-+ errno = ENOTSUP;
- return NULL;
- }
- res = snprintf(dst, size, "%lu.%lu.%lu.%lu", SL_IPV4_BYTE(in->s_addr, 0),
-@@ -12880,7 +13632,7 @@ int inet_pton(int af, const char *src, v
- uint32_t a0, a1, a2, a3;
- uint8_t *db = (uint8_t *) dst;
- if (af != AF_INET) {
-- errno = EAFNOSUPPORT;
-+ errno = ENOTSUP;
- return 0;
- }
- if (sscanf(src, "%lu.%lu.%lu.%lu", &a0, &a1, &a2, &a3) != 4) {
-@@ -12970,7 +13722,7 @@ extern "C" {
- #define MG_ENABLE_NET_IF_SIMPLELINK MG_NET_IF == MG_NET_IF_SIMPLELINK
- #endif
-
--extern struct mg_iface_vtable mg_simplelink_iface_vtable;
-+extern const struct mg_iface_vtable mg_simplelink_iface_vtable;
-
- #ifdef __cplusplus
- }
-@@ -12995,19 +13747,22 @@ extern struct mg_iface_vtable mg_simplel
- #define MG_TCP_RECV_BUFFER_SIZE 1024
- #define MG_UDP_RECV_BUFFER_SIZE 1500
-
--static sock_t mg_open_listening_socket(union socket_address *sa, int type,
-+static sock_t mg_open_listening_socket(struct mg_connection *nc,
-+ union socket_address *sa, int type,
- int proto);
-
--int sl_set_ssl_opts(struct mg_connection *nc);
--
- void mg_set_non_blocking_mode(sock_t sock) {
- SlSockNonblocking_t opt;
-+#if SL_MAJOR_VERSION_NUM < 2
- opt.NonblockingEnabled = 1;
-+#else
-+ opt.NonBlockingEnabled = 1;
-+#endif
- sl_SetSockOpt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, &opt, sizeof(opt));
- }
-
- static int mg_is_error(int n) {
-- return (n < 0 && n != SL_EALREADY && n != SL_EAGAIN);
-+ return (n < 0 && n != SL_ERROR_BSD_EALREADY && n != SL_ERROR_BSD_EAGAIN);
- }
-
- void mg_sl_if_connect_tcp(struct mg_connection *nc,
-@@ -13021,7 +13776,7 @@ void mg_sl_if_connect_tcp(struct mg_conn
- }
- mg_sock_set(nc, sock);
- #if MG_ENABLE_SSL
-- nc->err = sl_set_ssl_opts(nc);
-+ nc->err = sl_set_ssl_opts(sock, nc);
- if (nc->err != 0) goto out;
- #endif
- nc->err = sl_Connect(sock, &sa->sa, sizeof(sa->sin));
-@@ -13043,18 +13798,14 @@ void mg_sl_if_connect_udp(struct mg_conn
- int mg_sl_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
- int proto = 0;
- if (nc->flags & MG_F_SSL) proto = SL_SEC_SOCKET;
-- sock_t sock = mg_open_listening_socket(sa, SOCK_STREAM, proto);
-+ sock_t sock = mg_open_listening_socket(nc, sa, SOCK_STREAM, proto);
- if (sock < 0) return sock;
- mg_sock_set(nc, sock);
--#if MG_ENABLE_SSL
-- return sl_set_ssl_opts(nc);
--#else
- return 0;
--#endif
- }
-
- int mg_sl_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
-- sock_t sock = mg_open_listening_socket(sa, SOCK_DGRAM, 0);
-+ sock_t sock = mg_open_listening_socket(nc, sa, SOCK_DGRAM, 0);
- if (sock == INVALID_SOCKET) return (errno ? errno : 1);
- mg_sock_set(nc, sock);
- return 0;
-@@ -13110,22 +13861,27 @@ static int mg_accept_conn(struct mg_conn
- }
-
- /* 'sa' must be an initialized address to bind to */
--static sock_t mg_open_listening_socket(union socket_address *sa, int type,
-+static sock_t mg_open_listening_socket(struct mg_connection *nc,
-+ union socket_address *sa, int type,
- int proto) {
- int r;
- socklen_t sa_len =
- (sa->sa.sa_family == AF_INET) ? sizeof(sa->sin) : sizeof(sa->sin6);
- sock_t sock = sl_Socket(sa->sa.sa_family, type, proto);
- if (sock < 0) return sock;
-- if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) {
-- sl_Close(sock);
-- return r;
-+ if ((r = sl_Bind(sock, &sa->sa, sa_len)) < 0) goto clean;
-+ if (type != SOCK_DGRAM) {
-+#if MG_ENABLE_SSL
-+ if ((r = sl_set_ssl_opts(sock, nc)) < 0) goto clean;
-+#endif
-+ if ((r = sl_Listen(sock, SOMAXCONN)) < 0) goto clean;
- }
-- if (type != SOCK_DGRAM && (r = sl_Listen(sock, SOMAXCONN)) < 0) {
-+ mg_set_non_blocking_mode(sock);
-+clean:
-+ if (r < 0) {
- sl_Close(sock);
-- return r;
-+ sock = r;
- }
-- mg_set_non_blocking_mode(sock);
- return sock;
- }
-
-@@ -13144,7 +13900,6 @@ static void mg_write_to_socket(struct mg
- }
-
- if (n > 0) {
-- mbuf_remove(io, n);
- mg_if_sent_cb(nc, n);
- } else if (n < 0 && mg_is_error(n)) {
- /* Something went wrong, drop the connection. */
-@@ -13209,7 +13964,7 @@ void mg_mgr_handle_conn(struct mg_connec
- fd_flags, nc->flags, (int) nc->recv_mbuf.len, (int) nc->send_mbuf.len));
-
- if (nc->flags & MG_F_CONNECTING) {
-- if (nc->flags & MG_F_UDP || nc->err != SL_EALREADY) {
-+ if (nc->flags & MG_F_UDP || nc->err != SL_ERROR_BSD_EALREADY) {
- mg_if_connect_cb(nc, nc->err);
- } else {
- /* In SimpleLink, to get status of non-blocking connect() we need to wait
-@@ -13218,9 +13973,17 @@ void mg_mgr_handle_conn(struct mg_connec
- if (fd_flags & _MG_F_FD_CAN_WRITE) {
- nc->err = sl_Connect(nc->sock, &nc->sa.sa, sizeof(nc->sa.sin));
- DBG(("%p conn res=%d", nc, nc->err));
-- if (nc->err == SL_ESECSNOVERIFY ||
-+ if (nc->err == SL_ERROR_BSD_ESECSNOVERIFY ||
- /* TODO(rojer): Provide API to set the date for verification. */
-- nc->err == SL_ESECDATEERROR) {
-+ nc->err == SL_ERROR_BSD_ESECDATEERROR
-+#if SL_MAJOR_VERSION_NUM >= 2
-+ /* Per SWRU455, this error does not mean verification failed,
-+ * it only means that the cert used is not present in the trusted
-+ * root CA catalog. Which is perfectly fine. */
-+ ||
-+ nc->err == SL_ERROR_BSD_ESECUNKNOWNROOTCA
-+#endif
-+ ) {
- nc->err = 0;
- }
- if (nc->flags & MG_F_SSL && nc->err == 0) {
-@@ -13294,9 +14057,9 @@ time_t mg_sl_if_poll(struct mg_iface *if
- sock_t max_fd = INVALID_SOCKET;
- int num_fds, num_ev = 0, num_timers = 0;
-
-- SL_FD_ZERO(&read_set);
-- SL_FD_ZERO(&write_set);
-- SL_FD_ZERO(&err_set);
-+ SL_SOCKET_FD_ZERO(&read_set);
-+ SL_SOCKET_FD_ZERO(&write_set);
-+ SL_SOCKET_FD_ZERO(&err_set);
-
- /*
- * Note: it is ok to have connections with sock == INVALID_SOCKET in the list,
-@@ -13312,14 +14075,14 @@ time_t mg_sl_if_poll(struct mg_iface *if
- if (!(nc->flags & MG_F_WANT_WRITE) &&
- nc->recv_mbuf.len < nc->recv_mbuf_limit &&
- (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) {
-- SL_FD_SET(nc->sock, &read_set);
-+ SL_SOCKET_FD_SET(nc->sock, &read_set);
- if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock;
- }
-
- if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) ||
- (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) {
-- SL_FD_SET(nc->sock, &write_set);
-- SL_FD_SET(nc->sock, &err_set);
-+ SL_SOCKET_FD_SET(nc->sock, &write_set);
-+ SL_SOCKET_FD_SET(nc->sock, &err_set);
- if (max_fd == INVALID_SOCKET || nc->sock > max_fd) max_fd = nc->sock;
- }
- }
-@@ -13360,14 +14123,15 @@ time_t mg_sl_if_poll(struct mg_iface *if
- if (nc->sock != INVALID_SOCKET) {
- if (num_ev > 0) {
- fd_flags =
-- (SL_FD_ISSET(nc->sock, &read_set) &&
-+ (SL_SOCKET_FD_ISSET(nc->sock, &read_set) &&
- (!(nc->flags & MG_F_UDP) || nc->listener == NULL)
- ? _MG_F_FD_CAN_READ
- : 0) |
-- (SL_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) |
-- (SL_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0);
-+ (SL_SOCKET_FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE
-+ : 0) |
-+ (SL_SOCKET_FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0);
- }
-- /* SimpleLink does not report UDP sockets as writeable. */
-+ /* SimpleLink does not report UDP sockets as writable. */
- if (nc->flags & MG_F_UDP && nc->send_mbuf.len > 0) {
- fd_flags |= _MG_F_FD_CAN_WRITE;
- }
-@@ -13390,7 +14154,7 @@ time_t mg_sl_if_poll(struct mg_iface *if
- void mg_sl_if_get_conn_addr(struct mg_connection *nc, int remote,
- union socket_address *sa) {
- /* SimpleLink does not provide a way to get socket's peer address after
-- * accept or connect. Address hould have been preserved in the connection,
-+ * accept or connect. Address should have been preserved in the connection,
- * so we do our best here by using it. */
- if (remote) memcpy(sa, &nc->sa, sizeof(*sa));
- }
-@@ -13442,9 +14206,9 @@ void sl_restart_cb(struct mg_mgr *mgr) {
- }
- /* clang-format on */
-
--struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_simplelink_iface_vtable = MG_SL_IFACE_VTABLE;
- #if MG_NET_IF == MG_NET_IF_SIMPLELINK
--struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_default_iface_vtable = MG_SL_IFACE_VTABLE;
- #endif
-
- #endif /* MG_ENABLE_NET_IF_SIMPLELINK */
-@@ -13458,6 +14222,15 @@ struct mg_iface_vtable mg_default_iface_
-
- #if MG_ENABLE_SSL && MG_SSL_IF == MG_SSL_IF_SIMPLELINK
-
-+/* Amalgamated: #include "common/mg_mem.h" */
-+
-+#ifndef MG_SSL_IF_SIMPLELINK_SLFS_PREFIX
-+#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "SL:"
-+#endif
-+
-+#define MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN \
-+ (sizeof(MG_SSL_IF_SIMPLELINK_SLFS_PREFIX) - 1)
-+
- struct mg_ssl_if_ctx {
- char *ssl_cert;
- char *ssl_key;
-@@ -13491,12 +14264,18 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- if (params->ca_cert != NULL && strcmp(params->ca_cert, "*") != 0) {
- ctx->ssl_ca_cert = strdup(params->ca_cert);
- }
-+ /* TODO(rojer): cipher_suites. */
- if (params->server_name != NULL) {
- ctx->ssl_server_name = strdup(params->server_name);
- }
- return MG_SSL_OK;
- }
-
-+void mg_ssl_if_conn_close_notify(struct mg_connection *nc) {
-+ /* Nothing to do */
-+ (void) nc;
-+}
-+
- void mg_ssl_if_conn_free(struct mg_connection *nc) {
- struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
- if (ctx == NULL) return;
-@@ -13516,7 +14295,8 @@ bool pem_to_der(const char *pem_file, co
- pf = fopen(pem_file, "r");
- if (pf == NULL) goto clean;
- remove(der_file);
-- fs_slfs_set_new_file_size(der_file + 3, 2048);
-+ fs_slfs_set_new_file_size(der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
-+ 2048);
- df = fopen(der_file, "w");
- if (df == NULL) goto clean;
- while (1) {
-@@ -13558,8 +14338,8 @@ static char *sl_pem2der(const char *pem_
- }
- char *der_file = NULL;
- /* DER file must be located on SLFS, add prefix. */
-- int l = mg_asprintf(&der_file, 0, "SL:%.*s.der", (int) (pem_ext - pem_file),
-- pem_file);
-+ int l = mg_asprintf(&der_file, 0, MG_SSL_IF_SIMPLELINK_SLFS_PREFIX "%.*s.der",
-+ (int) (pem_ext - pem_file), pem_file);
- if (der_file == NULL) return NULL;
- bool result = false;
- cs_stat_t st;
-@@ -13572,9 +14352,10 @@ static char *sl_pem2der(const char *pem_
- }
- if (result) {
- /* Strip the SL: prefix we added since NWP does not expect it. */
-- memmove(der_file, der_file + 3, l - 2 /* including \0 */);
-+ memmove(der_file, der_file + MG_SSL_IF_SIMPLELINK_SLFS_PREFIX_LEN,
-+ l - 2 /* including \0 */);
- } else {
-- free(der_file);
-+ MG_FREE(der_file);
- der_file = NULL;
- }
- return der_file;
-@@ -13585,9 +14366,9 @@ static char *sl_pem2der(const char *pem_
- }
- #endif
-
--int sl_set_ssl_opts(struct mg_connection *nc) {
-+int sl_set_ssl_opts(int sock, struct mg_connection *nc) {
- int err;
-- struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
-+ const struct mg_ssl_if_ctx *ctx = (struct mg_ssl_if_ctx *) nc->ssl_if_data;
- DBG(("%p ssl ctx: %p", nc, ctx));
-
- if (ctx != NULL) {
-@@ -13599,45 +14380,46 @@ int sl_set_ssl_opts(struct mg_connection
- char *ssl_cert = sl_pem2der(ctx->ssl_cert);
- char *ssl_key = sl_pem2der(ctx->ssl_key);
- if (ssl_cert != NULL && ssl_key != NULL) {
-- err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET,
-+ err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
- SL_SO_SECURE_FILES_CERTIFICATE_FILE_NAME, ssl_cert,
- strlen(ssl_cert));
- LOG(LL_INFO, ("CERTIFICATE_FILE_NAME %s -> %d", ssl_cert, err));
-- err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET,
-+ err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
- SL_SO_SECURE_FILES_PRIVATE_KEY_FILE_NAME, ssl_key,
- strlen(ssl_key));
- LOG(LL_INFO, ("PRIVATE_KEY_FILE_NAME %s -> %d", ssl_key, err));
- } else {
- err = -1;
- }
-- free(ssl_cert);
-- free(ssl_key);
-+ MG_FREE(ssl_cert);
-+ MG_FREE(ssl_key);
- if (err != 0) return err;
- }
- if (ctx->ssl_ca_cert != NULL) {
- if (ctx->ssl_ca_cert[0] != '\0') {
- char *ssl_ca_cert = sl_pem2der(ctx->ssl_ca_cert);
- if (ssl_ca_cert != NULL) {
-- err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET,
-+ err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
- SL_SO_SECURE_FILES_CA_FILE_NAME, ssl_ca_cert,
- strlen(ssl_ca_cert));
- LOG(LL_INFO, ("CA_FILE_NAME %s -> %d", ssl_ca_cert, err));
- } else {
- err = -1;
- }
-- free(ssl_ca_cert);
-+ MG_FREE(ssl_ca_cert);
- if (err != 0) return err;
- }
- }
- if (ctx->ssl_server_name != NULL) {
-- err = sl_SetSockOpt(nc->sock, SL_SOL_SOCKET,
-- SO_SECURE_DOMAIN_NAME_VERIFICATION,
-+ err = sl_SetSockOpt(sock, SL_SOL_SOCKET,
-+ SL_SO_SECURE_DOMAIN_NAME_VERIFICATION,
- ctx->ssl_server_name, strlen(ctx->ssl_server_name));
- DBG(("DOMAIN_NAME_VERIFICATION %s -> %d", ctx->ssl_server_name, err));
- /* Domain name verificationw as added in a NWP service pack, older
-- * versions return SL_ENOPROTOOPT. There isn't much we can do about it,
-+ * versions return SL_ERROR_BSD_ENOPROTOOPT. There isn't much we can do
-+ * about it,
- * so we ignore the error. */
-- if (err != 0 && err != SL_ENOPROTOOPT) return err;
-+ if (err != 0 && err != SL_ERROR_BSD_ENOPROTOOPT) return err;
- }
- }
- return 0;
-@@ -13663,10 +14445,11 @@ int sl_set_ssl_opts(struct mg_connection
-
- #include <stdint.h>
-
--extern struct mg_iface_vtable mg_lwip_iface_vtable;
-+extern const struct mg_iface_vtable mg_lwip_iface_vtable;
-
- struct mg_lwip_conn_state {
- struct mg_connection *nc;
-+ struct mg_connection *lc;
- union {
- struct tcp_pcb *tcp;
- struct udp_pcb *udp;
-@@ -13677,14 +14460,18 @@ struct mg_lwip_conn_state {
- size_t rx_offset; /* Offset within the first pbuf (if partially consumed) */
- /* Last SSL write size, for retries. */
- int last_ssl_write_size;
-+ /* Whether MG_SIG_RECV is already pending for this connection */
-+ int recv_pending : 1;
-+ /* Whether the connection is about to close, just `rx_chain` needs to drain */
-+ int draining_rx_chain : 1;
- };
-
- enum mg_sig_type {
- MG_SIG_CONNECT_RESULT = 1,
- MG_SIG_RECV = 2,
-- MG_SIG_SENT_CB = 3,
-- MG_SIG_CLOSE_CONN = 4,
-- MG_SIG_TOMBSTONE = 5,
-+ MG_SIG_CLOSE_CONN = 3,
-+ MG_SIG_TOMBSTONE = 4,
-+ MG_SIG_ACCEPT = 5,
- };
-
- void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc);
-@@ -13705,14 +14492,34 @@ void mg_lwip_mgr_schedule_poll(struct mg
-
- #if MG_ENABLE_NET_IF_LWIP_LOW_LEVEL
-
-+/* Amalgamated: #include "common/mg_mem.h" */
-+
-+#include <lwip/init.h>
- #include <lwip/pbuf.h>
- #include <lwip/tcp.h>
-+#include <lwip/tcpip.h>
-+#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
-+#include <lwip/priv/tcp_priv.h> /* For tcp_seg */
-+#else
- #include <lwip/tcp_impl.h>
-+#endif
- #include <lwip/udp.h>
-
- /* Amalgamated: #include "common/cs_dbg.h" */
-
- /*
-+ * Newest versions of LWIP have ip_2_ip4, older have ipX_2_ip,
-+ * even older have nothing.
-+ */
-+#ifndef ip_2_ip4
-+#ifdef ipX_2_ip
-+#define ip_2_ip4(addr) ipX_2_ip(addr)
-+#else
-+#define ip_2_ip4(addr) (addr)
-+#endif
-+#endif
-+
-+/*
- * Depending on whether Mongoose is compiled with ipv6 support, use right
- * lwip functions
- */
-@@ -13729,16 +14536,12 @@ void mg_lwip_mgr_schedule_poll(struct mg
- #define TCP_BIND tcp_bind
- #define UDP_BIND udp_bind
- #define IPADDR_NTOA ipaddr_ntoa
--#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = GET_IPV4(src)
-+#define SET_ADDR(dst, src) (dst)->sin.sin_addr.s_addr = ip_2_ip4(src)->addr
- #endif
-
--/*
-- * If lwip is compiled with ipv6 support, then API changes even for ipv4
-- */
--#if !defined(LWIP_IPV6) || !LWIP_IPV6
--#define GET_IPV4(ipX_addr) ((ipX_addr)->addr)
--#else
--#define GET_IPV4(ipX_addr) ((ipX_addr)->ip4.addr)
-+#if NO_SYS
-+#define tcpip_callback(fn, arg) (fn)(arg)
-+typedef void (*tcpip_callback_fn)(void *arg);
- #endif
-
- void mg_lwip_ssl_do_hs(struct mg_connection *nc);
-@@ -13751,6 +14554,16 @@ void mg_lwip_if_add_conn(struct mg_conne
- void mg_lwip_if_remove_conn(struct mg_connection *nc);
- time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms);
-
-+#if defined(RTOS_SDK) || defined(ESP_PLATFORM)
-+extern void mgos_lock();
-+extern void mgos_unlock();
-+#else
-+#define mgos_lock()
-+#define mgos_unlock()
-+#endif
-+
-+static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p);
-+
- #if LWIP_TCP_KEEPALIVE
- void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
- int interval, int count) {
-@@ -13774,7 +14587,7 @@ void mg_lwip_set_keepalive_params(struct
-
- static err_t mg_lwip_tcp_conn_cb(void *arg, struct tcp_pcb *tpcb, err_t err) {
- struct mg_connection *nc = (struct mg_connection *) arg;
-- DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(&tpcb->remote_ip),
-+ DBG(("%p connect to %s:%u = %d", nc, IPADDR_NTOA(ipX_2_ip(&tpcb->remote_ip)),
- tpcb->remote_port, err));
- if (nc == NULL) {
- tcp_abort(tpcb);
-@@ -13808,8 +14621,17 @@ static err_t mg_lwip_tcp_recv_cb(void *a
- struct mg_connection *nc = (struct mg_connection *) arg;
- DBG(("%p %p %u %d", nc, tpcb, (p != NULL ? p->tot_len : 0), err));
- if (p == NULL) {
-- if (nc != NULL) {
-- mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
-+ if (nc != NULL && !(nc->flags & MG_F_CLOSE_IMMEDIATELY)) {
-+ struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-+ if (cs->rx_chain != NULL) {
-+ /*
-+ * rx_chain still contains non-consumed data, don't close the
-+ * connection
-+ */
-+ cs->draining_rx_chain = 1;
-+ } else {
-+ mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
-+ }
- } else {
- /* Tombstoned connection, do nothing. */
- }
-@@ -13827,30 +14649,28 @@ static err_t mg_lwip_tcp_recv_cb(void *a
- struct pbuf *q = p->next;
- for (; q != NULL; q = q->next) pbuf_ref(q);
- }
-+ mgos_lock();
- if (cs->rx_chain == NULL) {
-- cs->rx_chain = p;
- cs->rx_offset = 0;
-- } else {
-- if (pbuf_clen(cs->rx_chain) >= 4) {
-- /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX
-- * will be completely blocked. We already have at least 4 in the chain,
-- * this one is, so we have to make a copy and release this one. */
-- struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
-- if (np != NULL) {
-- pbuf_copy(np, p);
-- pbuf_free(p);
-- p = np;
-- }
-+ } else if (pbuf_clen(cs->rx_chain) >= 4) {
-+ /* ESP SDK has a limited pool of 5 pbufs. We must not hog them all or RX
-+ * will be completely blocked. We already have at least 4 in the chain,
-+ * this one is, so we have to make a copy and release this one. */
-+ struct pbuf *np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
-+ if (np != NULL) {
-+ pbuf_copy(np, p);
-+ pbuf_free(p);
-+ p = np;
- }
-- pbuf_chain(cs->rx_chain, p);
- }
-- mg_lwip_post_signal(MG_SIG_RECV, nc);
-+ mgos_unlock();
-+ mg_lwip_recv_common(nc, p);
- return ERR_OK;
- }
-
--static void mg_lwip_handle_recv(struct mg_connection *nc) {
-+static void mg_lwip_consume_rx_chain_tcp(struct mg_connection *nc) {
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
--
-+ if (cs->rx_chain == NULL) return;
- #if MG_ENABLE_SSL
- if (nc->flags & MG_F_SSL) {
- if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
-@@ -13861,24 +14681,36 @@ static void mg_lwip_handle_recv(struct m
- return;
- }
- #endif
--
-- while (cs->rx_chain != NULL) {
-+ mgos_lock();
-+ while (cs->rx_chain != NULL && nc->recv_mbuf.len < nc->recv_mbuf_limit) {
- struct pbuf *seg = cs->rx_chain;
-- size_t len = (seg->len - cs->rx_offset);
-- char *data = (char *) malloc(len);
-+
-+ size_t seg_len = (seg->len - cs->rx_offset);
-+ size_t buf_avail = (nc->recv_mbuf_limit - nc->recv_mbuf.len);
-+ size_t len = MIN(seg_len, buf_avail);
-+
-+ char *data = (char *) MG_MALLOC(len);
- if (data == NULL) {
-+ mgos_unlock();
- DBG(("OOM"));
- return;
- }
- pbuf_copy_partial(seg, data, len, cs->rx_offset);
-- mg_if_recv_tcp_cb(nc, data, len, 1 /* own */);
- cs->rx_offset += len;
- if (cs->rx_offset == cs->rx_chain->len) {
- cs->rx_chain = pbuf_dechain(cs->rx_chain);
- pbuf_free(seg);
- cs->rx_offset = 0;
- }
-+ mgos_unlock();
-+ mg_if_recv_tcp_cb(nc, data, len, 1 /* own */);
-+ mgos_lock();
- }
-+ mgos_unlock();
-+}
-+
-+static void mg_lwip_handle_recv_tcp(struct mg_connection *nc) {
-+ mg_lwip_consume_rx_chain_tcp(nc);
-
- if (nc->send_mbuf.len > 0) {
- mg_lwip_mgr_schedule_poll(nc->mgr);
-@@ -13888,20 +14720,26 @@ static void mg_lwip_handle_recv(struct m
- static err_t mg_lwip_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb,
- u16_t num_sent) {
- struct mg_connection *nc = (struct mg_connection *) arg;
-- DBG(("%p %p %u", nc, tpcb, num_sent));
-- if (nc == NULL) {
-- tcp_abort(tpcb);
-- return ERR_ABRT;
-+ DBG(("%p %p %u %p %p", nc, tpcb, num_sent, tpcb->unsent, tpcb->unacked));
-+ if (nc == NULL) return ERR_OK;
-+ if ((nc->flags & MG_F_SEND_AND_CLOSE) && !(nc->flags & MG_F_WANT_WRITE) &&
-+ nc->send_mbuf.len == 0 && tpcb->unsent == NULL && tpcb->unacked == NULL) {
-+ mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
- }
-- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-- cs->num_sent += num_sent;
--
-- mg_lwip_post_signal(MG_SIG_SENT_CB, nc);
- return ERR_OK;
- }
-
--void mg_lwip_if_connect_tcp(struct mg_connection *nc,
-- const union socket_address *sa) {
-+struct mg_lwip_if_connect_tcp_ctx {
-+ struct mg_connection *nc;
-+ const union socket_address *sa;
-+};
-+
-+static void mg_lwip_if_connect_tcp_tcpip(void *arg) {
-+ struct mg_lwip_if_connect_tcp_ctx *ctx =
-+ (struct mg_lwip_if_connect_tcp_ctx *) arg;
-+ struct mg_connection *nc = ctx->nc;
-+ const union socket_address *sa = ctx->sa;
-+
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- struct tcp_pcb *tpcb = TCP_NEW();
- cs->pcb.tcp = tpcb;
-@@ -13925,11 +14763,17 @@ void mg_lwip_if_connect_tcp(struct mg_co
- }
- }
-
-+void mg_lwip_if_connect_tcp(struct mg_connection *nc,
-+ const union socket_address *sa) {
-+ struct mg_lwip_if_connect_tcp_ctx ctx = {.nc = nc, .sa = sa};
-+ tcpip_callback(mg_lwip_if_connect_tcp_tcpip, &ctx);
-+}
-+
- /*
- * Lwip included in the SDKs for nRF5x chips has different type for the
- * callback of `udp_recv()`
- */
--#if CS_PLATFORM == CS_P_NRF51 || CS_PLATFORM == CS_P_NRF52
-+#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
- static void mg_lwip_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p,
- const ip_addr_t *addr, u16_t port)
- #else
-@@ -13938,24 +14782,70 @@ static void mg_lwip_udp_recv_cb(void *ar
- #endif
- {
- struct mg_connection *nc = (struct mg_connection *) arg;
-- size_t len = p->len;
-- char *data = (char *) malloc(len);
-- union socket_address sa;
-- (void) pcb;
-- DBG(("%p %s:%u %u", nc, IPADDR_NTOA(addr), port, p->len));
-- if (data == NULL) {
-- DBG(("OOM"));
-+ DBG(("%p %s:%u %p %u %u", nc, IPADDR_NTOA(addr), port, p, p->ref, p->len));
-+ /* Put address in a separate pbuf and tack it onto the packet. */
-+ struct pbuf *sap =
-+ pbuf_alloc(PBUF_RAW, sizeof(union socket_address), PBUF_RAM);
-+ if (sap == NULL) {
- pbuf_free(p);
- return;
- }
-- sa.sin.sin_addr.s_addr = addr->addr;
-- sa.sin.sin_port = htons(port);
-- pbuf_copy_partial(p, data, len, 0);
-- pbuf_free(p);
-- mg_if_recv_udp_cb(nc, data, len, &sa, sizeof(sa.sin));
-+ union socket_address *sa = (union socket_address *) sap->payload;
-+#if ((LWIP_VERSION_MAJOR << 8) | LWIP_VERSION_MINOR) >= 0x0105
-+ sa->sin.sin_addr.s_addr = ip_2_ip4(addr)->addr;
-+#else
-+ sa->sin.sin_addr.s_addr = addr->addr;
-+#endif
-+ sa->sin.sin_port = htons(port);
-+ /* Logic in the recv handler requires that there be exactly one data pbuf. */
-+ p = pbuf_coalesce(p, PBUF_RAW);
-+ pbuf_chain(sap, p);
-+ mg_lwip_recv_common(nc, sap);
-+ (void) pcb;
- }
-
--void mg_lwip_if_connect_udp(struct mg_connection *nc) {
-+static void mg_lwip_recv_common(struct mg_connection *nc, struct pbuf *p) {
-+ struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-+ mgos_lock();
-+ if (cs->rx_chain == NULL) {
-+ cs->rx_chain = p;
-+ } else {
-+ pbuf_chain(cs->rx_chain, p);
-+ }
-+ if (!cs->recv_pending) {
-+ cs->recv_pending = 1;
-+ mg_lwip_post_signal(MG_SIG_RECV, nc);
-+ }
-+ mgos_unlock();
-+}
-+
-+static void mg_lwip_handle_recv_udp(struct mg_connection *nc) {
-+ struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-+ /*
-+ * For UDP, RX chain consists of interleaved address and packet bufs:
-+ * Address pbuf followed by exactly one data pbuf (recv_cb took care of that).
-+ */
-+ while (cs->rx_chain != NULL) {
-+ struct pbuf *sap = cs->rx_chain;
-+ struct pbuf *p = sap->next;
-+ cs->rx_chain = pbuf_dechain(p);
-+ size_t data_len = p->len;
-+ char *data = (char *) MG_MALLOC(data_len);
-+ if (data != NULL) {
-+ pbuf_copy_partial(p, data, data_len, 0);
-+ pbuf_free(p);
-+ mg_if_recv_udp_cb(nc, data, data_len,
-+ (union socket_address *) sap->payload, sap->len);
-+ pbuf_free(sap);
-+ } else {
-+ pbuf_free(p);
-+ pbuf_free(sap);
-+ }
-+ }
-+}
-+
-+static void mg_lwip_if_connect_udp_tcpip(void *arg) {
-+ struct mg_connection *nc = (struct mg_connection *) arg;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- struct udp_pcb *upcb = udp_new();
- cs->err = UDP_BIND(upcb, IP_ADDR_ANY, 0 /* any port */);
-@@ -13969,6 +14859,10 @@ void mg_lwip_if_connect_udp(struct mg_co
- mg_lwip_post_signal(MG_SIG_CONNECT_RESULT, nc);
- }
-
-+void mg_lwip_if_connect_udp(struct mg_connection *nc) {
-+ tcpip_callback(mg_lwip_if_connect_udp_tcpip, nc);
-+}
-+
- void mg_lwip_accept_conn(struct mg_connection *nc, struct tcp_pcb *tpcb) {
- union socket_address sa;
- SET_ADDR(&sa, &tpcb->remote_ip);
-@@ -13976,18 +14870,52 @@ void mg_lwip_accept_conn(struct mg_conne
- mg_if_accept_tcp_cb(nc, &sa, sizeof(sa.sin));
- }
-
-+static void tcp_close_tcpip(void *arg) {
-+ tcp_close((struct tcp_pcb *) arg);
-+}
-+
-+void mg_lwip_handle_accept(struct mg_connection *nc) {
-+ struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-+ if (cs->pcb.tcp == NULL) return;
-+#if MG_ENABLE_SSL
-+ if (cs->lc->flags & MG_F_SSL) {
-+ if (mg_ssl_if_conn_accept(nc, cs->lc) != MG_SSL_OK) {
-+ LOG(LL_ERROR, ("SSL error"));
-+ tcpip_callback(tcp_close_tcpip, cs->pcb.tcp);
-+ }
-+ } else
-+#endif
-+ {
-+ mg_lwip_accept_conn(nc, cs->pcb.tcp);
-+ }
-+}
-+
- static err_t mg_lwip_accept_cb(void *arg, struct tcp_pcb *newtpcb, err_t err) {
-- struct mg_connection *lc = (struct mg_connection *) arg;
-- (void) err;
-- DBG(("%p conn %p from %s:%u", lc, newtpcb, IPADDR_NTOA(&newtpcb->remote_ip),
-- newtpcb->remote_port));
-- struct mg_connection *nc = mg_if_accept_new_conn(lc);
-+ struct mg_connection *lc = (struct mg_connection *) arg, *nc;
-+ struct mg_lwip_conn_state *lcs, *cs;
-+ struct tcp_pcb_listen *lpcb;
-+ LOG(LL_DEBUG,
-+ ("%p conn %p from %s:%u", lc, newtpcb,
-+ IPADDR_NTOA(ipX_2_ip(&newtpcb->remote_ip)), newtpcb->remote_port));
-+ if (lc == NULL) {
-+ tcp_abort(newtpcb);
-+ return ERR_ABRT;
-+ }
-+ lcs = (struct mg_lwip_conn_state *) lc->sock;
-+ lpcb = (struct tcp_pcb_listen *) lcs->pcb.tcp;
-+#if TCP_LISTEN_BACKLOG
-+ tcp_accepted(lpcb);
-+#endif
-+ nc = mg_if_accept_new_conn(lc);
- if (nc == NULL) {
- tcp_abort(newtpcb);
- return ERR_ABRT;
- }
-- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-+ cs = (struct mg_lwip_conn_state *) nc->sock;
-+ cs->lc = lc;
- cs->pcb.tcp = newtpcb;
-+ /* We need to set up callbacks before returning because data may start
-+ * arriving immediately. */
- tcp_arg(newtpcb, nc);
- tcp_err(newtpcb, mg_lwip_tcp_error_cb);
- tcp_sent(newtpcb, mg_lwip_tcp_sent_cb);
-@@ -13995,21 +14923,22 @@ static err_t mg_lwip_accept_cb(void *arg
- #if LWIP_TCP_KEEPALIVE
- mg_lwip_set_keepalive_params(nc, 60, 10, 6);
- #endif
--#if MG_ENABLE_SSL
-- if (lc->flags & MG_F_SSL) {
-- if (mg_ssl_if_conn_accept(nc, lc) != MG_SSL_OK) {
-- LOG(LL_ERROR, ("SSL error"));
-- tcp_close(newtpcb);
-- }
-- } else
--#endif
-- {
-- mg_lwip_accept_conn(nc, newtpcb);
-- }
-+ mg_lwip_post_signal(MG_SIG_ACCEPT, nc);
-+ (void) err;
-+ (void) lpcb;
- return ERR_OK;
- }
-
--int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
-+struct mg_lwip_if_listen_ctx {
-+ struct mg_connection *nc;
-+ union socket_address *sa;
-+ int ret;
-+};
-+
-+static void mg_lwip_if_listen_tcp_tcpip(void *arg) {
-+ struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
-+ struct mg_connection *nc = ctx->nc;
-+ union socket_address *sa = ctx->sa;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- struct tcp_pcb *tpcb = TCP_NEW();
- ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
-@@ -14018,16 +14947,26 @@ int mg_lwip_if_listen_tcp(struct mg_conn
- DBG(("%p tcp_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
- if (cs->err != ERR_OK) {
- tcp_close(tpcb);
-- return -1;
-+ ctx->ret = -1;
-+ return;
- }
- tcp_arg(tpcb, nc);
- tpcb = tcp_listen(tpcb);
- cs->pcb.tcp = tpcb;
- tcp_accept(tpcb, mg_lwip_accept_cb);
-- return 0;
-+ ctx->ret = 0;
- }
-
--int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
-+int mg_lwip_if_listen_tcp(struct mg_connection *nc, union socket_address *sa) {
-+ struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
-+ tcpip_callback(mg_lwip_if_listen_tcp_tcpip, &ctx);
-+ return ctx.ret;
-+}
-+
-+static void mg_lwip_if_listen_udp_tcpip(void *arg) {
-+ struct mg_lwip_if_listen_ctx *ctx = (struct mg_lwip_if_listen_ctx *) arg;
-+ struct mg_connection *nc = ctx->nc;
-+ union socket_address *sa = ctx->sa;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- struct udp_pcb *upcb = udp_new();
- ip_addr_t *ip = (ip_addr_t *) &sa->sin.sin_addr.s_addr;
-@@ -14036,24 +14975,47 @@ int mg_lwip_if_listen_udp(struct mg_conn
- DBG(("%p udb_bind(%s:%u) = %d", nc, IPADDR_NTOA(ip), port, cs->err));
- if (cs->err != ERR_OK) {
- udp_remove(upcb);
-- return -1;
-+ ctx->ret = -1;
-+ } else {
-+ udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
-+ cs->pcb.udp = upcb;
-+ ctx->ret = 0;
- }
-- udp_recv(upcb, mg_lwip_udp_recv_cb, nc);
-- cs->pcb.udp = upcb;
-- return 0;
- }
-
--int mg_lwip_tcp_write(struct mg_connection *nc, const void *data,
-- uint16_t len) {
-+int mg_lwip_if_listen_udp(struct mg_connection *nc, union socket_address *sa) {
-+ struct mg_lwip_if_listen_ctx ctx = {.nc = nc, .sa = sa};
-+ tcpip_callback(mg_lwip_if_listen_udp_tcpip, &ctx);
-+ return ctx.ret;
-+}
-+
-+struct mg_lwip_tcp_write_ctx {
-+ struct mg_connection *nc;
-+ const void *data;
-+ uint16_t len;
-+ int ret;
-+};
-+
-+static void tcp_output_tcpip(void *arg) {
-+ tcp_output((struct tcp_pcb *) arg);
-+}
-+
-+static void mg_lwip_tcp_write_tcpip(void *arg) {
-+ struct mg_lwip_tcp_write_ctx *ctx = (struct mg_lwip_tcp_write_ctx *) arg;
-+ struct mg_connection *nc = ctx->nc;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- struct tcp_pcb *tpcb = cs->pcb.tcp;
-- len = MIN(tpcb->mss, MIN(len, tpcb->snd_buf));
-+ size_t len = MIN(tpcb->mss, MIN(ctx->len, tpcb->snd_buf));
-+ size_t unsent, unacked;
- if (len == 0) {
-- DBG(("%p no buf avail %u %u %u %p %p", tpcb, tpcb->acked, tpcb->snd_buf,
-- tpcb->snd_queuelen, tpcb->unsent, tpcb->unacked));
-- tcp_output(tpcb);
-- return 0;
-+ DBG(("%p no buf avail %u %u %p %p", tpcb, tpcb->snd_buf, tpcb->snd_queuelen,
-+ tpcb->unsent, tpcb->unacked));
-+ tcpip_callback(tcp_output_tcpip, tpcb);
-+ ctx->ret = 0;
-+ return;
- }
-+ unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
-+ unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
- /*
- * On ESP8266 we only allow one TCP segment in flight at any given time.
- * This may increase latency and reduce efficiency of tcp windowing,
-@@ -14061,75 +15023,124 @@ int mg_lwip_tcp_write(struct mg_connecti
- * reduce footprint.
- */
- #if CS_PLATFORM == CS_P_ESP8266
-- if (tpcb->unacked != NULL) {
-- return 0;
-- }
-- if (tpcb->unsent != NULL) {
-- len = MIN(len, (TCP_MSS - tpcb->unsent->len));
-+ if (unacked > 0) {
-+ ctx->ret = 0;
-+ return;
- }
-+ len = MIN(len, (TCP_MSS - unsent));
- #endif
-- err_t err = tcp_write(tpcb, data, len, TCP_WRITE_FLAG_COPY);
-- DBG(("%p tcp_write %u = %d", tpcb, len, err));
-- if (err != ERR_OK) {
-+ cs->err = tcp_write(tpcb, ctx->data, len, TCP_WRITE_FLAG_COPY);
-+ unsent = (tpcb->unsent != NULL ? tpcb->unsent->len : 0);
-+ unacked = (tpcb->unacked != NULL ? tpcb->unacked->len : 0);
-+ DBG(("%p tcp_write %u = %d, %u %u", tpcb, len, cs->err, unsent, unacked));
-+ if (cs->err != ERR_OK) {
- /*
- * We ignore ERR_MEM because memory will be freed up when the data is sent
- * and we'll retry.
- */
-- return (err == ERR_MEM ? 0 : -1);
-+ ctx->ret = (cs->err == ERR_MEM ? 0 : -1);
-+ return;
- }
-- return len;
-+ ctx->ret = len;
- }
-
--static void mg_lwip_send_more(struct mg_connection *nc) {
-+static int mg_lwip_tcp_write(struct mg_connection *nc, const void *data,
-+ uint16_t len) {
-+ struct mg_lwip_tcp_write_ctx ctx = {.nc = nc, .data = data, .len = len};
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-- if (nc->sock == INVALID_SOCKET || cs->pcb.tcp == NULL) {
-- DBG(("%p invalid socket", nc));
-- return;
-- }
-- int num_written = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len);
-- DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_written));
-- if (num_written == 0) return;
-- if (num_written < 0) {
-- mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
-+ struct tcp_pcb *tpcb = cs->pcb.tcp;
-+ if (tpcb == NULL) {
-+ return -1;
- }
-- mbuf_remove(&nc->send_mbuf, num_written);
-- mbuf_trim(&nc->send_mbuf);
-+ tcpip_callback(mg_lwip_tcp_write_tcpip, &ctx);
-+ return ctx.ret;
- }
-
--void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf,
-- size_t len) {
-- mbuf_append(&nc->send_mbuf, buf, len);
-- mg_lwip_mgr_schedule_poll(nc->mgr);
-+struct udp_sendto_ctx {
-+ struct udp_pcb *upcb;
-+ struct pbuf *p;
-+ ip_addr_t *ip;
-+ uint16_t port;
-+ int ret;
-+};
-+
-+static void udp_sendto_tcpip(void *arg) {
-+ struct udp_sendto_ctx *ctx = (struct udp_sendto_ctx *) arg;
-+ ctx->ret = udp_sendto(ctx->upcb, ctx->p, ctx->ip, ctx->port);
- }
-
--void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf,
-- size_t len) {
-+static int mg_lwip_udp_send(struct mg_connection *nc, const void *data,
-+ uint16_t len) {
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-- if (nc->sock == INVALID_SOCKET || cs->pcb.udp == NULL) {
-+ if (cs->pcb.udp == NULL) {
- /*
- * In case of UDP, this usually means, what
- * async DNS resolve is still in progress and connection
- * is not ready yet
- */
- DBG(("%p socket is not connected", nc));
-- return;
-+ return -1;
- }
- struct udp_pcb *upcb = cs->pcb.udp;
- struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
-- ip_addr_t *ip = (ip_addr_t *) &nc->sa.sin.sin_addr.s_addr;
-+#if defined(LWIP_IPV4) && LWIP_IPV4 && defined(LWIP_IPV6) && LWIP_IPV6
-+ ip_addr_t ip = {.u_addr.ip4.addr = nc->sa.sin.sin_addr.s_addr, .type = 0};
-+#else
-+ ip_addr_t ip = {.addr = nc->sa.sin.sin_addr.s_addr};
-+#endif
- u16_t port = ntohs(nc->sa.sin.sin_port);
-- memcpy(p->payload, buf, len);
-- cs->err = udp_sendto(upcb, p, (ip_addr_t *) ip, port);
-- DBG(("%p udp_sendto = %d", nc, cs->err));
-+ if (p == NULL) {
-+ DBG(("OOM"));
-+ return 0;
-+ }
-+ memcpy(p->payload, data, len);
-+ struct udp_sendto_ctx ctx = {.upcb = upcb, .p = p, .ip = &ip, .port = port};
-+ tcpip_callback(udp_sendto_tcpip, &ctx);
-+ cs->err = ctx.ret;
- pbuf_free(p);
-- if (cs->err != ERR_OK) {
-- mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
-+ return (cs->err == ERR_OK ? len : -1);
-+}
-+
-+static void mg_lwip_send_more(struct mg_connection *nc) {
-+ int num_sent = 0;
-+ if (nc->sock == INVALID_SOCKET) return;
-+ if (nc->flags & MG_F_UDP) {
-+ num_sent = mg_lwip_udp_send(nc, nc->send_mbuf.buf, nc->send_mbuf.len);
-+ DBG(("%p mg_lwip_udp_send %u = %d", nc, nc->send_mbuf.len, num_sent));
-+ } else {
-+ num_sent = mg_lwip_tcp_write(nc, nc->send_mbuf.buf, nc->send_mbuf.len);
-+ DBG(("%p mg_lwip_tcp_write %u = %d", nc, nc->send_mbuf.len, num_sent));
-+ }
-+ if (num_sent == 0) return;
-+ if (num_sent > 0) {
-+ mg_if_sent_cb(nc, num_sent);
- } else {
-- cs->num_sent += len;
-- mg_lwip_post_signal(MG_SIG_SENT_CB, nc);
-+ mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
- }
- }
-
-+void mg_lwip_if_tcp_send(struct mg_connection *nc, const void *buf,
-+ size_t len) {
-+ mbuf_append(&nc->send_mbuf, buf, len);
-+ mg_lwip_mgr_schedule_poll(nc->mgr);
-+}
-+
-+void mg_lwip_if_udp_send(struct mg_connection *nc, const void *buf,
-+ size_t len) {
-+ mbuf_append(&nc->send_mbuf, buf, len);
-+ mg_lwip_mgr_schedule_poll(nc->mgr);
-+}
-+
-+struct tcp_recved_ctx {
-+ struct tcp_pcb *tpcb;
-+ size_t len;
-+};
-+
-+void tcp_recved_tcpip(void *arg) {
-+ struct tcp_recved_ctx *ctx = (struct tcp_recved_ctx *) arg;
-+ tcp_recved(ctx->tpcb, ctx->len);
-+}
-+
- void mg_lwip_if_recved(struct mg_connection *nc, size_t len) {
- if (nc->flags & MG_F_UDP) return;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-@@ -14137,28 +15148,35 @@ void mg_lwip_if_recved(struct mg_connect
- DBG(("%p invalid socket", nc));
- return;
- }
-- DBG(("%p %p %u", nc, cs->pcb.tcp, len));
--/* Currently SSL acknowledges data immediately.
-- * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */
-+ DBG(("%p %p %u %u", nc, cs->pcb.tcp, len,
-+ (cs->rx_chain ? cs->rx_chain->tot_len : 0)));
-+ struct tcp_recved_ctx ctx = {.tpcb = cs->pcb.tcp, .len = len};
- #if MG_ENABLE_SSL
- if (!(nc->flags & MG_F_SSL)) {
-- tcp_recved(cs->pcb.tcp, len);
-+ tcpip_callback(tcp_recved_tcpip, &ctx);
-+ } else {
-+ /* Currently SSL acknowledges data immediately.
-+ * TODO(rojer): Find a way to propagate mg_lwip_if_recved. */
- }
- #else
-- tcp_recved(cs->pcb.tcp, len);
-+ tcpip_callback(tcp_recved_tcpip, &ctx);
- #endif
- mbuf_trim(&nc->recv_mbuf);
- }
-
- int mg_lwip_if_create_conn(struct mg_connection *nc) {
- struct mg_lwip_conn_state *cs =
-- (struct mg_lwip_conn_state *) calloc(1, sizeof(*cs));
-+ (struct mg_lwip_conn_state *) MG_CALLOC(1, sizeof(*cs));
- if (cs == NULL) return 0;
- cs->nc = nc;
- nc->sock = (intptr_t) cs;
- return 1;
- }
-
-+static void udp_remove_tcpip(void *arg) {
-+ udp_remove((struct udp_pcb *) arg);
-+}
-+
- void mg_lwip_if_destroy_conn(struct mg_connection *nc) {
- if (nc->sock == INVALID_SOCKET) return;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-@@ -14168,7 +15186,7 @@ void mg_lwip_if_destroy_conn(struct mg_c
- tcp_arg(tpcb, NULL);
- DBG(("%p tcp_close %p", nc, tpcb));
- tcp_arg(tpcb, NULL);
-- tcp_close(tpcb);
-+ tcpip_callback(tcp_close_tcpip, tpcb);
- }
- while (cs->rx_chain != NULL) {
- struct pbuf *seg = cs->rx_chain;
-@@ -14176,16 +15194,16 @@ void mg_lwip_if_destroy_conn(struct mg_c
- pbuf_free(seg);
- }
- memset(cs, 0, sizeof(*cs));
-- free(cs);
-+ MG_FREE(cs);
- } else if (nc->listener == NULL) {
- /* Only close outgoing UDP pcb or listeners. */
- struct udp_pcb *upcb = cs->pcb.udp;
- if (upcb != NULL) {
- DBG(("%p udp_remove %p", nc, upcb));
-- udp_remove(upcb);
-+ tcpip_callback(udp_remove_tcpip, upcb);
- }
- memset(cs, 0, sizeof(*cs));
-- free(cs);
-+ MG_FREE(cs);
- }
- nc->sock = INVALID_SOCKET;
- }
-@@ -14193,22 +15211,21 @@ void mg_lwip_if_destroy_conn(struct mg_c
- void mg_lwip_if_get_conn_addr(struct mg_connection *nc, int remote,
- union socket_address *sa) {
- memset(sa, 0, sizeof(*sa));
-- if (nc->sock == INVALID_SOCKET) return;
-+ if (nc == NULL || nc->sock == INVALID_SOCKET) return;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- if (nc->flags & MG_F_UDP) {
- struct udp_pcb *upcb = cs->pcb.udp;
- if (remote) {
- memcpy(sa, &nc->sa, sizeof(*sa));
-- } else {
-+ } else if (upcb != NULL) {
- sa->sin.sin_port = htons(upcb->local_port);
- SET_ADDR(sa, &upcb->local_ip);
- }
- } else {
- struct tcp_pcb *tpcb = cs->pcb.tcp;
- if (remote) {
-- sa->sin.sin_port = htons(tpcb->remote_port);
-- SET_ADDR(sa, &tpcb->remote_ip);
-- } else {
-+ memcpy(sa, &nc->sa, sizeof(*sa));
-+ } else if (tpcb != NULL) {
- sa->sin.sin_port = htons(tpcb->local_port);
- SET_ADDR(sa, &tpcb->local_ip);
- }
-@@ -14241,9 +15258,9 @@ void mg_lwip_if_sock_set(struct mg_conne
- }
- /* clang-format on */
-
--struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_lwip_iface_vtable = MG_LWIP_IFACE_VTABLE;
- #if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
--struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_default_iface_vtable = MG_LWIP_IFACE_VTABLE;
- #endif
-
- #endif /* MG_ENABLE_NET_IF_LWIP_LOW_LEVEL */
-@@ -14258,7 +15275,7 @@ struct mg_iface_vtable mg_default_iface_
- #if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
-
- #ifndef MG_SIG_QUEUE_LEN
--#define MG_SIG_QUEUE_LEN 16
-+#define MG_SIG_QUEUE_LEN 32
- #endif
-
- struct mg_ev_mgr_lwip_signal {
-@@ -14275,20 +15292,32 @@ struct mg_ev_mgr_lwip_data {
- void mg_lwip_post_signal(enum mg_sig_type sig, struct mg_connection *nc) {
- struct mg_ev_mgr_lwip_data *md =
- (struct mg_ev_mgr_lwip_data *) nc->iface->data;
-- if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) return;
-+ mgos_lock();
-+ if (md->sig_queue_len >= MG_SIG_QUEUE_LEN) {
-+ mgos_unlock();
-+ return;
-+ }
- int end_index = (md->start_index + md->sig_queue_len) % MG_SIG_QUEUE_LEN;
- md->sig_queue[end_index].sig = sig;
- md->sig_queue[end_index].nc = nc;
- md->sig_queue_len++;
-+ mg_lwip_mgr_schedule_poll(nc->mgr);
-+ mgos_unlock();
- }
-
- void mg_ev_mgr_lwip_process_signals(struct mg_mgr *mgr) {
- struct mg_ev_mgr_lwip_data *md =
- (struct mg_ev_mgr_lwip_data *) mgr->ifaces[MG_MAIN_IFACE]->data;
- while (md->sig_queue_len > 0) {
-+ mgos_lock();
-+ int sig = md->sig_queue[md->start_index].sig;
- struct mg_connection *nc = md->sig_queue[md->start_index].nc;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-- switch (md->sig_queue[md->start_index].sig) {
-+ md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN;
-+ md->sig_queue_len--;
-+ mgos_unlock();
-+ if (nc->iface == NULL || nc->mgr == NULL) continue;
-+ switch (sig) {
- case MG_SIG_CONNECT_RESULT: {
- #if MG_ENABLE_SSL
- if (cs->err == 0 && (nc->flags & MG_F_SSL) &&
-@@ -14302,30 +15331,32 @@ void mg_ev_mgr_lwip_process_signals(stru
- break;
- }
- case MG_SIG_CLOSE_CONN: {
-- nc->flags |= MG_F_CLOSE_IMMEDIATELY;
-+ nc->flags |= MG_F_SEND_AND_CLOSE;
- mg_close_conn(nc);
- break;
- }
- case MG_SIG_RECV: {
-- mg_lwip_handle_recv(nc);
-+ cs->recv_pending = 0;
-+ if (nc->flags & MG_F_UDP) {
-+ mg_lwip_handle_recv_udp(nc);
-+ } else {
-+ mg_lwip_handle_recv_tcp(nc);
-+ }
- break;
- }
-- case MG_SIG_SENT_CB: {
-- if (cs->num_sent > 0) mg_if_sent_cb(nc, cs->num_sent);
-- cs->num_sent = 0;
-+ case MG_SIG_TOMBSTONE: {
- break;
- }
-- case MG_SIG_TOMBSTONE: {
-+ case MG_SIG_ACCEPT: {
-+ mg_lwip_handle_accept(nc);
- break;
- }
- }
-- md->start_index = (md->start_index + 1) % MG_SIG_QUEUE_LEN;
-- md->sig_queue_len--;
- }
- }
-
- void mg_lwip_if_init(struct mg_iface *iface) {
-- LOG(LL_INFO, ("%p Mongoose init"));
-+ LOG(LL_INFO, ("%p Mongoose init", iface));
- iface->data = MG_CALLOC(1, sizeof(struct mg_ev_mgr_lwip_data));
- }
-
-@@ -14364,22 +15395,20 @@ time_t mg_lwip_if_poll(struct mg_iface *
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- tmp = nc->next;
- n++;
-- if (nc->flags & MG_F_CLOSE_IMMEDIATELY) {
-+ if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
-+ ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) &&
-+ (nc->send_mbuf.len == 0))) {
- mg_close_conn(nc);
- continue;
- }
- mg_if_poll(nc, now);
- mg_if_timer(nc, now);
-- if (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE) &&
-- !(nc->flags & MG_F_WANT_WRITE)) {
-- mg_close_conn(nc);
-- continue;
-- }
- #if MG_ENABLE_SSL
- if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL &&
- cs->pcb.tcp->state == ESTABLISHED) {
- if (((nc->flags & MG_F_WANT_WRITE) ||
-- (nc->send_mbuf.len > 0) && (nc->flags & MG_F_SSL_HANDSHAKE_DONE)) &&
-+ ((nc->send_mbuf.len > 0) &&
-+ (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) &&
- cs->pcb.tcp->snd_buf > 0) {
- /* Can write more. */
- if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
-@@ -14398,14 +15427,14 @@ time_t mg_lwip_if_poll(struct mg_iface *
- } else
- #endif /* MG_ENABLE_SSL */
- {
-- if (!(nc->flags & (MG_F_CONNECTING | MG_F_UDP))) {
-- if (nc->send_mbuf.len > 0) mg_lwip_send_more(nc);
-+ if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) {
-+ mg_lwip_send_more(nc);
- }
- }
- if (nc->sock != INVALID_SOCKET &&
- !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL &&
- cs->pcb.tcp->unsent != NULL) {
-- tcp_output(cs->pcb.tcp);
-+ tcpip_callback(tcp_output_tcpip, cs->pcb.tcp);
- }
- if (nc->ev_timer_time > 0) {
- if (num_timers == 0 || nc->ev_timer_time < min_timer) {
-@@ -14413,6 +15442,19 @@ time_t mg_lwip_if_poll(struct mg_iface *
- }
- num_timers++;
- }
-+
-+ if (nc->sock != INVALID_SOCKET) {
-+ /* Try to consume data from cs->rx_chain */
-+ mg_lwip_consume_rx_chain_tcp(nc);
-+
-+ /*
-+ * If the connection is about to close, and rx_chain is finally empty,
-+ * send the MG_SIG_CLOSE_CONN signal
-+ */
-+ if (cs->draining_rx_chain && cs->rx_chain == NULL) {
-+ mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
-+ }
-+ }
- }
- #if 0
- DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms",
-@@ -14425,20 +15467,40 @@ time_t mg_lwip_if_poll(struct mg_iface *
-
- uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) {
- struct mg_connection *nc;
-- double now = mg_time();
-+ double now;
- double min_timer = 0;
- int num_timers = 0;
- mg_ev_mgr_lwip_process_signals(mgr);
- for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
-+ struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- if (nc->ev_timer_time > 0) {
- if (num_timers == 0 || nc->ev_timer_time < min_timer) {
- min_timer = nc->ev_timer_time;
- }
- num_timers++;
- }
-+ if (nc->send_mbuf.len > 0
-+#if MG_ENABLE_SSL
-+ || (nc->flags & MG_F_WANT_WRITE)
-+#endif
-+ ) {
-+ int can_send = 0;
-+ /* We have stuff to send, but can we? */
-+ if (nc->flags & MG_F_UDP) {
-+ /* UDP is always ready for sending. */
-+ can_send = (cs->pcb.udp != NULL);
-+ } else {
-+ can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0);
-+ }
-+ /* We want and can send, request a poll immediately. */
-+ if (can_send) return 0;
-+ }
- }
- uint32_t timeout_ms = ~0;
-+ now = mg_time();
- if (num_timers > 0) {
-+ /* If we have a timer that is past due, do a poll ASAP. */
-+ if (min_timer < now) return 0;
- double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
- if (timer_timeout_ms < timeout_ms) {
- timeout_ms = timer_timeout_ms;
-@@ -14458,6 +15520,7 @@ uint32_t mg_lwip_get_poll_delay_ms(struc
-
- #if MG_ENABLE_SSL && MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
-
-+/* Amalgamated: #include "common/mg_mem.h" */
- /* Amalgamated: #include "common/cs_dbg.h" */
-
- #include <lwip/pbuf.h>
-@@ -14467,14 +15530,6 @@ uint32_t mg_lwip_get_poll_delay_ms(struc
- #define MG_LWIP_SSL_IO_SIZE 1024
- #endif
-
--/*
-- * Stop processing incoming SSL traffic when recv_mbuf.size is this big.
-- * It'a a uick solution for SSL recv pushback.
-- */
--#ifndef MG_LWIP_SSL_RECV_MBUF_LIMIT
--#define MG_LWIP_SSL_RECV_MBUF_LIMIT 3072
--#endif
--
- #ifndef MIN
- #define MIN(a, b) ((a) < (b) ? (a) : (b))
- #endif
-@@ -14485,7 +15540,7 @@ void mg_lwip_ssl_do_hs(struct mg_connect
- enum mg_ssl_if_result res;
- if (nc->flags & MG_F_CLOSE_IMMEDIATELY) return;
- res = mg_ssl_if_handshake(nc);
-- DBG(("%p %d %d %d", nc, nc->flags, server_side, res));
-+ DBG(("%p %lu %d %d", nc, nc->flags, server_side, res));
- if (res != MG_SSL_OK) {
- if (res == MG_SSL_WANT_WRITE) {
- nc->flags |= MG_F_WANT_WRITE;
-@@ -14533,10 +15588,9 @@ void mg_lwip_ssl_send(struct mg_connecti
- len = MIN(MG_LWIP_SSL_IO_SIZE, nc->send_mbuf.len);
- }
- int ret = mg_ssl_if_write(nc, nc->send_mbuf.buf, len);
-- DBG(("%p SSL_write %u = %d, %d", nc, len, ret));
-+ DBG(("%p SSL_write %u = %d", nc, len, ret));
- if (ret > 0) {
-- mbuf_remove(&nc->send_mbuf, ret);
-- mbuf_trim(&nc->send_mbuf);
-+ mg_if_sent_cb(nc, ret);
- cs->last_ssl_write_size = 0;
- } else if (ret < 0) {
- /* This is tricky. We must remember the exact data we were sending to retry
-@@ -14556,13 +15610,13 @@ void mg_lwip_ssl_recv(struct mg_connecti
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- /* Don't deliver data before connect callback */
- if (nc->flags & MG_F_CONNECTING) return;
-- while (nc->recv_mbuf.len < MG_LWIP_SSL_RECV_MBUF_LIMIT) {
-- char *buf = (char *) malloc(MG_LWIP_SSL_IO_SIZE);
-+ while (nc->recv_mbuf.len < nc->recv_mbuf_limit) {
-+ char *buf = (char *) MG_MALLOC(MG_LWIP_SSL_IO_SIZE);
- if (buf == NULL) return;
- int ret = mg_ssl_if_read(nc, buf, MG_LWIP_SSL_IO_SIZE);
- DBG(("%p %p SSL_read %u = %d", nc, cs->rx_chain, MG_LWIP_SSL_IO_SIZE, ret));
- if (ret <= 0) {
-- free(buf);
-+ MG_FREE(buf);
- if (ret == MG_SSL_WANT_WRITE) {
- nc->flags |= MG_F_WANT_WRITE;
- return;
-@@ -14621,8 +15675,8 @@ int ssl_socket_send(void *ctx, const uns
- struct mg_connection *nc = (struct mg_connection *) ctx;
- struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
- int ret = mg_lwip_tcp_write(cs->nc, buf, len);
-- DBG(("%p mg_lwip_tcp_write %u = %d", cs->nc, len, ret));
- if (ret == 0) ret = MBEDTLS_ERR_SSL_WANT_WRITE;
-+ LOG(LL_DEBUG, ("%p %d -> %d", nc, len, ret));
- return ret;
- }
-
-@@ -14636,6 +15690,7 @@ int ssl_socket_recv(void *ctx, unsigned
- }
- size_t seg_len = (seg->len - cs->rx_offset);
- DBG(("%u %u %u %u", len, cs->rx_chain->len, seg_len, cs->rx_chain->tot_len));
-+ mgos_lock();
- len = MIN(len, seg_len);
- pbuf_copy_partial(seg, buf, len, cs->rx_offset);
- cs->rx_offset += len;
-@@ -14647,6 +15702,8 @@ int ssl_socket_recv(void *ctx, unsigned
- pbuf_free(seg);
- cs->rx_offset = 0;
- }
-+ mgos_unlock();
-+ LOG(LL_DEBUG, ("%p <- %d", nc, (int) len));
- return len;
- }
-
-@@ -14751,7 +15808,7 @@ extern "C" {
- #define MG_ENABLE_NET_IF_PIC32 MG_NET_IF == MG_NET_IF_PIC32
- #endif
-
--extern struct mg_iface_vtable mg_pic32_iface_vtable;
-+extern const struct mg_iface_vtable mg_pic32_iface_vtable;
-
- #ifdef __cplusplus
- }
-@@ -14919,10 +15976,7 @@ static void mg_handle_send(struct mg_con
- }
- }
-
-- if (bytes_written != 0) {
-- mbuf_remove(&nc->send_mbuf, bytes_written);
-- mg_if_sent_cb(nc, bytes_written);
-- }
-+ mg_if_sent_cb(nc, bytes_written);
- }
-
- static void mg_handle_recv(struct mg_connection *nc) {
-@@ -15050,9 +16104,29 @@ void mg_pic32_if_connect_udp(struct mg_c
- }
- /* clang-format on */
-
--struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_pic32_iface_vtable = MG_PIC32_IFACE_VTABLE;
- #if MG_NET_IF == MG_NET_IF_PIC32
--struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE;
-+const struct mg_iface_vtable mg_default_iface_vtable = MG_PIC32_IFACE_VTABLE;
- #endif
-
- #endif /* MG_ENABLE_NET_IF_PIC32 */
-+#ifdef MG_MODULE_LINES
-+#line 1 "common/platforms/windows/windows_direct.c"
-+#endif
-+/*
-+ * Copyright (c) 2017 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#ifdef _WIN32
-+
-+int rmdir(const char *dirname) {
-+ return _rmdir(dirname);
-+}
-+
-+unsigned int sleep(unsigned int seconds) {
-+ Sleep(seconds * 1000);
-+ return 0;
-+}
-+
-+#endif /* _WIN32 */
---- smplayer-18.4.0~ds0.orig/webserver/mongoose.h
-+++ smplayer-18.4.0~ds0/webserver/mongoose.h
-@@ -1,5 +1,5 @@
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/common.h"
-+#line 1 "mongoose/src/mg_common.h"
- #endif
- /*
- * Copyright (c) 2004-2013 Sergey Lyubka
-@@ -23,7 +23,7 @@
- #ifndef CS_MONGOOSE_SRC_COMMON_H_
- #define CS_MONGOOSE_SRC_COMMON_H_
-
--#define MG_VERSION "6.6"
-+#define MG_VERSION "6.11"
-
- /* Local tweaks, applied before any of Mongoose's own headers. */
- #ifdef MG_LOCALS
-@@ -46,9 +46,10 @@
- #define CS_P_WINDOWS 2
- #define CS_P_ESP32 15
- #define CS_P_ESP8266 3
-+#define CS_P_CC3100 6
- #define CS_P_CC3200 4
-+#define CS_P_CC3220 17
- #define CS_P_MSP432 5
--#define CS_P_CC3100 6
- #define CS_P_TM4C129 14
- #define CS_P_MBED 7
- #define CS_P_WINCE 8
-@@ -57,15 +58,18 @@
- #define CS_P_NRF51 12
- #define CS_P_NRF52 10
- #define CS_P_PIC32 11
--/* Next id: 16 */
-+#define CS_P_STM32 16
-+/* Next id: 18 */
-
- /* If not specified explicitly, we guess platform by defines. */
- #ifndef CS_PLATFORM
-
- #if defined(TARGET_IS_MSP432P4XX) || defined(__MSP432P401R__)
- #define CS_PLATFORM CS_P_MSP432
--#elif defined(cc3200)
-+#elif defined(cc3200) || defined(TARGET_IS_CC3200)
- #define CS_PLATFORM CS_P_CC3200
-+#elif defined(cc3220) || defined(TARGET_IS_CC3220)
-+#define CS_PLATFORM CS_P_CC3220
- #elif defined(__unix__) || defined(__APPLE__)
- #define CS_PLATFORM CS_P_UNIX
- #elif defined(WINCE)
-@@ -87,6 +91,8 @@
- #elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
- defined(TARGET_IS_TM4C129_RA2)
- #define CS_PLATFORM CS_P_TM4C129
-+#elif defined(STM32)
-+#define CS_PLATFORM CS_P_STM32
- #endif
-
- #ifndef CS_PLATFORM
-@@ -108,8 +114,9 @@
- /* Amalgamated: #include "common/platforms/platform_windows.h" */
- /* Amalgamated: #include "common/platforms/platform_esp32.h" */
- /* Amalgamated: #include "common/platforms/platform_esp8266.h" */
--/* Amalgamated: #include "common/platforms/platform_cc3200.h" */
- /* Amalgamated: #include "common/platforms/platform_cc3100.h" */
-+/* Amalgamated: #include "common/platforms/platform_cc3200.h" */
-+/* Amalgamated: #include "common/platforms/platform_cc3220.h" */
- /* Amalgamated: #include "common/platforms/platform_mbed.h" */
- /* Amalgamated: #include "common/platforms/platform_nrf51.h" */
- /* Amalgamated: #include "common/platforms/platform_nrf52.h" */
-@@ -117,6 +124,7 @@
- /* Amalgamated: #include "common/platforms/platform_nxp_lpc.h" */
- /* Amalgamated: #include "common/platforms/platform_nxp_kinetis.h" */
- /* Amalgamated: #include "common/platforms/platform_pic32.h" */
-+/* Amalgamated: #include "common/platforms/platform_stm32.h" */
-
- /* Common stuff */
-
-@@ -191,6 +199,7 @@
- #include <stdlib.h>
- #include <sys/stat.h>
- #include <time.h>
-+#include <ctype.h>
-
- #ifdef _MSC_VER
- #pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */
-@@ -201,6 +210,12 @@
- #include <windows.h>
- #include <process.h>
-
-+#if _MSC_VER < 1700
-+typedef int bool;
-+#else
-+#include <stdbool.h>
-+#endif
-+
- #if defined(_MSC_VER) && _MSC_VER >= 1800
- #define strdup _strdup
- #endif
-@@ -217,15 +232,13 @@
- #define __func__ __FILE__ ":" STR(__LINE__)
- #endif
- #define snprintf _snprintf
--#define fileno _fileno
- #define vsnprintf _vsnprintf
--#define sleep(x) Sleep((x) *1000)
- #define to64(x) _atoi64(x)
- #if !defined(__MINGW32__) && !defined(__MINGW64__)
- #define popen(x, y) _popen((x), (y))
- #define pclose(x) _pclose(x)
-+#define fileno _fileno
- #endif
--#define rmdir _rmdir
- #if defined(_MSC_VER) && _MSC_VER >= 1400
- #define fseeko(x, y, z) _fseeki64((x), (y), (z))
- #else
-@@ -270,6 +283,7 @@ typedef struct _stati64 cs_stat_t;
- #define S_ISREG(x) (((x) &_S_IFMT) == _S_IFREG)
- #endif
- #define DIRSEP '\\'
-+#define CS_DEFINE_DIRENT
-
- #ifndef va_copy
- #ifdef __va_copy
-@@ -315,6 +329,16 @@ typedef struct _stati64 cs_stat_t;
- #define MG_NET_IF MG_NET_IF_SOCKET
- #endif
-
-+unsigned int sleep(unsigned int seconds);
-+
-+/* https://stackoverflow.com/questions/16647819/timegm-cross-platform */
-+#define timegm _mkgmtime
-+
-+#define gmtime_r(a, b) \
-+ do { \
-+ *(b) = *gmtime(a); \
-+ } while (0)
-+
- #endif /* CS_PLATFORM == CS_P_WINDOWS */
- #endif /* CS_COMMON_PLATFORMS_PLATFORM_WINDOWS_H_ */
- #ifdef MG_MODULE_LINES
-@@ -363,6 +387,7 @@ typedef struct _stati64 cs_stat_t;
- #include <pthread.h>
- #include <signal.h>
- #include <stdarg.h>
-+#include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-@@ -454,6 +479,14 @@ typedef struct stat cs_stat_t;
- #define MG_NET_IF MG_NET_IF_SOCKET
- #endif
-
-+#ifndef MG_HOSTS_FILE_NAME
-+#define MG_HOSTS_FILE_NAME "/etc/hosts"
-+#endif
-+
-+#ifndef MG_RESOLV_CONF_FILE_NAME
-+#define MG_RESOLV_CONF_FILE_NAME "/etc/resolv.conf"
-+#endif
-+
- #endif /* CS_PLATFORM == CS_P_UNIX */
- #endif /* CS_COMMON_PLATFORMS_PLATFORM_UNIX_H_ */
- #ifdef MG_MODULE_LINES
-@@ -470,9 +503,11 @@ typedef struct stat cs_stat_t;
-
- #include <assert.h>
- #include <ctype.h>
-+#include <dirent.h>
- #include <fcntl.h>
- #include <inttypes.h>
- #include <machine/endian.h>
-+#include <stdbool.h>
- #include <stdint.h>
- #include <string.h>
- #include <sys/stat.h>
-@@ -516,6 +551,7 @@ typedef struct stat cs_stat_t;
- #include <fcntl.h>
- #include <inttypes.h>
- #include <machine/endian.h>
-+#include <stdbool.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <sys/time.h>
-@@ -523,13 +559,17 @@ typedef struct stat cs_stat_t;
- #define SIZE_T_FMT "u"
- typedef struct stat cs_stat_t;
- #define DIRSEP '/'
-+#if !defined(MGOS_VFS_DEFINE_DIRENT)
-+#define CS_DEFINE_DIRENT
-+#endif
-+
- #define to64(x) strtoll(x, NULL, 10)
- #define INT64_FMT PRId64
- #define INT64_X_FMT PRIx64
- #define __cdecl
- #define _FILE_OFFSET_BITS 32
-
--#ifndef RTOS_SDK
-+#if !defined(RTOS_SDK) && !defined(__cplusplus)
- #define fileno(x) -1
- #endif
-
-@@ -541,9 +581,9 @@ typedef struct stat cs_stat_t;
- #ifndef MG_NET_IF
- #include <lwip/opt.h>
- #if LWIP_SOCKET /* RTOS SDK has LWIP sockets */
--# define MG_NET_IF MG_NET_IF_SOCKET
-+#define MG_NET_IF MG_NET_IF_SOCKET
- #else
--# define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
-+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
- #endif
- #endif
-
-@@ -551,6 +591,12 @@ typedef struct stat cs_stat_t;
- #define CS_ENABLE_STDIO 1
- #endif
-
-+#define inet_ntop(af, src, dst, size) \
-+ (((af) == AF_INET) ? ipaddr_ntoa_r((const ip_addr_t *) (src), (dst), (size)) \
-+ : NULL)
-+#define inet_pton(af, src, dst) \
-+ (((af) == AF_INET) ? ipaddr_aton((src), (ip_addr_t *) (dst)) : 0)
-+
- #endif /* CS_PLATFORM == CS_P_ESP8266 */
- #endif /* CS_COMMON_PLATFORMS_PLATFORM_ESP8266_H_ */
- #ifdef MG_MODULE_LINES
-@@ -584,7 +630,7 @@ typedef struct stat cs_stat_t;
-
- #include <simplelink.h>
- #include <netapp.h>
--#undef timeval
-+#undef timeval
-
- typedef int sock_t;
- #define INVALID_SOCKET (-1)
-@@ -618,6 +664,7 @@ int inet_pton(int af, const char *src, v
- #include <ctype.h>
- #include <errno.h>
- #include <inttypes.h>
-+#include <stdbool.h>
- #include <stdint.h>
- #include <string.h>
- #include <time.h>
-@@ -659,6 +706,7 @@ extern "C" {
- struct SlTimeval_t;
- #define timeval SlTimeval_t
- int gettimeofday(struct timeval *t, void *tz);
-+int settimeofday(const struct timeval *tv, const void *tz);
-
- int asprintf(char **strp, const char *fmt, ...);
-
-@@ -682,7 +730,7 @@ struct stat {
- };
-
- int _stat(const char *pathname, struct stat *st);
--#define stat(a, b) _stat(a, b)
-+int stat(const char *pathname, struct stat *st);
-
- #define __S_IFMT 0170000
-
-@@ -698,27 +746,13 @@ int _stat(const char *pathname, struct s
- #define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
- #define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
-
--/* As of 5.2.7, TI compiler does not support va_copy() yet. */
-+/* 5.x series compilers don't have va_copy, 16.x do. */
-+#if __TI_COMPILER_VERSION__ < 16000000
- #define va_copy(apc, ap) ((apc) = (ap))
-+#endif
-
- #endif /* __TI_COMPILER_VERSION__ */
-
--#ifdef CC3200_FS_SPIFFS
--#include <common/spiffs/spiffs.h>
--
--typedef struct {
-- spiffs_DIR dh;
-- struct spiffs_dirent de;
--} DIR;
--
--#define d_name name
--#define dirent spiffs_dirent
--
--DIR *opendir(const char *dir_name);
--int closedir(DIR *dir);
--struct dirent *readdir(DIR *dir);
--#endif /* CC3200_FS_SPIFFS */
--
- #ifdef CC3200_FS_SLFS
- #define MG_FS_SLFS
- #endif
-@@ -726,6 +760,7 @@ struct dirent *readdir(DIR *dir);
- #if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \
- !defined(MG_ENABLE_FILESYSTEM)
- #define MG_ENABLE_FILESYSTEM 1
-+#define CS_DEFINE_DIRENT
- #endif
-
- #ifndef CS_ENABLE_STDIO
-@@ -835,7 +870,8 @@ int _stat(const char *pathname, struct s
- #define CS_ENABLE_STDIO 1
- #endif
-
--#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && !defined(MG_ENABLE_FILESYSTEM)
-+#if (defined(CC3200_FS_SPIFFS) || defined(CC3200_FS_SLFS)) && \
-+ !defined(MG_ENABLE_FILESYSTEM)
- #define MG_ENABLE_FILESYSTEM 1
- #endif
-
-@@ -879,15 +915,15 @@ typedef struct stat cs_stat_t;
- #define __cdecl
-
- #ifndef MG_NET_IF
--# include <lwip/opt.h>
--# if LWIP_SOCKET
--# define MG_NET_IF MG_NET_IF_SOCKET
--# else
--# define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
--# endif
--# define MG_LWIP 1
-+#include <lwip/opt.h>
-+#if LWIP_SOCKET
-+#define MG_NET_IF MG_NET_IF_SOCKET
-+#else
-+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
-+#endif
-+#define MG_LWIP 1
- #elif MG_NET_IF == MG_NET_IF_SIMPLELINK
--# include "common/platforms/simplelink/cs_simplelink.h"
-+/* Amalgamated: #include "common/platforms/simplelink/cs_simplelink.h" */
- #endif
-
- #ifndef CS_ENABLE_STDIO
-@@ -1009,16 +1045,16 @@ in_addr_t inet_addr(const char *cp);
-
- #define to64(x) strtoll(x, NULL, 10)
-
--#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
--#define MG_LWIP 1
--#define MG_ENABLE_IPV6 1
-+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
-+#define MG_LWIP 1
-+#define MG_ENABLE_IPV6 1
-
- /*
- * For ARM C Compiler, make lwip to export `struct timeval`; for other
- * compilers, suppress it.
- */
- #if !defined(__ARMCC_VERSION)
--# define LWIP_TIMEVAL_PRIVATE 0
-+#define LWIP_TIMEVAL_PRIVATE 0
- #else
- struct timeval;
- int gettimeofday(struct timeval *tp, void *tzp);
-@@ -1049,18 +1085,19 @@ int gettimeofday(struct timeval *tp, voi
- #include <ctype.h>
- #include <errno.h>
- #include <inttypes.h>
-+#include <stdbool.h>
- #include <stdint.h>
- #include <string.h>
- #include <time.h>
-
- #define to64(x) strtoll(x, NULL, 10)
-
--#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
--#define MG_LWIP 1
--#define MG_ENABLE_IPV6 1
-+#define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
-+#define MG_LWIP 1
-+#define MG_ENABLE_IPV6 1
-
- #if !defined(ENOSPC)
--# define ENOSPC 28 /* No space left on device */
-+#define ENOSPC 28 /* No space left on device */
- #endif
-
- /*
-@@ -1068,7 +1105,7 @@ int gettimeofday(struct timeval *tp, voi
- * compilers, suppress it.
- */
- #if !defined(__ARMCC_VERSION)
--# define LWIP_TIMEVAL_PRIVATE 0
-+#define LWIP_TIMEVAL_PRIVATE 0
- #endif
-
- #define INT64_FMT PRId64
-@@ -1092,9 +1129,10 @@ int gettimeofday(struct timeval *tp, voi
- #ifndef CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
- #define CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_
-
-+#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK
-+
- /* If simplelink.h is already included, all bets are off. */
--#if defined(MG_NET_IF) && MG_NET_IF == MG_NET_IF_SIMPLELINK && \
-- !defined(__SIMPLELINK_H__)
-+#if !defined(__SIMPLELINK_H__)
-
- #include <stdbool.h>
-
-@@ -1108,6 +1146,12 @@ int gettimeofday(struct timeval *tp, voi
- #undef fd_set
- #endif
-
-+#if CS_PLATFORM == CS_P_CC3220
-+#include <ti/drivers/net/wifi/porting/user.h>
-+#include <ti/drivers/net/wifi/simplelink.h>
-+#include <ti/drivers/net/wifi/sl_socket.h>
-+#include <ti/drivers/net/wifi/netapp.h>
-+#else
- /* We want to disable SL_INC_STD_BSD_API_NAMING, so we include user.h ourselves
- * and undef it. */
- #define PROVISIONING_API_H_
-@@ -1117,6 +1161,7 @@ int gettimeofday(struct timeval *tp, voi
-
- #include <simplelink/include/simplelink.h>
- #include <simplelink/include/netapp.h>
-+#endif /* CS_PLATFORM == CS_P_CC3220 */
-
- /* Now define only the subset of the BSD API that we use.
- * Notably, close(), read() and write() are not defined. */
-@@ -1179,13 +1224,65 @@ int sl_fs_init(void);
-
- void sl_restart_cb(struct mg_mgr *mgr);
-
--int sl_set_ssl_opts(struct mg_connection *nc);
-+int sl_set_ssl_opts(int sock, struct mg_connection *nc);
-
- #ifdef __cplusplus
- }
- #endif
-
--#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK && !defined(__SIMPLELINK_H__) */
-+#endif /* !defined(__SIMPLELINK_H__) */
-+
-+/* Compatibility with older versions of SimpleLink */
-+#if SL_MAJOR_VERSION_NUM < 2
-+
-+#define SL_ERROR_BSD_EAGAIN SL_EAGAIN
-+#define SL_ERROR_BSD_EALREADY SL_EALREADY
-+#define SL_ERROR_BSD_ENOPROTOOPT SL_ENOPROTOOPT
-+#define SL_ERROR_BSD_ESECDATEERROR SL_ESECDATEERROR
-+#define SL_ERROR_BSD_ESECSNOVERIFY SL_ESECSNOVERIFY
-+#define SL_ERROR_FS_FAILED_TO_ALLOCATE_MEM SL_FS_ERR_FAILED_TO_ALLOCATE_MEM
-+#define SL_ERROR_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY \
-+ SL_FS_FILE_HAS_NOT_BEEN_CLOSE_CORRECTLY
-+#define SL_ERROR_FS_FILE_NAME_EXIST SL_FS_FILE_NAME_EXIST
-+#define SL_ERROR_FS_FILE_NOT_EXISTS SL_FS_ERR_FILE_NOT_EXISTS
-+#define SL_ERROR_FS_NO_AVAILABLE_NV_INDEX SL_FS_ERR_NO_AVAILABLE_NV_INDEX
-+#define SL_ERROR_FS_NOT_ENOUGH_STORAGE_SPACE SL_FS_ERR_NO_AVAILABLE_BLOCKS
-+#define SL_ERROR_FS_NOT_SUPPORTED SL_FS_ERR_NOT_SUPPORTED
-+#define SL_ERROR_FS_WRONG_FILE_NAME SL_FS_WRONG_FILE_NAME
-+#define SL_ERROR_FS_INVALID_HANDLE SL_FS_ERR_INVALID_HANDLE
-+#define SL_NETCFG_MAC_ADDRESS_GET SL_MAC_ADDRESS_GET
-+#define SL_SOCKET_FD_ZERO SL_FD_ZERO
-+#define SL_SOCKET_FD_SET SL_FD_SET
-+#define SL_SOCKET_FD_ISSET SL_FD_ISSET
-+#define SL_SO_SECURE_DOMAIN_NAME_VERIFICATION SO_SECURE_DOMAIN_NAME_VERIFICATION
-+
-+#define SL_FS_READ FS_MODE_OPEN_READ
-+#define SL_FS_WRITE FS_MODE_OPEN_WRITE
-+
-+#define SL_FI_FILE_SIZE(fi) ((fi).FileLen)
-+#define SL_FI_FILE_MAX_SIZE(fi) ((fi).AllocatedLen)
-+
-+#define SlDeviceVersion_t SlVersionFull
-+#define sl_DeviceGet sl_DevGet
-+#define SL_DEVICE_GENERAL SL_DEVICE_GENERAL_CONFIGURATION
-+#define SL_LEN_TYPE _u8
-+#define SL_OPT_TYPE _u8
-+
-+#else /* SL_MAJOR_VERSION_NUM >= 2 */
-+
-+#define FS_MODE_OPEN_CREATE(max_size, flag) \
-+ (SL_FS_CREATE | SL_FS_CREATE_MAX_SIZE(max_size))
-+#define SL_FI_FILE_SIZE(fi) ((fi).Len)
-+#define SL_FI_FILE_MAX_SIZE(fi) ((fi).MaxSize)
-+
-+#define SL_LEN_TYPE _u16
-+#define SL_OPT_TYPE _u16
-+
-+#endif /* SL_MAJOR_VERSION_NUM < 2 */
-+
-+int slfs_open(const unsigned char *fname, uint32_t flags);
-+
-+#endif /* MG_NET_IF == MG_NET_IF_SIMPLELINK */
-
- #endif /* CS_COMMON_PLATFORMS_SIMPLELINK_CS_SIMPLELINK_H_ */
- #ifdef MG_MODULE_LINES
-@@ -1242,6 +1339,10 @@ int sl_set_ssl_opts(struct mg_connection
- #define EWOULDBLOCK WSAEWOULDBLOCK
- #endif
-
-+#ifndef EAGAIN
-+#define EAGAIN EWOULDBLOCK
-+#endif
-+
- #ifndef __func__
- #define STRX(x) #x
- #define STR(x) STRX(x)
-@@ -1297,6 +1398,7 @@ typedef uint32_t in_addr_t;
- #define SIZE_T_FMT "u"
-
- #define DIRSEP '\\'
-+#define CS_DEFINE_DIRENT
-
- #ifndef va_copy
- #ifdef __va_copy
-@@ -1367,18 +1469,18 @@ typedef struct _stati64 {
- #endif
-
- #ifndef _UINTPTR_T_DEFINED
--typedef unsigned int* uintptr_t;
-+typedef unsigned int *uintptr_t;
- #endif
-
- #define _S_IFREG 2
- #define _S_IFDIR 4
-
- #ifndef S_ISDIR
--#define S_ISDIR(x) (((x) & _S_IFDIR) != 0)
-+#define S_ISDIR(x) (((x) &_S_IFDIR) != 0)
- #endif
-
- #ifndef S_ISREG
--#define S_ISREG(x) (((x) & _S_IFREG) != 0)
-+#define S_ISREG(x) (((x) &_S_IFREG) != 0)
- #endif
-
- int open(const char *filename, int oflag, int pmode);
-@@ -1415,7 +1517,8 @@ typedef struct stat cs_stat_t;
- #define MG_NET_IF MG_NET_IF_LWIP_LOW_LEVEL
-
- /*
-- * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and Redlib.
-+ * LPCXpress comes with 3 C library implementations: Newlib, NewlibNano and
-+ *Redlib.
- * See https://community.nxp.com/message/630860 for more details.
- *
- * Redlib is the default and lacks certain things, so we provide them.
-@@ -1511,12 +1614,54 @@ typedef TCP_SOCKET sock_t;
- #define CS_ENABLE_STDIO 1
- #endif
-
--char* inet_ntoa(struct in_addr in);
-+char *inet_ntoa(struct in_addr in);
-
- #endif /* CS_PLATFORM == CS_P_PIC32 */
-
- #endif /* CS_COMMON_PLATFORMS_PLATFORM_PIC32_H_ */
- #ifdef MG_MODULE_LINES
-+#line 1 "common/platforms/platform_stm32.h"
-+#endif
-+/*
-+ * Copyright (c) 2014-2016 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#ifndef CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
-+#define CS_COMMON_PLATFORMS_PLATFORM_STM32_H_
-+#if CS_PLATFORM == CS_P_STM32
-+
-+#include <ctype.h>
-+#include <errno.h>
-+#include <fcntl.h>
-+#include <stdint.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <sys/stat.h>
-+#include <sys/time.h>
-+#include <sys/types.h>
-+#include <unistd.h>
-+#include <dirent.h>
-+
-+#include <stm32_sdk_hal.h>
-+
-+#define to64(x) strtoll(x, NULL, 10)
-+#define INT64_FMT PRId64
-+#define SIZE_T_FMT "u"
-+typedef struct stat cs_stat_t;
-+#define DIRSEP '/'
-+
-+#ifndef CS_ENABLE_STDIO
-+#define CS_ENABLE_STDIO 1
-+#endif
-+
-+#ifndef MG_ENABLE_FILESYSTEM
-+#define MG_ENABLE_FILESYSTEM 1
-+#endif
-+
-+#endif /* CS_PLATFORM == CS_P_STM32 */
-+#endif /* CS_COMMON_PLATFORMS_PLATFORM_STM32_H_ */
-+#ifdef MG_MODULE_LINES
- #line 1 "common/platforms/lwip/mg_lwip.h"
- #endif
- /*
-@@ -1580,10 +1725,93 @@ void mg_lwip_set_keepalive_params(struct
- int interval, int count);
- #endif
-
-+/* For older version of LWIP */
-+#ifndef ipX_2_ip
-+#define ipX_2_ip(x) (x)
-+#endif
-+
- #endif /* MG_LWIP */
-
- #endif /* CS_COMMON_PLATFORMS_LWIP_MG_LWIP_H_ */
- #ifdef MG_MODULE_LINES
-+#line 1 "common/cs_md5.h"
-+#endif
-+/*
-+ * Copyright (c) 2014 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#ifndef CS_COMMON_MD5_H_
-+#define CS_COMMON_MD5_H_
-+
-+/* Amalgamated: #include "common/platform.h" */
-+
-+#ifndef CS_DISABLE_MD5
-+#define CS_DISABLE_MD5 0
-+#endif
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif /* __cplusplus */
-+
-+typedef struct {
-+ uint32_t buf[4];
-+ uint32_t bits[2];
-+ unsigned char in[64];
-+} cs_md5_ctx;
-+
-+void cs_md5_init(cs_md5_ctx *c);
-+void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len);
-+void cs_md5_final(unsigned char *md, cs_md5_ctx *c);
-+
-+#ifdef __cplusplus
-+}
-+#endif /* __cplusplus */
-+
-+#endif /* CS_COMMON_MD5_H_ */
-+#ifdef MG_MODULE_LINES
-+#line 1 "common/cs_sha1.h"
-+#endif
-+/*
-+ * Copyright (c) 2014 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#ifndef CS_COMMON_SHA1_H_
-+#define CS_COMMON_SHA1_H_
-+
-+#ifndef CS_DISABLE_SHA1
-+#define CS_DISABLE_SHA1 0
-+#endif
-+
-+#if !CS_DISABLE_SHA1
-+
-+/* Amalgamated: #include "common/platform.h" */
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif /* __cplusplus */
-+
-+typedef struct {
-+ uint32_t state[5];
-+ uint32_t count[2];
-+ unsigned char buffer[64];
-+} cs_sha1_ctx;
-+
-+void cs_sha1_init(cs_sha1_ctx *);
-+void cs_sha1_update(cs_sha1_ctx *, const unsigned char *data, uint32_t len);
-+void cs_sha1_final(unsigned char digest[20], cs_sha1_ctx *);
-+void cs_hmac_sha1(const unsigned char *key, size_t key_len,
-+ const unsigned char *text, size_t text_len,
-+ unsigned char out[20]);
-+#ifdef __cplusplus
-+}
-+#endif /* __cplusplus */
-+
-+#endif /* CS_DISABLE_SHA1 */
-+
-+#endif /* CS_COMMON_SHA1_H_ */
-+#ifdef MG_MODULE_LINES
- #line 1 "common/cs_time.h"
- #endif
- /*
-@@ -1594,6 +1822,8 @@ void mg_lwip_set_keepalive_params(struct
- #ifndef CS_COMMON_CS_TIME_H_
- #define CS_COMMON_CS_TIME_H_
-
-+#include <time.h>
-+
- /* Amalgamated: #include "common/platform.h" */
-
- #ifdef __cplusplus
-@@ -1603,6 +1833,12 @@ extern "C" {
- /* Sub-second granularity time(). */
- double cs_time(void);
-
-+/*
-+ * Similar to (non-standard) timegm, converts broken-down time into the number
-+ * of seconds since Unix Epoch.
-+ */
-+double cs_timegm(const struct tm *tm);
-+
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
-@@ -1625,7 +1861,7 @@ double cs_time(void);
-
- #ifdef __cplusplus
- extern "C" {
--#endif /* __cplusplus */
-+#endif
-
- /* Describes chunk of memory */
- struct mg_str {
-@@ -1634,15 +1870,21 @@ struct mg_str {
- };
-
- /*
-- * Helper functions for creating mg_str struct from plain C string.
-+ * Helper function for creating mg_str struct from plain C string.
- * `NULL` is allowed and becomes `{NULL, 0}`.
- */
- struct mg_str mg_mk_str(const char *s);
-+
-+/*
-+ * Like `mg_mk_str`, but takes string length explicitly.
-+ */
- struct mg_str mg_mk_str_n(const char *s, size_t len);
-
- /* Macro for initializing mg_str. */
- #define MG_MK_STR(str_literal) \
- { str_literal, sizeof(str_literal) - 1 }
-+#define MG_NULL_STR \
-+ { NULL, 0 }
-
- /*
- * Cross-platform version of `strcmp()` where where first string is
-@@ -1656,13 +1898,38 @@ int mg_vcmp(const struct mg_str *str2, c
- */
- int mg_vcasecmp(const struct mg_str *str2, const char *str1);
-
-+/* Creates a copy of s (heap-allocated). */
- struct mg_str mg_strdup(const struct mg_str s);
-+
-+/*
-+ * Creates a copy of s (heap-allocated).
-+ * Resulting string is NUL-terminated (but NUL is not included in len).
-+ */
-+struct mg_str mg_strdup_nul(const struct mg_str s);
-+
-+/*
-+ * Locates character in a string.
-+ */
-+const char *mg_strchr(const struct mg_str s, int c);
-+
-+/*
-+ * Compare two `mg_str`s; return value is the same as `strcmp`.
-+ */
- int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
-+
-+/*
-+ * Like `mg_strcmp`, but compares at most `n` characters.
-+ */
- int mg_strncmp(const struct mg_str str1, const struct mg_str str2, size_t n);
-
-+/*
-+ * Finds the first occurrence of a substring `needle` in the `haystack`.
-+ */
-+const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
-+
- #ifdef __cplusplus
- }
--#endif /* __cplusplus */
-+#endif
-
- #endif /* CS_COMMON_MG_STR_H_ */
- #ifdef MG_MODULE_LINES
-@@ -1748,103 +2015,15 @@ void mbuf_trim(struct mbuf *);
-
- #endif /* CS_COMMON_MBUF_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "common/sha1.h"
--#endif
--/*
-- * Copyright (c) 2014 Cesanta Software Limited
-- * All rights reserved
-- */
--
--#ifndef CS_COMMON_SHA1_H_
--#define CS_COMMON_SHA1_H_
--
--#ifndef DISABLE_SHA1
--#define DISABLE_SHA1 0
--#endif
--
--#if !DISABLE_SHA1
--
--/* Amalgamated: #include "common/platform.h" */
--
--#ifdef __cplusplus
--extern "C" {
--#endif /* __cplusplus */
--
--typedef struct {
-- uint32_t state[5];
-- uint32_t count[2];
-- unsigned char buffer[64];
--} cs_sha1_ctx;
--
--void cs_sha1_init(cs_sha1_ctx *);
--void cs_sha1_update(cs_sha1_ctx *, const unsigned char *data, uint32_t len);
--void cs_sha1_final(unsigned char digest[20], cs_sha1_ctx *);
--void cs_hmac_sha1(const unsigned char *key, size_t key_len,
-- const unsigned char *text, size_t text_len,
-- unsigned char out[20]);
--#ifdef __cplusplus
--}
--#endif /* __cplusplus */
--
--#endif /* DISABLE_SHA1 */
--
--#endif /* CS_COMMON_SHA1_H_ */
--#ifdef MG_MODULE_LINES
--#line 1 "common/md5.h"
--#endif
--/*
-- * Copyright (c) 2014 Cesanta Software Limited
-- * All rights reserved
-- */
--
--#ifndef CS_COMMON_MD5_H_
--#define CS_COMMON_MD5_H_
--
--/* Amalgamated: #include "common/platform.h" */
--
--#ifndef DISABLE_MD5
--#define DISABLE_MD5 0
--#endif
--
--#ifdef __cplusplus
--extern "C" {
--#endif /* __cplusplus */
--
--typedef struct MD5Context {
-- uint32_t buf[4];
-- uint32_t bits[2];
-- unsigned char in[64];
--} MD5_CTX;
--
--void MD5_Init(MD5_CTX *c);
--void MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len);
--void MD5_Final(unsigned char *md, MD5_CTX *c);
--
--/*
-- * Return stringified MD5 hash for NULL terminated list of pointer/length pairs.
-- * A length should be specified as size_t variable.
-- * Example:
-- *
-- * char buf[33];
-- * cs_md5(buf, "foo", (size_t) 3, "bar", (size_t) 3, NULL);
-- */
--char *cs_md5(char buf[33], ...);
--
--#ifdef __cplusplus
--}
--#endif /* __cplusplus */
--
--#endif /* CS_COMMON_MD5_H_ */
--#ifdef MG_MODULE_LINES
--#line 1 "common/base64.h"
-+#line 1 "common/cs_base64.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
- * All rights reserved
- */
-
--#ifndef CS_COMMON_BASE64_H_
--#define CS_COMMON_BASE64_H_
-+#ifndef CS_COMMON_CS_BASE64_H_
-+#define CS_COMMON_CS_BASE64_H_
-
- #ifndef DISABLE_BASE64
- #define DISABLE_BASE64 0
-@@ -1883,7 +2062,7 @@ int cs_base64_decode(const unsigned char
-
- #endif /* DISABLE_BASE64 */
-
--#endif /* CS_COMMON_BASE64_H_ */
-+#endif /* CS_COMMON_CS_BASE64_H_ */
- #ifdef MG_MODULE_LINES
- #line 1 "common/str_util.h"
- #endif
-@@ -1898,6 +2077,7 @@ int cs_base64_decode(const unsigned char
- #include <stdarg.h>
- #include <stdlib.h>
-
-+/* Amalgamated: #include "common/mg_str.h" */
- /* Amalgamated: #include "common/platform.h" */
-
- #ifndef CS_ENABLE_STRDUP
-@@ -1929,9 +2109,21 @@ int cs_base64_decode(const unsigned char
- extern "C" {
- #endif
-
-+/*
-+ * Equivalent of standard `strnlen()`.
-+ */
- size_t c_strnlen(const char *s, size_t maxlen);
-+
-+/*
-+ * Equivalent of standard `snprintf()`.
-+ */
- int c_snprintf(char *buf, size_t buf_size, const char *format, ...);
-+
-+/*
-+ * Equivalent of standard `vsnprintf()`.
-+ */
- int c_vsnprintf(char *buf, size_t buf_size, const char *format, va_list ap);
-+
- /*
- * Find the first occurrence of find in s, where the search is limited to the
- * first slen characters of s.
-@@ -1952,6 +2144,9 @@ void cs_to_hex(char *to, const unsigned
- void cs_from_hex(char *to, const char *p, size_t len);
-
- #if CS_ENABLE_STRDUP
-+/*
-+ * Equivalent of standard `strdup()`, defined if only `CS_ENABLE_STRDUP` is 1.
-+ */
- char *strdup(const char *src);
- #endif
-
-@@ -1979,12 +2174,14 @@ int mg_casecmp(const char *s1, const cha
- * enough buffer on heap and returns allocated buffer.
- * This is a supposed use case:
- *
-+ * ```c
- * char buf[5], *p = buf;
- * mg_avprintf(&p, sizeof(buf), "%s", "hi there");
- * use_p_somehow(p);
- * if (p != buf) {
- * free(p);
- * }
-+ * ```
- *
- * The purpose of this is to avoid malloc-ing if generated strings are small.
- */
-@@ -1993,6 +2190,55 @@ int mg_asprintf(char **buf, size_t size,
- /* Same as mg_asprintf, but takes varargs list. */
- int mg_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
-
-+/*
-+ * 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.
-+ * The value is stored in a val vector. If the value has a form "x=y", then
-+ * eq_val vector is initialised to point to the "y" part, and val vector length
-+ * is adjusted to point only to "x".
-+ * If the list is just a comma separated list of entries, like "aa,bb,cc" then
-+ * `eq_val` will contain zero-length string.
-+ *
-+ * The purpose of this function is to parse comma separated string without
-+ * any copying/memory allocation.
-+ */
-+const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
-+ struct mg_str *eq_val);
-+
-+/*
-+ * Like `mg_next_comma_list_entry()`, but takes `list` as `struct mg_str`.
-+ */
-+struct mg_str mg_next_comma_list_entry_n(struct mg_str list, struct mg_str *val,
-+ struct mg_str *eq_val);
-+
-+/*
-+ * Matches 0-terminated string (mg_match_prefix) or string with given length
-+ * mg_match_prefix_n against a glob pattern. Glob syntax:
-+ * ```
-+ * - * matches zero or more characters until a slash character /
-+ * - ** matches zero or more characters
-+ * - ? Matches exactly one character which is not a slash /
-+ * - | or , divides alternative patterns
-+ * - any other character matches itself
-+ * ```
-+ * Match is case-insensitive. Return number of bytes matched.
-+ * Examples:
-+ * ```
-+ * mg_match_prefix("a*f", len, "abcdefgh") == 6
-+ * mg_match_prefix("a*f", len, "abcdexgh") == 0
-+ * mg_match_prefix("a*f|de*,xy", len, "defgh") == 5
-+ * mg_match_prefix("?*", len, "abc") == 3
-+ * mg_match_prefix("?*", len, "") == 0
-+ * ```
-+ */
-+size_t mg_match_prefix(const char *pattern, int pattern_len, const char *str);
-+
-+/*
-+ * Like `mg_match_prefix()`, but takes `pattern` and `str` as `struct mg_str`.
-+ */
-+size_t mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str);
-+
- #ifdef __cplusplus
- }
- #endif
-@@ -2754,7 +3000,7 @@ struct { \
-
- #endif /* !_SYS_QUEUE_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/features.h"
-+#line 1 "mongoose/src/mg_features.h"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -2856,14 +3102,14 @@ struct { \
- #define MG_ENABLE_IPV6 0
- #endif
-
--#ifndef MG_ENABLE_JAVASCRIPT
--#define MG_ENABLE_JAVASCRIPT 0
--#endif
--
- #ifndef MG_ENABLE_MQTT
- #define MG_ENABLE_MQTT 1
- #endif
-
-+#ifndef MG_ENABLE_SOCKS
-+#define MG_ENABLE_SOCKS 0
-+#endif
-+
- #ifndef MG_ENABLE_MQTT_BROKER
- #define MG_ENABLE_MQTT_BROKER 0
- #endif
-@@ -2911,10 +3157,6 @@ struct { \
- (CS_PLATFORM == CS_P_WINDOWS || CS_PLATFORM == CS_P_UNIX)
- #endif
-
--#ifndef MG_ENABLE_TUN
--#define MG_ENABLE_TUN MG_ENABLE_HTTP_WEBSOCKET
--#endif
--
- #ifndef MG_ENABLE_SNTP
- #define MG_ENABLE_SNTP 0
- #endif
-@@ -2923,9 +3165,21 @@ struct { \
- #define MG_ENABLE_EXTRA_ERRORS_DESC 0
- #endif
-
-+#ifndef MG_ENABLE_CALLBACK_USERDATA
-+#define MG_ENABLE_CALLBACK_USERDATA 0
-+#endif
-+
-+#if MG_ENABLE_CALLBACK_USERDATA
-+#define MG_UD_ARG(ud) , ud
-+#define MG_CB(cb, ud) cb, ud
-+#else
-+#define MG_UD_ARG(ud)
-+#define MG_CB(cb, ud) cb
-+#endif
-+
- #endif /* CS_MONGOOSE_SRC_FEATURES_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net_if.h"
-+#line 1 "mongoose/src/mg_net_if.h"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -2962,7 +3216,7 @@ struct mg_iface_vtable;
- struct mg_iface {
- struct mg_mgr *mgr;
- void *data; /* Implementation-specific data */
-- struct mg_iface_vtable *vtable;
-+ const struct mg_iface_vtable *vtable;
- };
-
- struct mg_iface_vtable {
-@@ -3001,11 +3255,11 @@ struct mg_iface_vtable {
- union socket_address *sa);
- };
-
--extern struct mg_iface_vtable *mg_ifaces[];
-+extern const struct mg_iface_vtable *mg_ifaces[];
- extern int mg_num_ifaces;
-
- /* Creates a new interface instance. */
--struct mg_iface *mg_if_create_iface(struct mg_iface_vtable *vtable,
-+struct mg_iface *mg_if_create_iface(const struct mg_iface_vtable *vtable,
- struct mg_mgr *mgr);
-
- /*
-@@ -3013,7 +3267,7 @@ struct mg_iface *mg_if_create_iface(stru
- * interface `from`, exclusive. Returns NULL if none is found.
- */
- struct mg_iface *mg_find_iface(struct mg_mgr *mgr,
-- struct mg_iface_vtable *vtable,
-+ const struct mg_iface_vtable *vtable,
- struct mg_iface *from);
- /*
- * Deliver a new TCP connection. Returns NULL in case on error (unable to
-@@ -3058,7 +3312,7 @@ void mg_if_timer(struct mg_connection *c
-
- #endif /* CS_MONGOOSE_SRC_NET_IF_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/ssl_if.h"
-+#line 1 "mongoose/src/mg_ssl_if.h"
- #endif
- /*
- * Copyright (c) 2014-2016 Cesanta Software Limited
-@@ -3091,6 +3345,9 @@ struct mg_ssl_if_conn_params {
- const char *key;
- const char *ca_cert;
- const char *server_name;
-+ const char *cipher_suites;
-+ const char *psk_identity;
-+ const char *psk_key;
- };
-
- enum mg_ssl_if_result mg_ssl_if_conn_init(
-@@ -3098,6 +3355,7 @@ enum mg_ssl_if_result mg_ssl_if_conn_ini
- const char **err_msg);
- enum mg_ssl_if_result mg_ssl_if_conn_accept(struct mg_connection *nc,
- struct mg_connection *lc);
-+void mg_ssl_if_conn_close_notify(struct mg_connection *nc);
- void mg_ssl_if_conn_free(struct mg_connection *nc);
-
- enum mg_ssl_if_result mg_ssl_if_handshake(struct mg_connection *nc);
-@@ -3112,7 +3370,7 @@ int mg_ssl_if_write(struct mg_connection
-
- #endif /* CS_MONGOOSE_SRC_SSL_IF_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/net.h"
-+#line 1 "mongoose/src/mg_net.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -3144,13 +3402,8 @@ int mg_ssl_if_write(struct mg_connection
- #ifndef CS_MONGOOSE_SRC_NET_H_
- #define CS_MONGOOSE_SRC_NET_H_
-
--#if MG_ENABLE_JAVASCRIPT
--#define EXCLUDE_COMMON
--#include <v7.h>
--#endif
--
--/* Amalgamated: #include "mongoose/src/common.h" */
--/* Amalgamated: #include "mongoose/src/net_if.h" */
-+/* Amalgamated: #include "mg_common.h" */
-+/* Amalgamated: #include "mg_net_if.h" */
- /* Amalgamated: #include "common/mbuf.h" */
-
- #ifndef MG_VPRINTF_BUFFER_SIZE
-@@ -3186,13 +3439,13 @@ struct mg_connection;
- * Mongoose calls the event handler, passing the events defined below.
- */
- typedef void (*mg_event_handler_t)(struct mg_connection *nc, int ev,
-- void *ev_data);
-+ void *ev_data MG_UD_ARG(void *user_data));
-
- /* Events. Meaning of event parameter (evp) is given in the comment. */
- #define MG_EV_POLL 0 /* Sent to each connection on each mg_mgr_poll() call */
- #define MG_EV_ACCEPT 1 /* New connection accepted. union socket_address * */
- #define MG_EV_CONNECT 2 /* connect() succeeded or failed. int * */
--#define MG_EV_RECV 3 /* Data has benn received. int *num_bytes */
-+#define MG_EV_RECV 3 /* Data has been received. int *num_bytes */
- #define MG_EV_SEND 4 /* Data has been written to a socket. int *num_bytes */
- #define MG_EV_CLOSE 5 /* Connection is closed. NULL */
- #define MG_EV_TIMER 6 /* now >= conn->ev_timer_time. double * */
-@@ -3211,9 +3464,7 @@ struct mg_mgr {
- void *user_data; /* User data */
- int num_ifaces;
- struct mg_iface **ifaces; /* network interfaces */
--#if MG_ENABLE_JAVASCRIPT
-- struct v7 *v7;
--#endif
-+ const char *nameserver; /* DNS server to use */
- };
-
- /*
-@@ -3247,8 +3498,8 @@ struct mg_connection {
- * void pointers, since some archs might have fat pointers for functions.
- */
- mg_event_handler_t f;
-- } priv_1; /* Used by mg_enable_multithreading() */
-- void *priv_2; /* Used by mg_enable_multithreading() */
-+ } priv_1;
-+ void *priv_2;
- void *mgr_data; /* Implementation-specific event manager's data. */
- struct mg_iface *iface;
- unsigned long flags;
-@@ -3264,12 +3515,11 @@ struct mg_connection {
- #define MG_F_IS_WEBSOCKET (1 << 8) /* Websocket specific */
-
- /* Flags that are settable by user */
--#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
--#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
--#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
--#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */
--#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
--#define MG_F_TUN_DO_NOT_RECONNECT (1 << 15) /* Don't reconnect tunnel */
-+#define MG_F_SEND_AND_CLOSE (1 << 10) /* Push remaining data and close */
-+#define MG_F_CLOSE_IMMEDIATELY (1 << 11) /* Disconnect */
-+#define MG_F_WEBSOCKET_NO_DEFRAG (1 << 12) /* Websocket specific */
-+#define MG_F_DELETE_CHUNK (1 << 13) /* HTTP specific */
-+#define MG_F_ENABLE_BROADCAST (1 << 14) /* Allow broadcast address usage */
-
- #define MG_F_USER_1 (1 << 20) /* Flags left for application */
- #define MG_F_USER_2 (1 << 21)
-@@ -3305,9 +3555,10 @@ void mg_mgr_init(struct mg_mgr *mgr, voi
- * `num_ifaces` pointers it contains will be reclaimed by `mg_mgr_free`.
- */
- struct mg_mgr_init_opts {
-- struct mg_iface_vtable *main_iface;
-+ const struct mg_iface_vtable *main_iface;
- int num_ifaces;
-- struct mg_iface_vtable **ifaces;
-+ const struct mg_iface_vtable **ifaces;
-+ const char *nameserver;
- };
-
- /*
-@@ -3349,7 +3600,8 @@ time_t mg_mgr_poll(struct mg_mgr *, int
- * be passed as the `ev_data` pointer. Maximum message size is capped
- * by `MG_CTL_MSG_MESSAGE_SIZE` which is set to 8192 bytes.
- */
--void mg_broadcast(struct mg_mgr *, mg_event_handler_t func, void *, size_t);
-+void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data,
-+ size_t len);
- #endif
-
- /*
-@@ -3365,7 +3617,7 @@ void mg_broadcast(struct mg_mgr *, mg_ev
- * }
- * ```
- */
--struct mg_connection *mg_next(struct mg_mgr *, struct mg_connection *);
-+struct mg_connection *mg_next(struct mg_mgr *mgr, struct mg_connection *c);
-
- /*
- * Optional parameters to `mg_add_sock_opt()`.
-@@ -3386,7 +3638,9 @@ struct mg_add_sock_opts {
- *
- * For more options see the `mg_add_sock_opt` variant.
- */
--struct mg_connection *mg_add_sock(struct mg_mgr *, sock_t, mg_event_handler_t);
-+struct mg_connection *mg_add_sock(struct mg_mgr *mgr, sock_t sock,
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data));
-
- /*
- * Creates a connection, associates it with the given socket and event handler
-@@ -3394,9 +3648,10 @@ struct mg_connection *mg_add_sock(struct
- *
- * See the `mg_add_sock_opts` structure for a description of the options.
- */
--struct mg_connection *mg_add_sock_opt(struct mg_mgr *, sock_t,
-- mg_event_handler_t,
-- struct mg_add_sock_opts);
-+struct mg_connection *mg_add_sock_opt(struct mg_mgr *mgr, sock_t sock,
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data),
-+ struct mg_add_sock_opts opts);
-
- /*
- * Optional parameters to `mg_bind_opt()`.
-@@ -3410,15 +3665,30 @@ struct mg_bind_opts {
- const char **error_string; /* Placeholder for the error string */
- struct mg_iface *iface; /* Interface instance */
- #if MG_ENABLE_SSL
-- /* SSL settings. */
-- const char *ssl_cert; /* Server certificate to present to clients
-- * Or client certificate to present to tunnel
-- * dispatcher. */
-- const char *ssl_key; /* Private key corresponding to the certificate.
-- If ssl_cert is set but ssl_key is not, ssl_cert
-- is used. */
-- const char *ssl_ca_cert; /* CA bundle used to verify client certificates or
-- * tunnel dispatchers. */
-+ /*
-+ * SSL settings.
-+ *
-+ * Server certificate to present to clients or client certificate to
-+ * present to tunnel dispatcher (for tunneled connections).
-+ */
-+ const char *ssl_cert;
-+ /* Private key corresponding to the certificate. If ssl_cert is set but
-+ * ssl_key is not, ssl_cert is used. */
-+ const char *ssl_key;
-+ /* CA bundle used to verify client certificates or tunnel dispatchers. */
-+ const char *ssl_ca_cert;
-+ /* Colon-delimited list of acceptable cipher suites.
-+ * Names depend on the library used, for example:
-+ *
-+ * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
-+ * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
-+ * (mbedTLS)
-+ *
-+ * For OpenSSL the list can be obtained by running "openssl ciphers".
-+ * For mbedTLS, names can be found in library/ssl_ciphersuites.c
-+ * If NULL, a reasonable default is used.
-+ */
-+ const char *ssl_cipher_suites;
- #endif
- };
-
-@@ -3427,8 +3697,9 @@ struct mg_bind_opts {
- *
- * See `mg_bind_opt` for full documentation.
- */
--struct mg_connection *mg_bind(struct mg_mgr *, const char *,
-- mg_event_handler_t);
-+struct mg_connection *mg_bind(struct mg_mgr *mgr, const char *address,
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data));
- /*
- * Creates a listening connection.
- *
-@@ -3437,7 +3708,7 @@ struct mg_connection *mg_bind(struct mg_
- * `address` can be just a port number, e.g. `:8000`. To bind to a specific
- * interface, an IP address can be specified, e.g. `1.2.3.4:8000`. By default,
- * a TCP connection is created. To create UDP connection, prepend `udp://`
-- * prefix, e.g. `udp://:8000`. To summarize, `address` paramer has following
-+ * prefix, e.g. `udp://:8000`. To summarize, `address` parameter has following
- * format: `[PROTO://][IP_ADDRESS]:PORT`, where `PROTO` could be `tcp` or
- * `udp`.
- *
-@@ -3448,7 +3719,8 @@ struct mg_connection *mg_bind(struct mg_
- * NOTE: The connection remains owned by the manager, do not free().
- */
- struct mg_connection *mg_bind_opt(struct mg_mgr *mgr, const char *address,
-- mg_event_handler_t handler,
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data),
- struct mg_bind_opts opts);
-
- /* Optional parameters to `mg_connect_opt()` */
-@@ -3457,16 +3729,35 @@ struct mg_connect_opts {
- unsigned int flags; /* Extra connection flags */
- const char **error_string; /* Placeholder for the error string */
- struct mg_iface *iface; /* Interface instance */
-+ const char *nameserver; /* DNS server to use, NULL for default */
- #if MG_ENABLE_SSL
-- /* SSL settings. */
-- const char *ssl_cert; /* Client certificate to present to the server */
-- const char *ssl_key; /* Private key corresponding to the certificate.
-- If ssl_cert is set but ssl_key is not, ssl_cert
-- is used. */
-- const char *ssl_ca_cert; /* Verify server certificate using this CA bundle.
-- If set to "*", then SSL is enabled but no cert
-- verification is performed. */
--
-+ /*
-+ * SSL settings.
-+ * Client certificate to present to the server.
-+ */
-+ const char *ssl_cert;
-+ /*
-+ * Private key corresponding to the certificate.
-+ * If ssl_cert is set but ssl_key is not, ssl_cert is used.
-+ */
-+ const char *ssl_key;
-+ /*
-+ * Verify server certificate using this CA bundle. If set to "*", then SSL
-+ * is enabled but no cert verification is performed.
-+ */
-+ const char *ssl_ca_cert;
-+ /* Colon-delimited list of acceptable cipher suites.
-+ * Names depend on the library used, for example:
-+ *
-+ * ECDH-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256 (OpenSSL)
-+ * TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256
-+ * (mbedTLS)
-+ *
-+ * For OpenSSL the list can be obtained by running "openssl ciphers".
-+ * For mbedTLS, names can be found in library/ssl_ciphersuites.c
-+ * If NULL, a reasonable default is used.
-+ */
-+ const char *ssl_cipher_suites;
- /*
- * Server name verification. If ssl_ca_cert is set and the certificate has
- * passed verification, its subject will be verified against this string.
-@@ -3475,6 +3766,15 @@ struct mg_connect_opts {
- * name verification.
- */
- const char *ssl_server_name;
-+ /*
-+ * PSK identity and key. Identity is a NUL-terminated string and key is a hex
-+ * string. Key must be either 16 or 32 bytes (32 or 64 hex digits) for AES-128
-+ * or AES-256 respectively.
-+ * Note: Default list of cipher suites does not include PSK suites, if you
-+ * want to use PSK you will need to set ssl_cipher_suites as well.
-+ */
-+ const char *ssl_psk_identity;
-+ const char *ssl_psk_key;
- #endif
- };
-
-@@ -3484,7 +3784,8 @@ struct mg_connect_opts {
- * See `mg_connect_opt()` for full documentation.
- */
- struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *address,
-- mg_event_handler_t handler);
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data));
-
- /*
- * Connects to a remote host.
-@@ -3535,7 +3836,8 @@ struct mg_connection *mg_connect(struct
- * ```
- */
- struct mg_connection *mg_connect_opt(struct mg_mgr *mgr, const char *address,
-- mg_event_handler_t handler,
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data),
- struct mg_connect_opts opts);
-
- #if MG_ENABLE_SSL && MG_NET_IF != MG_NET_IF_SIMPLELINK
-@@ -3616,7 +3918,7 @@ int mg_resolve(const char *domain_name,
- * is to allow all access. On each request the full list is traversed,
- * and the last match wins. Example:
- *
-- * `-0.0.0.0/0,+192.168/16` - deny all acccesses, only allow 192.168/16 subnet
-+ * `-0.0.0.0/0,+192.168/16` - deny all accesses, only allow 192.168/16 subnet
- *
- * To learn more about subnet masks, see this
- * link:https://en.wikipedia.org/wiki/Subnetwork[Wikipedia page on Subnetwork].
-@@ -3626,35 +3928,6 @@ int mg_resolve(const char *domain_name,
- int mg_check_ip_acl(const char *acl, uint32_t remote_ip);
-
- /*
-- * Optional parameters for mg_enable_multithreading_opt()
-- */
--struct mg_multithreading_opts {
-- int poll_timeout; /* Polling interval */
--};
--
--/*
-- * Enables multi-threaded handling for the given listening connection `nc`.
-- * For each accepted connection, Mongoose will create a separate thread
-- * and run an event handler in that thread. Thus, if an event handler is doing
-- * a blocking call or some long computation, it will not slow down
-- * other connections.
-- */
--void mg_enable_multithreading(struct mg_connection *nc);
--void mg_enable_multithreading_opt(struct mg_connection *nc,
-- struct mg_multithreading_opts opts);
--
--#if MG_ENABLE_JAVASCRIPT
--/*
-- * Enables server-side JavaScript scripting.
-- * Requires a `-DMG_ENABLE_JAVASCRIPT` compilation flag and V7 engine sources.
-- * V7 instance must not be destroyed during manager's lifetime.
-- * Returns a V7 error.
-- */
--enum v7_err mg_enable_javascript(struct mg_mgr *m, struct v7 *v7,
-- const char *init_js_file_name);
--#endif
--
--/*
- * Schedules an MG_EV_TIMER event to be delivered at `timestamp` time.
- * `timestamp` is UNIX time (the number of seconds since Epoch). It is
- * `double` instead of `time_t` to allow for sub-second precision.
-@@ -3691,7 +3964,7 @@ double mg_time(void);
-
- #endif /* CS_MONGOOSE_SRC_NET_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/uri.h"
-+#line 1 "mongoose/src/mg_uri.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -3705,7 +3978,7 @@ double mg_time(void);
- #ifndef CS_MONGOOSE_SRC_URI_H_
- #define CS_MONGOOSE_SRC_URI_H_
-
--/* Amalgamated: #include "mongoose/src/net.h" */
-+/* Amalgamated: #include "mg_net.h" */
-
- #ifdef __cplusplus
- extern "C" {
-@@ -3734,11 +4007,26 @@ extern "C" {
- *
- * Returns 0 on success, -1 on error.
- */
--int mg_parse_uri(struct mg_str uri, struct mg_str *scheme,
-+int mg_parse_uri(const struct mg_str uri, struct mg_str *scheme,
- struct mg_str *user_info, struct mg_str *host,
- unsigned int *port, struct mg_str *path, struct mg_str *query,
- struct mg_str *fragment);
-
-+/*
-+ * Assemble URI from parts. Any of the inputs can be NULL or zero-length mg_str.
-+ *
-+ * If normalize_path is true, path is normalized by resolving relative refs.
-+ *
-+ * Result is a heap-allocated string (uri->p must be free()d after use).
-+ *
-+ * Returns 0 on success, -1 on error.
-+ */
-+int mg_assemble_uri(const struct mg_str *scheme, const struct mg_str *user_info,
-+ const struct mg_str *host, unsigned int port,
-+ const struct mg_str *path, const struct mg_str *query,
-+ const struct mg_str *fragment, int normalize_path,
-+ struct mg_str *uri);
-+
- int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out);
-
- #ifdef __cplusplus
-@@ -3746,7 +4034,7 @@ int mg_normalize_uri_path(const struct m
- #endif /* __cplusplus */
- #endif /* CS_MONGOOSE_SRC_URI_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/util.h"
-+#line 1 "mongoose/src/mg_util.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -3762,15 +4050,19 @@ int mg_normalize_uri_path(const struct m
-
- #include <stdio.h>
-
--/* Amalgamated: #include "mongoose/src/common.h" */
--/* Amalgamated: #include "mongoose/src/net_if.h" */
-+/* Amalgamated: #include "mg_common.h" */
-+/* Amalgamated: #include "mg_net_if.h" */
-
- #ifdef __cplusplus
- extern "C" {
- #endif /* __cplusplus */
-
--#ifndef MAX_PATH_SIZE
--#define MAX_PATH_SIZE 500
-+#ifndef MG_MAX_PATH
-+#ifdef PATH_MAX
-+#define MG_MAX_PATH PATH_MAX
-+#else
-+#define MG_MAX_PATH 256
-+#endif
- #endif
-
- /*
-@@ -3831,6 +4123,21 @@ FILE *mg_fopen(const char *path, const c
- * Return value is the same as for the `open()` syscall.
- */
- int mg_open(const char *path, int flag, int mode);
-+
-+/*
-+ * Reads data from the given file stream.
-+ *
-+ * Return value is a number of bytes readen.
-+ */
-+size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f);
-+
-+/*
-+ * Writes data to the given file stream.
-+ *
-+ * Return value is a number of bytes wtitten.
-+ */
-+size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f);
-+
- #endif /* MG_ENABLE_FILESYSTEM */
-
- #if MG_ENABLE_THREADS
-@@ -3860,9 +4167,10 @@ void mg_set_close_on_exec(sock_t);
- *
- * If both port number and IP address are printed, they are separated by `:`.
- * If compiled with `-DMG_ENABLE_IPV6`, IPv6 addresses are supported.
-+ * Return length of the stringified address.
- */
--void mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len,
-- int flags);
-+int mg_conn_addr_to_str(struct mg_connection *c, char *buf, size_t len,
-+ int flags);
- #if MG_NET_IF == MG_NET_IF_SOCKET
- /* Legacy interface. */
- void mg_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
-@@ -3873,8 +4181,8 @@ void mg_sock_to_str(sock_t sock, char *b
- *
- * `flags` is MG_SOCK_STRINGIFY_IP and/or MG_SOCK_STRINGIFY_PORT.
- */
--void mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
-- int flags);
-+int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
-+ int flags);
-
- #if MG_ENABLE_HEXDUMP
- /*
-@@ -3887,6 +4195,9 @@ void mg_sock_addr_to_str(const union soc
- */
- int mg_hexdump(const void *buf, int len, char *dst, int dst_len);
-
-+/* Same as mg_hexdump, but with output going to file instead of a buffer. */
-+void mg_hexdumpf(FILE *fp, const void *buf, int len);
-+
- /*
- * Generates human-readable hexdump of the data sent or received by the
- * connection. `path` is a file name where hexdump should be written.
-@@ -3904,32 +4215,6 @@ void mg_hexdump_connection(struct mg_con
- int mg_is_big_endian(void);
-
- /*
-- * 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.
-- * The value is stored in a val vector. If the value has a form "x=y", then
-- * eq_val vector is initialised to point to the "y" part, and val vector length
-- * is adjusted to point only to "x".
-- * If the list is just a comma separated list of entries, like "aa,bb,cc" then
-- * `eq_val` will contain zero-length string.
-- *
-- * The purpose of this function is to parse comma separated string without
-- * any copying/memory allocation.
-- */
--const char *mg_next_comma_list_entry(const char *list, struct mg_str *val,
-- struct mg_str *eq_val);
--
--/*
-- * Matches 0-terminated string (mg_match_prefix) or string with given length
-- * mg_match_prefix_n against a glob pattern.
-- *
-- * Match is case-insensitive. Returns number of bytes matched, or -1 if no
-- * match.
-- */
--int mg_match_prefix(const char *pattern, int pattern_len, const char *str);
--int mg_match_prefix_n(const struct mg_str pattern, const struct mg_str str);
--
--/*
- * Use with cs_base64_init/update/finish in order to write out base64 in chunks.
- */
- void mg_mbuf_append_base64_putc(char ch, void *user_data);
-@@ -3944,14 +4229,23 @@ void mg_mbuf_append_base64(struct mbuf *
- * If pass is NULL, then user is expected to contain the credentials pair
- * already encoded as `user:pass`.
- */
--void mg_basic_auth_header(const char *user, const char *pass, struct mbuf *buf);
-+void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass,
-+ struct mbuf *buf);
-+
-+/*
-+ * URL-escape the specified string.
-+ * All non-printable characters are escaped, plus `._-$,;~()/`.
-+ * Input need not be NUL-terminated, but the returned string is.
-+ * Returned string is heap-allocated and must be free()'d.
-+ */
-+struct mg_str mg_url_encode(const struct mg_str src);
-
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
- #endif /* CS_MONGOOSE_SRC_UTIL_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http.h"
-+#line 1 "mongoose/src/mg_http.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -3967,7 +4261,7 @@ void mg_basic_auth_header(const char *us
-
- #if MG_ENABLE_HTTP
-
--/* Amalgamated: #include "mongoose/src/net.h" */
-+/* Amalgamated: #include "mg_net.h" */
- /* Amalgamated: #include "common/mg_str.h" */
-
- #ifdef __cplusplus
-@@ -3982,14 +4276,6 @@ extern "C" {
- #define MG_MAX_HTTP_REQUEST_SIZE 1024
- #endif
-
--#ifndef MG_MAX_PATH
--#ifdef PATH_MAX
--#define MG_MAX_PATH PATH_MAX
--#else
--#define MG_MAX_PATH 256
--#endif
--#endif
--
- #ifndef MG_MAX_HTTP_SEND_MBUF
- #define MG_MAX_HTTP_SEND_MBUF 1024
- #endif
-@@ -4001,6 +4287,7 @@ extern "C" {
- /* HTTP message */
- struct http_message {
- struct mg_str message; /* Whole message: request line + headers + body */
-+ struct mg_str body; /* Message body. 0-length for requests with no body */
-
- /* HTTP Request line (or HTTP response line) */
- struct mg_str method; /* "GET" */
-@@ -4024,9 +4311,6 @@ struct http_message {
- /* Headers */
- struct mg_str header_names[MG_MAX_HTTP_HEADERS];
- struct mg_str header_values[MG_MAX_HTTP_HEADERS];
--
-- /* Message body */
-- struct mg_str body; /* Zero-length for requests with no body */
- };
-
- #if MG_ENABLE_HTTP_WEBSOCKET
-@@ -4168,6 +4452,17 @@ void mg_send_websocket_handshake3(struct
- const char *host, const char *protocol,
- const char *extra_headers, const char *user,
- const char *pass);
-+
-+/* Same as mg_send_websocket_handshake3 but with strings not necessarily
-+ * NUL-temrinated */
-+void mg_send_websocket_handshake3v(struct mg_connection *nc,
-+ const struct mg_str path,
-+ const struct mg_str host,
-+ const struct mg_str protocol,
-+ const struct mg_str extra_headers,
-+ const struct mg_str user,
-+ const struct mg_str pass);
-+
- /*
- * Helper function that creates an outbound WebSocket connection.
- *
-@@ -4189,7 +4484,8 @@ void mg_send_websocket_handshake3(struct
- * ```
- */
- struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
-- mg_event_handler_t event_handler,
-+ MG_CB(mg_event_handler_t event_handler,
-+ void *user_data),
- const char *url, const char *protocol,
- const char *extra_headers);
-
-@@ -4199,11 +4495,10 @@ struct mg_connection *mg_connect_ws(stru
- * Mostly identical to `mg_connect_ws`, but allows to provide extra parameters
- * (for example, SSL parameters)
- */
--struct mg_connection *mg_connect_ws_opt(struct mg_mgr *mgr,
-- mg_event_handler_t ev_handler,
-- struct mg_connect_opts opts,
-- const char *url, const char *protocol,
-- const char *extra_headers);
-+struct mg_connection *mg_connect_ws_opt(
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ struct mg_connect_opts opts, const char *url, const char *protocol,
-+ const char *extra_headers);
-
- /*
- * Send WebSocket frame to the remote end.
-@@ -4227,9 +4522,8 @@ void mg_send_websocket_frame(struct mg_c
- const void *data, size_t data_len);
-
- /*
-- * Sends multiple websocket frames.
-- *
-- * Like `mg_send_websocket_frame()`, but composes a frame from multiple buffers.
-+ * Like `mg_send_websocket_frame()`, but composes a single frame from multiple
-+ * buffers.
- */
- void mg_send_websocket_framev(struct mg_connection *nc, int op_and_flags,
- const struct mg_str *strings, int num_strings);
-@@ -4274,12 +4568,42 @@ void mg_printf_websocket_frame(struct mg
- * (`dst`, `dst_len`). If `is_form_url_encoded` is non-zero, then
- * `+` character is decoded as a blank space character. This function
- * guarantees to NUL-terminate the destination. If destination is too small,
-- * then the source string is partially decoded and `-1` is returned. Otherwise,
-+ * then the source string is partially decoded and `-1` is returned.
-+ *Otherwise,
- * a length of the decoded string is returned, not counting final NUL.
- */
- int mg_url_decode(const char *src, int src_len, char *dst, int dst_len,
- int is_form_url_encoded);
-
-+extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
-+ const size_t *msg_lens, uint8_t *digest);
-+extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
-+ const size_t *msg_lens, uint8_t *digest);
-+
-+/*
-+ * Flags for `mg_http_is_authorized()`.
-+ */
-+#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0)
-+#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
-+#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
-+
-+/*
-+ * Checks whether an http request is authorized. `domain` is the authentication
-+ * realm, `passwords_file` is a htdigest file (can be created e.g. with
-+ * `htdigest` utility). If either `domain` or `passwords_file` is NULL, this
-+ * function always returns 1; otherwise checks the authentication in the
-+ * http request and returns 1 only if there is a match; 0 otherwise.
-+ */
-+int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
-+ const char *domain, const char *passwords_file,
-+ int flags);
-+
-+/*
-+ * Sends 401 Unauthorized response.
-+ */
-+void mg_http_send_digest_auth_request(struct mg_connection *c,
-+ const char *domain);
-+
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
-@@ -4288,7 +4612,7 @@ int mg_url_decode(const char *src, int s
-
- #endif /* CS_MONGOOSE_SRC_HTTP_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http_server.h"
-+#line 1 "mongoose/src/mg_http_server.h"
- #endif
- /*
- * === Server API reference
-@@ -4323,21 +4647,42 @@ struct mg_str *mg_get_http_header(struct
-
- /*
- * Parses the HTTP header `hdr`. Finds variable `var_name` and stores its value
-- * in the buffer `buf`, `buf_size`. Returns 0 if variable not found, non-zero
-- * otherwise.
-+ * in the buffer `*buf`, `buf_size`. If the buffer size is not enough,
-+ * allocates a buffer of required size and writes it to `*buf`, similar to
-+ * asprintf(). The caller should always check whether the buffer was updated,
-+ * and free it if so.
- *
- * This function is supposed to parse cookies, authentication headers, etc.
- * Example (error handling omitted):
- *
-- * char user[20];
-+ * char user_buf[20];
-+ * char *user = user_buf;
- * struct mg_str *hdr = mg_get_http_header(hm, "Authorization");
-- * mg_http_parse_header(hdr, "username", user, sizeof(user));
-+ * mg_http_parse_header2(hdr, "username", &user, sizeof(user_buf));
-+ * // ... do something useful with user
-+ * if (user != user_buf) {
-+ * free(user);
-+ * }
- *
-- * Returns the length of the variable's value. If buffer is not large enough,
-- * or variable not found, 0 is returned.
-+ * Returns the length of the variable's value. If variable is not found, 0 is
-+ * returned.
-+ */
-+int mg_http_parse_header2(struct mg_str *hdr, const char *var_name, char **buf,
-+ size_t buf_size);
-+
-+/*
-+ * DEPRECATED: use mg_http_parse_header2() instead.
-+ *
-+ * Same as mg_http_parse_header2(), but takes buffer as a `char *` (instead of
-+ * `char **`), and thus it cannot allocate a new buffer if the provided one
-+ * is not enough, and just returns 0 in that case.
- */
- int mg_http_parse_header(struct mg_str *hdr, const char *var_name, char *buf,
-- size_t buf_size);
-+ size_t buf_size)
-+#ifdef __GNUC__
-+ __attribute__((deprecated));
-+#endif
-+;
-
- /*
- * Gets and parses the Authorization: Basic header
-@@ -4403,7 +4748,8 @@ size_t mg_parse_multipart(const char *bu
- * Fetches a variable `name` from a `buf` into a buffer specified by `dst`,
- * `dst_len`. The destination is always zero-terminated. Returns the length of
- * a fetched variable. If not found, 0 is returned. `buf` must be valid
-- * url-encoded buffer. If destination is too small, `-1` is returned.
-+ * url-encoded buffer. If destination is too small or an error occured,
-+ * negative number is returned.
- */
- int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
- size_t dst_len);
-@@ -4654,7 +5000,8 @@ typedef struct mg_str (*mg_fu_fname_fn)(
- * ```
- */
- void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
-- mg_fu_fname_fn local_name_fn);
-+ mg_fu_fname_fn local_name_fn
-+ MG_UD_ARG(void *user_data));
- #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
- #endif /* MG_ENABLE_FILESYSTEM */
-
-@@ -4672,7 +5019,7 @@ void mg_file_upload_handler(struct mg_co
- * nc->flags |= MG_F_SEND_AND_CLOSE;
- * }
- *
-- * static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) {
-+ * static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) {
- * (void) ev; (void) ev_data;
- * mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n[I am Hello2]");
- * nc->flags |= MG_F_SEND_AND_CLOSE;
-@@ -4686,7 +5033,20 @@ void mg_file_upload_handler(struct mg_co
- * ```
- */
- void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path,
-- mg_event_handler_t handler);
-+ MG_CB(mg_event_handler_t handler,
-+ void *user_data));
-+
-+struct mg_http_endpoint_opts {
-+ void *user_data;
-+ /* Authorization domain (realm) */
-+ const char *auth_domain;
-+ const char *auth_file;
-+};
-+
-+void mg_register_http_endpoint_opt(struct mg_connection *nc,
-+ const char *uri_path,
-+ mg_event_handler_t handler,
-+ struct mg_http_endpoint_opts opts);
-
- /*
- * Authenticates a HTTP request against an opened password file.
-@@ -4696,10 +5056,22 @@ int mg_http_check_digest_auth(struct htt
- FILE *fp);
-
- /*
-+ * Authenticates given response params against an opened password file.
-+ * Returns 1 if authenticated, 0 otherwise.
-+ *
-+ * It's used by mg_http_check_digest_auth().
-+ */
-+int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
-+ struct mg_str username, struct mg_str cnonce,
-+ struct mg_str response, struct mg_str qop,
-+ struct mg_str nc, struct mg_str nonce,
-+ struct mg_str auth_domain, FILE *fp);
-+
-+/*
- * Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
- * This function sends the buffer size as hex number + newline first, then
- * the buffer itself, then the newline. For example,
-- * `mg_send_http_chunk(nc, "foo", 3)` whill append the `3\r\nfoo\r\n` string
-+ * `mg_send_http_chunk(nc, "foo", 3)` will append the `3\r\nfoo\r\n` string
- * to the `nc->send_mbuf` output IO buffer.
- *
- * NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to
-@@ -4724,7 +5096,7 @@ void mg_printf_http_chunk(struct mg_conn
- /*
- * Sends the response status line.
- * If `extra_headers` is not NULL, then `extra_headers` are also sent
-- * after the reponse line. `extra_headers` must NOT end end with new line.
-+ * after the response line. `extra_headers` must NOT end end with new line.
- * Example:
- *
- * mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
-@@ -4748,7 +5120,7 @@ void mg_http_send_error(struct mg_connec
- * `status_code` should be either 301 or 302 and `location` point to the
- * new location.
- * If `extra_headers` is not empty, then `extra_headers` are also sent
-- * after the reponse line. `extra_headers` must NOT end end with new line.
-+ * after the response line. `extra_headers` must NOT end end with new line.
- *
- * Example:
- *
-@@ -4804,7 +5176,7 @@ void mg_http_reverse_proxy(struct mg_con
-
- #endif /* CS_MONGOOSE_SRC_HTTP_SERVER_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/http_client.h"
-+#line 1 "mongoose/src/mg_http_client.h"
- #endif
- /*
- * === Client API reference
-@@ -4841,11 +5213,10 @@ extern "C" {
- * "var_1=value_1&var_2=value_2");
- * ```
- */
--struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
-- mg_event_handler_t event_handler,
-- const char *url,
-- const char *extra_headers,
-- const char *post_data);
-+struct mg_connection *mg_connect_http(
-+ struct mg_mgr *mgr,
-+ MG_CB(mg_event_handler_t event_handler, void *user_data), const char *url,
-+ const char *extra_headers, const char *post_data);
-
- /*
- * Helper function that creates an outbound HTTP connection.
-@@ -4854,25 +5225,23 @@ struct mg_connection *mg_connect_http(st
- *parameters
- * (for example, SSL parameters)
- */
--struct mg_connection *mg_connect_http_opt(struct mg_mgr *mgr,
-- mg_event_handler_t ev_handler,
-- struct mg_connect_opts opts,
-- const char *url,
-- const char *extra_headers,
-- const char *post_data);
-+struct mg_connection *mg_connect_http_opt(
-+ struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data),
-+ struct mg_connect_opts opts, const char *url, const char *extra_headers,
-+ const char *post_data);
-
- /* Creates digest authentication header for a client request. */
- int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
- const char *method, const char *uri,
- const char *auth_domain, const char *user,
-- const char *passwd);
-+ const char *passwd, const char *nonce);
-
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
- #endif /* CS_MONGOOSE_SRC_HTTP_CLIENT_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/mqtt.h"
-+#line 1 "mongoose/src/mg_mqtt.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -4898,11 +5267,12 @@ int mg_http_create_digest_auth_header(ch
- #ifndef CS_MONGOOSE_SRC_MQTT_H_
- #define CS_MONGOOSE_SRC_MQTT_H_
-
--/* Amalgamated: #include "mongoose/src/net.h" */
-+/* Amalgamated: #include "mg_net.h" */
-
- struct mg_mqtt_message {
- int cmd;
- int qos;
-+ int len; /* message length in the IO buffer */
- struct mg_str topic;
- struct mg_str payload;
-
-@@ -4938,6 +5308,7 @@ struct mg_send_mqtt_handshake_opts {
- /* mg_mqtt_proto_data should be in header to allow external access to it */
- struct mg_mqtt_proto_data {
- uint16_t keep_alive;
-+ double last_control_time;
- };
-
- /* Message types */
-@@ -5082,13 +5453,26 @@ void mg_mqtt_pong(struct mg_connection *
- int mg_mqtt_next_subscribe_topic(struct mg_mqtt_message *msg,
- struct mg_str *topic, uint8_t *qos, int pos);
-
-+/*
-+ * Matches a topic against a topic expression
-+ *
-+ * Returns 1 if it matches; 0 otherwise.
-+ */
-+int mg_mqtt_match_topic_expression(struct mg_str exp, struct mg_str topic);
-+
-+/*
-+ * Same as `mg_mqtt_match_topic_expression()`, but takes `exp` as a
-+ * NULL-terminated string.
-+ */
-+int mg_mqtt_vmatch_topic_expression(const char *exp, struct mg_str topic);
-+
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
-
- #endif /* CS_MONGOOSE_SRC_MQTT_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/mqtt_server.h"
-+#line 1 "mongoose/src/mg_mqtt_server.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -5117,13 +5501,15 @@ int mg_mqtt_next_subscribe_topic(struct
- #if MG_ENABLE_MQTT_BROKER
-
- /* Amalgamated: #include "common/queue.h" */
--/* Amalgamated: #include "mongoose/src/mqtt.h" */
-+/* Amalgamated: #include "mg_mqtt.h" */
-
- #ifdef __cplusplus
- extern "C" {
- #endif /* __cplusplus */
-
--#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512;
-+#ifndef MG_MQTT_MAX_SESSION_SUBSCRIPTIONS
-+#define MG_MQTT_MAX_SESSION_SUBSCRIPTIONS 512
-+#endif
-
- struct mg_mqtt_broker;
-
-@@ -5193,7 +5579,7 @@ struct mg_mqtt_session *mg_mqtt_next(str
- #endif /* MG_ENABLE_MQTT_BROKER */
- #endif /* CS_MONGOOSE_SRC_MQTT_BROKER_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/dns.h"
-+#line 1 "mongoose/src/mg_dns.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -5207,7 +5593,7 @@ struct mg_mqtt_session *mg_mqtt_next(str
- #ifndef CS_MONGOOSE_SRC_DNS_H_
- #define CS_MONGOOSE_SRC_DNS_H_
-
--/* Amalgamated: #include "mongoose/src/net.h" */
-+/* Amalgamated: #include "mg_net.h" */
-
- #ifdef __cplusplus
- extern "C" {
-@@ -5220,6 +5606,8 @@ extern "C" {
- #define MG_DNS_AAAA_RECORD 0x1c /* Lookup IPv6 address */
- #define MG_DNS_SRV_RECORD 0x21 /* Lookup SRV */
- #define MG_DNS_MX_RECORD 0x0f /* Lookup mail server for domain */
-+#define MG_DNS_ANY_RECORD 0xff
-+#define MG_DNS_NSEC_RECORD 0x2f
-
- #define MG_MAX_DNS_QUESTIONS 32
- #define MG_MAX_DNS_ANSWERS 32
-@@ -5309,7 +5697,7 @@ int mg_dns_copy_questions(struct mbuf *i
- * struct because they might be invalidated as soon as the IO buffer grows
- * again.
- *
-- * Returns the number of bytes appened or -1 in case of error.
-+ * Returns the number of bytes appended or -1 in case of error.
- */
- int mg_dns_encode_record(struct mbuf *io, struct mg_dns_resource_record *rr,
- const char *name, size_t nlen, const void *rdata,
-@@ -5358,7 +5746,7 @@ void mg_set_protocol_dns(struct mg_conne
- #endif /* __cplusplus */
- #endif /* CS_MONGOOSE_SRC_DNS_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/dns_server.h"
-+#line 1 "mongoose/src/mg_dns_server.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -5376,7 +5764,7 @@ void mg_set_protocol_dns(struct mg_conne
-
- #if MG_ENABLE_DNS_SERVER
-
--/* Amalgamated: #include "mongoose/src/dns.h" */
-+/* Amalgamated: #include "mg_dns.h" */
-
- #ifdef __cplusplus
- extern "C" {
-@@ -5455,7 +5843,7 @@ void mg_dns_send_reply(struct mg_connect
- #endif /* MG_ENABLE_DNS_SERVER */
- #endif /* CS_MONGOOSE_SRC_DNS_SERVER_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/resolv.h"
-+#line 1 "mongoose/src/mg_resolv.h"
- #endif
- /*
- * Copyright (c) 2014 Cesanta Software Limited
-@@ -5469,7 +5857,7 @@ void mg_dns_send_reply(struct mg_connect
- #ifndef CS_MONGOOSE_SRC_RESOLV_H_
- #define CS_MONGOOSE_SRC_RESOLV_H_
-
--/* Amalgamated: #include "mongoose/src/dns.h" */
-+/* Amalgamated: #include "mg_dns.h" */
-
- #ifdef __cplusplus
- extern "C" {
-@@ -5487,7 +5875,7 @@ typedef void (*mg_resolve_callback_t)(st
-
- /* Options for `mg_resolve_async_opt`. */
- struct mg_resolve_async_opts {
-- const char *nameserver_url;
-+ const char *nameserver;
- int max_retries; /* defaults to 2 if zero */
- int timeout; /* in seconds; defaults to 5 if zero */
- int accept_literal; /* pseudo-resolve literal ipv4 and ipv6 addrs */
-@@ -5499,6 +5887,9 @@ struct mg_resolve_async_opts {
- int mg_resolve_async(struct mg_mgr *mgr, const char *name, int query,
- mg_resolve_callback_t cb, void *data);
-
-+/* Set default DNS server */
-+void mg_set_nameserver(struct mg_mgr *mgr, const char *nameserver);
-+
- /*
- * Resolved a DNS name asynchronously.
- *
-@@ -5536,7 +5927,7 @@ int mg_resolve_from_hosts_file(const cha
- #endif /* __cplusplus */
- #endif /* CS_MONGOOSE_SRC_RESOLV_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/coap.h"
-+#line 1 "mongoose/src/mg_coap.h"
- #endif
- /*
- * Copyright (c) 2015 Cesanta Software Limited
-@@ -5647,7 +6038,7 @@ struct mg_coap_option *mg_coap_add_optio
-
- /*
- * Frees the memory allocated for options.
-- * If the cm paramater doesn't contain any option it does nothing.
-+ * If the cm parameter doesn't contain any option it does nothing.
- */
- void mg_coap_free_options(struct mg_coap_message *cm);
-
-@@ -5704,7 +6095,7 @@ uint32_t mg_coap_compose(struct mg_coap_
-
- #endif /* CS_MONGOOSE_SRC_COAP_H_ */
- #ifdef MG_MODULE_LINES
--#line 1 "mongoose/src/sntp.h"
-+#line 1 "mongoose/src/mg_sntp.h"
- #endif
- /*
- * Copyright (c) 2016 Cesanta Software Limited
-@@ -5739,7 +6130,8 @@ struct mg_sntp_message {
-
- /* Establishes connection to given sntp server */
- struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr,
-- mg_event_handler_t event_handler,
-+ MG_CB(mg_event_handler_t event_handler,
-+ void *user_data),
- const char *sntp_server_name);
-
- /* Sends time request to given connection */
-@@ -5759,3 +6151,72 @@ struct mg_connection *mg_sntp_get_time(s
- #endif
-
- #endif /* CS_MONGOOSE_SRC_SNTP_H_ */
-+#ifdef MG_MODULE_LINES
-+#line 1 "mongoose/src/mg_socks.h"
-+#endif
-+/*
-+ * Copyright (c) 2017 Cesanta Software Limited
-+ * All rights reserved
-+ */
-+
-+#ifndef CS_MONGOOSE_SRC_SOCKS_H_
-+#define CS_MONGOOSE_SRC_SOCKS_H_
-+
-+#if MG_ENABLE_SOCKS
-+
-+#define MG_SOCKS_VERSION 5
-+
-+#define MG_SOCKS_HANDSHAKE_DONE MG_F_USER_1
-+#define MG_SOCKS_CONNECT_DONE MG_F_USER_2
-+
-+/* SOCKS5 handshake methods */
-+enum mg_socks_handshake_method {
-+ MG_SOCKS_HANDSHAKE_NOAUTH = 0, /* Handshake method - no authentication */
-+ MG_SOCKS_HANDSHAKE_GSSAPI = 1, /* Handshake method - GSSAPI auth */
-+ MG_SOCKS_HANDSHAKE_USERPASS = 2, /* Handshake method - user/password auth */
-+ MG_SOCKS_HANDSHAKE_FAILURE = 0xff, /* Handshake method - failure */
-+};
-+
-+/* SOCKS5 commands */
-+enum mg_socks_command {
-+ MG_SOCKS_CMD_CONNECT = 1, /* Command: CONNECT */
-+ MG_SOCKS_CMD_BIND = 2, /* Command: BIND */
-+ MG_SOCKS_CMD_UDP_ASSOCIATE = 3, /* Command: UDP ASSOCIATE */
-+};
-+
-+/* SOCKS5 address types */
-+enum mg_socks_address_type {
-+ MG_SOCKS_ADDR_IPV4 = 1, /* Address type: IPv4 */
-+ MG_SOCKS_ADDR_DOMAIN = 3, /* Address type: Domain name */
-+ MG_SOCKS_ADDR_IPV6 = 4, /* Address type: IPv6 */
-+};
-+
-+/* SOCKS5 response codes */
-+enum mg_socks_response {
-+ MG_SOCKS_SUCCESS = 0, /* Response: success */
-+ MG_SOCKS_FAILURE = 1, /* Response: failure */
-+ MG_SOCKS_NOT_ALLOWED = 2, /* Response: connection not allowed */
-+ MG_SOCKS_NET_UNREACHABLE = 3, /* Response: network unreachable */
-+ MG_SOCKS_HOST_UNREACHABLE = 4, /* Response: network unreachable */
-+ MG_SOCKS_CONN_REFUSED = 5, /* Response: network unreachable */
-+ MG_SOCKS_TTL_EXPIRED = 6, /* Response: network unreachable */
-+ MG_SOCKS_CMD_NOT_SUPPORTED = 7, /* Response: network unreachable */
-+ MG_SOCKS_ADDR_NOT_SUPPORTED = 8, /* Response: network unreachable */
-+};
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif /* __cplusplus */
-+
-+/* Turn the connection into the SOCKS server */
-+void mg_set_protocol_socks(struct mg_connection *c);
-+
-+/* Create socks tunnel for the client connection */
-+struct mg_iface *mg_socks_mk_iface(struct mg_mgr *, const char *proxy_addr);
-+
-+#ifdef __cplusplus
-+}
-+#endif /* __cplusplus */
-+
-+#endif
-+#endif
diff --git a/debian/patches/07-disable-chromecast.patch b/debian/patches/07-disable-chromecast.patch
new file mode 100644
index 0000000..afef27d
--- /dev/null
+++ b/debian/patches/07-disable-chromecast.patch
@@ -0,0 +1,46 @@
+Description: Disable Chromecast support to workaround security issues in Mongoose Simple web server
+Author: Reinhard Tartler <siretart@debian.org>
+Bug-Debian: http://bugs.debian.org/898943
+Last-Update: 2018-06-3
+
+--- a/src/smplayer.pro
++++ b/src/smplayer.pro
+@@ -38,7 +38,7 @@ DEFINES += MOUSE_GESTURES
+ DEFINES += GLOBALSHORTCUTS
+ DEFINES += ADD_BLACKBORDERS_FS
+ DEFINES += INITIAL_BLACKBORDERS
+-DEFINES += CHROMECAST_SUPPORT
++# DEFINES += CHROMECAST_SUPPORT
+
+ DEFINES += MPV_SUPPORT
+ DEFINES += MPLAYER_SUPPORT
+--- a/Makefile
++++ b/Makefile
+@@ -21,7 +21,7 @@ DEFS=DATA_PATH=\\\"$(DATA_PATH)\\\" \
+ DOC_PATH=\\\"$(DOC_PATH)\\\" THEMES_PATH=\\\"$(THEMES_PATH)\\\" \
+ SHORTCUTS_PATH=\\\"$(SHORTCUTS_PATH)\\\"
+
+-all: src/smplayer webserver/simple_web_server
++all: src/smplayer #webserver/simple_web_server
+
+ src/smplayer:
+ ./get_svn_revision.sh
+@@ -29,7 +29,8 @@ src/smplayer:
+ cd src && $(LRELEASE) smplayer.pro
+
+ webserver/simple_web_server:
+- cd webserver && make
++ #cd webserver && make
++ touch $@
+
+ clean:
+ if [ -f src/Makefile ]; then cd src && make distclean; fi
+@@ -46,7 +47,7 @@ install: all
+ -install -d $(DESTDIR)$(DOC_PATH)
+ install -m 644 Changelog *.txt $(DESTDIR)$(DOC_PATH)
+
+- install -m 755 webserver/simple_web_server $(DESTDIR)$(PREFIX)/bin/
++# install -m 755 webserver/simple_web_server $(DESTDIR)$(PREFIX)/bin/
+
+ -install -d $(DESTDIR)$(DOC_PATH)
+ tar -C docs/ --exclude=.svn -c -f - . | tar -C $(DESTDIR)$(DOC_PATH) -x -f -
diff --git a/debian/patches/series b/debian/patches/series
index 91b34a9..888904f 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -3,5 +3,5 @@
05-add-debian-hardening-flags.patch
06-tryfixplaylist.patch
01-update-mime-types.patch
-03-update-mongoose-to-6.11.patch
07-fix-ftbfs-gcc8.patch
+07-disable-chromecast.patch