diff options
author | Reinhard Tartler <siretart@tauware.de> | 2018-06-15 12:21:15 -0400 |
---|---|---|
committer | Reinhard Tartler <siretart@tauware.de> | 2018-06-18 15:05:48 -0400 |
commit | 64b588e5d7ffae27265b3e95e59dbf577fbfb0db (patch) | |
tree | 60f01b0b3344696b0e81fd654f49110256a3b54a | |
parent | d1f3aaeda717076c1761f0aebb56681a5c4ce435 (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.patch | 12281 | ||||
-rw-r--r-- | debian/patches/07-disable-chromecast.patch | 46 | ||||
-rw-r--r-- | debian/patches/series | 2 |
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, ¶ms, &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 |