From 7817a91b7c6c0a8c10f2a096cf187d2565af3960 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 17 Jan 2014 16:07:35 +0100 Subject: [PATCH 01/80] Add all functions to dll.def, that are in the header --- resources/dll.def | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/dll.def b/resources/dll.def index afd53118..3ecc5d28 100644 --- a/resources/dll.def +++ b/resources/dll.def @@ -7,9 +7,26 @@ EXPORTS mg_printf mg_get_header mg_get_var + mg_get_var2 mg_get_cookie mg_get_option mg_get_valid_option_names mg_version mg_modify_passwords_file mg_md5 + mg_set_request_handler + mg_get_ports + mg_get_request_info + mg_websocket_write + mg_lock + mg_unlock + mg_send_file + mg_download + mg_close_connection + mg_upload + mg_start_thread + mg_get_builtin_mime_type + mg_url_decode + mg_url_encode + mg_cry + mg_strncasecmp From 081678e773faac5442840df67b97933603dbb3ad Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 17 Jan 2014 16:14:04 +0100 Subject: [PATCH 02/80] Sort dll.def like the header --- resources/dll.def | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/resources/dll.def b/resources/dll.def index 3ecc5d28..95b20b5b 100644 --- a/resources/dll.def +++ b/resources/dll.def @@ -2,31 +2,31 @@ LIBRARY EXPORTS mg_start mg_stop - mg_read + mg_set_request_handler + mg_get_option + mg_get_valid_option_names + mg_get_ports + mg_modify_passwords_file + mg_get_request_info mg_write + mg_websocket_write + mg_lock + mg_unlock mg_printf + mg_send_file + mg_read mg_get_header mg_get_var mg_get_var2 mg_get_cookie - mg_get_option - mg_get_valid_option_names - mg_version - mg_modify_passwords_file - mg_md5 - mg_set_request_handler - mg_get_ports - mg_get_request_info - mg_websocket_write - mg_lock - mg_unlock - mg_send_file mg_download mg_close_connection mg_upload mg_start_thread mg_get_builtin_mime_type + mg_version mg_url_decode mg_url_encode + mg_md5 mg_cry mg_strncasecmp From dd50cc65ca46458cb23fb66b6ca2f14e8c044e2b Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 17 Jan 2014 22:19:11 +0100 Subject: [PATCH 03/80] Hide read-only flag in file dialogs opened by the Edit Settings dialog --- src/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index 318ea311..3ae1179a 100644 --- a/src/main.c +++ b/src/main.c @@ -218,7 +218,6 @@ static void set_option(char **options, const char *name, const char *value) } } - static void read_config_file(const char *config_file, char **options) { char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p; @@ -265,7 +264,6 @@ static void read_config_file(const char *config_file, char **options) } } - static void process_command_line_arguments(char *argv[], char **options) { char *p; @@ -608,6 +606,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) case WM_COMMAND: switch (LOWORD(wParam)) { + case ID_SAVE: EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE); if ((fp = fopen(config_file, "w+")) != NULL) { @@ -618,6 +617,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) } EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE); break; + case ID_RESET_DEFAULTS: for (i = 0; default_options[i * 2] != NULL; i++) { name = default_options[i * 2]; @@ -630,6 +630,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) } } break; + case ID_RESET_FILE: read_config_file(config_file, file_options); for (i = 0; default_options[i * 2] != NULL; i++) { @@ -653,6 +654,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) free(file_options[i]); } break; + case ID_RESET_ACTIVE: for (i = 0; default_options[i * 2] != NULL; i++) { name = default_options[i * 2]; @@ -681,7 +683,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) of.lpstrFile = path; of.nMaxFile = sizeof(path); of.lpstrInitialDir = mg_get_option(ctx, "document_root"); - of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR; + of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; memset(&bi, 0, sizeof(bi)); bi.hwndOwner = (HWND) hDlg; @@ -699,7 +701,6 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) } } } - break; case WM_INITDIALOG: @@ -718,6 +719,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) } } break; + default: break; } @@ -770,7 +772,7 @@ static void show_settings_dialog() DWORD style; DLGTEMPLATE *dia = (DLGTEMPLATE *) mem; WORD i, cl, x, y, width, nelems = 0; - static int guard; + static int guard = 0; /* test if dialog is already open */ static struct { DLGTEMPLATE template; /* 18 bytes */ From ccda241b410f874ba5e7c35d6215e5c9a4dc8256 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 17 Jan 2014 22:26:49 +0100 Subject: [PATCH 04/80] Increase label width for Edit Settings dialog, so all current option names fit --- src/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 3ae1179a..bdcd6f2e 100644 --- a/src/main.c +++ b/src/main.c @@ -764,8 +764,8 @@ static void add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type, static void show_settings_dialog() { #define HEIGHT 15 -#define WIDTH 400 -#define LABEL_WIDTH 80 +#define WIDTH 450 +#define LABEL_WIDTH 85 unsigned char mem[4096], *p; const char **option_names, *long_option_name; From 3c5be54bf50fde10645ce86701e5c4eec395ce8a Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 17 Jan 2014 23:21:14 +0100 Subject: [PATCH 05/80] Prepare a server side check of the nonce for http digest auth --- src/civetweb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index 49b184fa..da904f0d 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -2704,6 +2704,7 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, { char *name, *value, *s; const char *auth_header; + unsigned long nonce; (void) memset(ah, 0, sizeof(*ah)); if ((auth_header = mg_get_header(conn, "Authorization")) == NULL || @@ -2754,6 +2755,12 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, } } + /* Convert the nonce from the client to a number and check it */ + nonce = strtoul(ah->nonce, &s, 10); + if ((s == NULL) || (*s != 0)) { + return 0; + } + /* CGI needs it as REMOTE_USER */ if (ah->user != NULL) { conn->request_info.remote_user = mg_strdup(ah->user); @@ -2855,6 +2862,7 @@ static void send_authorization_request(struct mg_connection *conn) { char date[64]; time_t curtime = time(NULL); + unsigned long nonce = (unsigned long)curtime ^ (unsigned long)conn; conn->status_code = 401; conn->must_close = 1; @@ -2869,7 +2877,7 @@ static void send_authorization_request(struct mg_connection *conn) "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", nonce=\"%lu\"\r\n\r\n", date, suggest_connection_header(conn), conn->ctx->config[AUTHENTICATION_DOMAIN], - (unsigned long) time(NULL)); + nonce); } static int is_authorized_for_put(struct mg_connection *conn) From 903287ff0a5c91ac630253fbe0ff0376c8194f9e Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 17 Jan 2014 23:56:58 +0100 Subject: [PATCH 06/80] Server should check the nonce - see http://en.wikipedia.org/wiki/Digest_access_authentication#Advantages --- src/civetweb.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index da904f0d..6add3a99 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -617,6 +617,9 @@ struct mg_context { int workerthreadcount; /* The amount of worker threads. */ pthread_t *workerthreadids;/* The worker thread IDs. */ + unsigned long start_time; /* Server start time, used for authentication */ + unsigned long nonce_count; /* Used nonces, used for authentication */ + /* linked list of uri handlers */ struct mg_request_handler_info *request_handlers; }; @@ -2760,6 +2763,14 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, if ((s == NULL) || (*s != 0)) { return 0; } + nonce ^= (unsigned long)(conn->ctx); + if (noncectx->start_time) { + /* nonce is from a previous start of the server and no longer valid (replay attack?) */ + return 0; + } + if (nonce>=conn->ctx->start_time+conn->ctx->nonce_count) { + return 0; + } /* CGI needs it as REMOTE_USER */ if (ah->user != NULL) { @@ -2862,8 +2873,14 @@ static void send_authorization_request(struct mg_connection *conn) { char date[64]; time_t curtime = time(NULL); - unsigned long nonce = (unsigned long)curtime ^ (unsigned long)conn; + unsigned long nonce = (unsigned long)(conn->ctx->start_time); + (void)pthread_mutex_lock(&conn->ctx->mutex); + nonce += conn->ctx->nonce_count; + ++conn->ctx->nonce_count; + (void)pthread_mutex_unlock(&conn->ctx->mutex); + + nonce ^= (unsigned long)(conn->ctx); conn->status_code = 401; conn->must_close = 1; @@ -6197,12 +6214,17 @@ static void master_thread_run(void *thread_func_param) } #endif + /* Initialize thread local storage */ #if defined(_WIN32) && !defined(__SYMBIAN32__) tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL); #endif tls.is_master = 1; pthread_setspecific(sTlsKey, &tls); + /* Server starts *now* */ + ctx->start_time = (unsigned long)time(NULL); + + /* Allocate memory for the listening sockets, and start the server */ pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0])); while (pfd != NULL && ctx->stop_flag == 0) { for (i = 0; i < ctx->num_listening_sockets; i++) { From ae7ff2273c35ffc62cfebf89ecef32f01ca7fad3 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 19 Jan 2014 18:19:44 +0100 Subject: [PATCH 07/80] Nonce check can be disabled (for embedding into applications that restart frequently) --- src/civetweb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index 6add3a99..e19d5ccf 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -2758,7 +2758,10 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, } } - /* Convert the nonce from the client to a number and check it */ +#ifndef NO_NONCE_CHECK + /* Convert the nonce from the client to a number and check it. */ + /* Server side nonce check is valuable in all situations but one: if the server restarts frequently, + but the client should not see that, so the server should accept nonces from previous starts. */ nonce = strtoul(ah->nonce, &s, 10); if ((s == NULL) || (*s != 0)) { return 0; @@ -2771,6 +2774,7 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, if (nonce>=conn->ctx->start_time+conn->ctx->nonce_count) { return 0; } +#endif /* CGI needs it as REMOTE_USER */ if (ah->user != NULL) { From 031af3defd50d2ff050ebc199ae765a76b707bd5 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 19 Jan 2014 20:48:41 +0100 Subject: [PATCH 08/80] Update RELEASE_NOTES --- RELEASE_NOTES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 97bb9cd9..cc2d3950 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,9 @@ Release Notes v1.6 (Under Development) Changes ------- +- Server should check the nonce for http digest access authentication (bel) +- Hide read-only flag in file dialogs opened by the Edit Settings dialog for the Windows executable (bel) +- Add all functions to dll.def, that are in the header (bel) - Added Lua extensions: send_file, get_var, get_mime_type, get_cookie, url_decode, url_encode (bel) - mg_set_request_handler() mod to use pattern (bel, Patch from Toni Wilk) - Solved, tested and documented SSL support for Windows (bel) From 8d3a48398c521e4038982822e81c9450dab572b7 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 19 Jan 2014 22:16:09 +0100 Subject: [PATCH 09/80] Support a pre-loaded file for the Lua environment --- src/civetweb.c | 3 ++- src/main.c | 3 +++ src/mod_lua.inl | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index e19d5ccf..341b2bbc 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -526,7 +526,7 @@ enum { NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT, #if defined(USE_LUA) - LUA_SCRIPT_EXTENSIONS, LUA_SERVER_PAGE_EXTENSIONS, + LUA_PRELOAD_FILE, LUA_SCRIPT_EXTENSIONS, LUA_SERVER_PAGE_EXTENSIONS, #endif #if defined(USE_WEBSOCKET) WEBSOCKET_ROOT, @@ -570,6 +570,7 @@ static const char *config_options[] = { "request_timeout_ms", "30000", #if defined(USE_LUA) + "lua_preload_file", NULL, "lua_script_pattern", "**.lua$", "lua_server_page_pattern", "**.lp$|**.lsp$", #endif diff --git a/src/main.c b/src/main.c index bdcd6f2e..5e1e6f97 100644 --- a/src/main.c +++ b/src/main.c @@ -552,6 +552,9 @@ static int is_filename_option(const char *option_name) !strcmp(option_name, "put_delete_auth_file") || !strcmp(option_name, "access_log_file") || !strcmp(option_name, "error_log_file") || +#ifdef USE_LUA + !strcmp(option_name, "lua_preload_file") || +#endif !strcmp(option_name, "ssl_certificate"); } diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 8f6dba34..0b419f15 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -605,6 +605,7 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co { const struct mg_request_info *ri = mg_get_request_info(conn); char src_addr[IP_ADDR_STR_LEN]; + const char * preload_file = conn->ctx->config[LUA_PRELOAD_FILE]; int i; extern void luaL_openlibs(lua_State *); @@ -719,6 +720,11 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co /* Register default mg.onerror function */ IGNORE_UNUSED_RESULT(luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', " "debug.traceback(e, 1)) end")); + + /* Preload */ + if ((preload_file != NULL) && (*preload_file != 0)) { + IGNORE_UNUSED_RESULT(luaL_dofile(L, preload_file)); + } } static int lua_error_handler(lua_State *L) From 3eba9878d4f320d572223838a1ad330d0fc235e5 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 19 Jan 2014 22:32:44 +0100 Subject: [PATCH 10/80] Document lua_preload_file option --- docs/UserManual.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/UserManual.md b/docs/UserManual.md index 56543ecb..cbea78aa 100644 --- a/docs/UserManual.md +++ b/docs/UserManual.md @@ -316,6 +316,13 @@ Timeout for network read and network write operations, in milliseconds. If client intends to keep long-running connection, either increase this value or use keep-alive messages. +### lua_preload_file +This configuration option can be used to specify a Lua script file, which +is executed before the actual web page script (Lua script, Lua server page +or Lua websocket). It can be used to modify the Lua environment of all web +page scripts, e.g., by loading additional libraries required by all scripts +or to achieve backward compatibility by defining obsolete functions. + ### lua_script_pattern `"**.lua$` A pattern for files that are interpreted as Lua scripts by the server. In contrast to Lua server pages, Lua scripts use plain Lua syntax. From e7f9e4b047d363d209415676590379560a8323d4 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 19 Jan 2014 22:42:06 +0100 Subject: [PATCH 11/80] Set absolute path for lua_preload_file --- src/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.c b/src/main.c index 5e1e6f97..0e9fdabc 100644 --- a/src/main.c +++ b/src/main.c @@ -446,6 +446,9 @@ static void start_civetweb(int argc, char *argv[]) set_absolute_path(options, "access_log_file", argv[0]); set_absolute_path(options, "error_log_file", argv[0]); set_absolute_path(options, "global_auth_file", argv[0]); +#ifdef USE_LUA + set_absolute_path(options, "lua_preload_file", argv[0]); +#endif set_absolute_path(options, "ssl_certificate", argv[0]); /* Make extra verification for certain options */ From 4f3c267731a8c05ba93275240c5de56ef32d2267 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 19 Jan 2014 22:50:13 +0100 Subject: [PATCH 12/80] Add test for lua_preload_file --- test/lua_preload_file.lua | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/lua_preload_file.lua diff --git a/test/lua_preload_file.lua b/test/lua_preload_file.lua new file mode 100644 index 00000000..7761f473 --- /dev/null +++ b/test/lua_preload_file.lua @@ -0,0 +1,7 @@ +--[[ +Load this test file by adding + lua_preload_file ./lua_preload_file.lua +to the civetweb.conf file +]] + +mg.preload = "lua_preload_file successfully loaded" From 50ccfe6d78f401c90f5111adae4aae7db999ab76 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 19 Jan 2014 23:06:00 +0100 Subject: [PATCH 13/80] Check new config field types --- src/main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 0e9fdabc..90929a8d 100644 --- a/src/main.c +++ b/src/main.c @@ -455,6 +455,9 @@ static void start_civetweb(int argc, char *argv[]) verify_existence(options, "document_root", 1); verify_existence(options, "cgi_interpreter", 0); verify_existence(options, "ssl_certificate", 0); +#ifdef USE_LUA + verify_existence(options, "lua_preload_file", 0); +#endif /* Setup signal handler: quit on Ctrl-C */ signal(SIGTERM, signal_handler); @@ -563,12 +566,17 @@ static int is_filename_option(const char *option_name) static int is_directory_option(const char *option_name) { - return !strcmp(option_name, "document_root"); + return !strcmp(option_name, "document_root") || +#if defined(USE_WEBSOCKET) + !strcmp(option_name, "websocket_root") || +#endif + 0; } static int is_numeric_options(const char *option_name) { - return !strcmp(option_name, "num_threads"); + return !strcmp(option_name, "num_threads") || + !strcmp(option_name, "request_timeout_ms"); } static void save_config(HWND hDlg, FILE *fp) From 6ee055197060d79866bbf018bd1091368afb9b38 Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 20 Jan 2014 21:48:27 +0100 Subject: [PATCH 14/80] mg.base64_encode for Lua --- src/civetweb.c | 54 +++++++++++++++++++++++++------------------------ src/mod_lua.inl | 31 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 341b2bbc..6a0eb175 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -2274,6 +2274,34 @@ int mg_get_cookie(const char *cookie_header, const char *var_name, return len; } +#if defined(USE_WEBSOCKET) || defined(USE_LUA) +static void base64_encode(const unsigned char *src, int src_len, char *dst) +{ + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 63]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} +#endif + static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, size_t buf_len, struct file *filep, int * is_script_ressource) @@ -4597,32 +4625,6 @@ static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) } /* END OF SHA1 CODE */ -static void base64_encode(const unsigned char *src, int src_len, char *dst) -{ - static const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i, j, a, b, c; - - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = i + 1 >= src_len ? 0 : src[i + 1]; - c = i + 2 >= src_len ? 0 : src[i + 2]; - - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 63]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; -} - static void send_websocket_handshake(struct mg_connection *conn) { static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 0b419f15..dd05002f 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -559,6 +559,35 @@ static int lsp_url_decode(lua_State *L) return 1; } +/* mg.base64_encode */ +static int lsp_base64_encode(lua_State *L) +{ + int num_args = lua_gettop(L); + const char *text; + size_t text_len; + char *dst; + + if (num_args==1) { + text = lua_tolstring(L, 1, &text_len); + if (text) { + dst = malloc(text_len*8/6+4); + if (dst) { + base64_encode(text, text_len, dst); + lua_pushstring(L, dst); + free(dst); + } else { + return luaL_error(L, "out of memory in base64_encode() call"); + } + } else { + lua_pushnil(L); + } + } else { + /* Syntax error */ + return luaL_error(L, "invalid base64_encode() call"); + } + return 1; +} + /* mg.write for websockets */ static int lwebsock_write(lua_State *L) { @@ -677,6 +706,8 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co reg_function(L, "md5", lsp_md5, conn); reg_function(L, "url_encode", lsp_url_encode, conn); reg_function(L, "url_decode", lsp_url_decode, conn); + reg_function(L, "base64_encode", lsp_base64_encode, conn); + //reg_function(L, "base64_decode", lsp_base64_decode, conn); reg_string(L, "version", CIVETWEB_VERSION); reg_string(L, "document_root", conn->ctx->config[DOCUMENT_ROOT]); From 9f9a6ffda3b14e01f1a527d94c6682a73167f869 Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 20 Jan 2014 21:54:59 +0100 Subject: [PATCH 15/80] test mg.base64_encode for Lua --- test/page4.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/page4.lua b/test/page4.lua index ac5ff2a0..58d97139 100644 --- a/test/page4.lua +++ b/test/page4.lua @@ -124,5 +124,15 @@ mg.write(" decoded mg-url: " .. htmlEscape(dec_url_string) .. "\r\n") mg.write(" decoded reference url: " .. htmlEscape(dec_ref_string) .. "\r\n") mg.write("\r\n") +-- base64_encode +mg.write("BASE64 encode/decode test:\r\n") +raw_string = [[ !"#$%&'()*+,-./0123456789:;<=>?@]] +mg.write(" original string: " .. htmlEscape(raw_string) .. "\r\n") +url_string = mg.base64_encode(raw_string) +ref_string = "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9A" -- from http://www.base64encode.org/ +mg.write(" mg-base64: " .. htmlEscape(url_string) .. "\r\n") +mg.write(" reference base64: " .. htmlEscape(ref_string) .. "\r\n") + + -- end of page mg.write("\r\n\r\n\r\n") From b72ad2cbe6bc1a4a62f28cca7e20398bab164081 Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 20 Jan 2014 22:49:37 +0100 Subject: [PATCH 16/80] mg.base64_decode for Lua --- src/civetweb.c | 43 ++++++++++++++++++++++++++++++++++++++++++- src/mod_lua.inl | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 6a0eb175..60d615f1 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -2300,6 +2300,47 @@ static void base64_encode(const unsigned char *src, int src_len, char *dst) } dst[j++] = '\0'; } + +static unsigned char b64reverse(char letter) { + if (letter>='A' && letter<='Z') return letter-'A'; + if (letter>='a' && letter<='z') return letter-'a'+26; + if (letter>='0' && letter<='9') return letter-'0'+52; + if (letter=='+') return 62; + if (letter=='/') return 63; + if (letter=='=') return 255; /* normal end */ + return 254; /* error */ +} + +static int base64_decode(const unsigned char *src, int src_len, char *dst, size_t *dst_len) +{ + int i; + unsigned char a, b, c, d; + + *dst_len = 0; + + for (i = 0; i < src_len; i += 4) { + a = b64reverse(src[i]); + if (a>=254) return i; + + b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]); + if (b>=254) return i+1; + + c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]); + if (c==254) return i+2; + + d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]); + if (c==254) return i+3; + + dst[(*dst_len)++] = (a << 2) + (b >> 4); + if (c!=255) { + dst[(*dst_len)++] = (b << 4) + (c >> 2); + if (d!=255) { + dst[(*dst_len)++] = (c << 6) + d; + } + } + } + return -1; +} #endif static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, @@ -2789,7 +2830,7 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, #ifndef NO_NONCE_CHECK /* Convert the nonce from the client to a number and check it. */ - /* Server side nonce check is valuable in all situations but one: if the server restarts frequently, + /* Server side nonce check is valuable in all situations but one: if the server restarts frequently, but the client should not see that, so the server should accept nonces from previous starts. */ nonce = strtoul(ah->nonce, &s, 10); if ((s == NULL) || (*s != 0)) { diff --git a/src/mod_lua.inl b/src/mod_lua.inl index dd05002f..fce311fe 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -588,6 +588,41 @@ static int lsp_base64_encode(lua_State *L) return 1; } +/* mg.base64_encode */ +static int lsp_base64_decode(lua_State *L) +{ + int num_args = lua_gettop(L); + const char *text; + size_t text_len, dst_len; + int ret; + char *dst; + + if (num_args==1) { + text = lua_tolstring(L, 1, &text_len); + if (text) { + dst = malloc(text_len); + if (dst) { + ret = base64_decode(text, text_len, dst, &dst_len); + if (ret != -1) { + free(dst); + return luaL_error(L, "illegal character in lsp_base64_decode() call"); + } else { + lua_pushlstring(L, dst, dst_len); + free(dst); + } + } else { + return luaL_error(L, "out of memory in lsp_base64_decode() call"); + } + } else { + lua_pushnil(L); + } + } else { + /* Syntax error */ + return luaL_error(L, "invalid lsp_base64_decode() call"); + } + return 1; +} + /* mg.write for websockets */ static int lwebsock_write(lua_State *L) { @@ -707,7 +742,7 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co reg_function(L, "url_encode", lsp_url_encode, conn); reg_function(L, "url_decode", lsp_url_decode, conn); reg_function(L, "base64_encode", lsp_base64_encode, conn); - //reg_function(L, "base64_decode", lsp_base64_decode, conn); + reg_function(L, "base64_decode", lsp_base64_decode, conn); reg_string(L, "version", CIVETWEB_VERSION); reg_string(L, "document_root", conn->ctx->config[DOCUMENT_ROOT]); @@ -755,7 +790,7 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co /* Preload */ if ((preload_file != NULL) && (*preload_file != 0)) { IGNORE_UNUSED_RESULT(luaL_dofile(L, preload_file)); - } + } } static int lua_error_handler(lua_State *L) From fa91b849073b0136c8964e29ad3d53cbd085e4f7 Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 20 Jan 2014 22:50:28 +0100 Subject: [PATCH 17/80] Test mg.base64_decode for Lua --- test/page4.lua | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/test/page4.lua b/test/page4.lua index 58d97139..cd73990c 100644 --- a/test/page4.lua +++ b/test/page4.lua @@ -11,6 +11,7 @@ end mg.write("HTTP/1.0 200 OK\r\n") mg.write("Connection: close\r\n") mg.write("Content-Type: text/html; charset=utf-8\r\n") +mg.write("Cache-Control: max-age=0, must-revalidate\r\n") if not cookie_value then mg.write("Set-Cookie: " .. cookie_name .. "=" .. tostring(now) .. "\r\n") end @@ -106,21 +107,21 @@ else end raw_string = [[ !"#$%&'()*+,-./0123456789:;<=>?@]] mg.write(" original string: " .. htmlEscape(raw_string) .. "\r\n") -url_string = mg.url_encode(raw_string):upper() +mg_string = mg.url_encode(raw_string):upper() ref_string = "%20!%22%23%24%25%26'()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40" -- from http://www.w3schools.com/tags/ref_urlencode.asp -mg.write(" mg-url: " .. htmlEscape(url_string) .. "\r\n") +mg.write(" mg-url: " .. htmlEscape(mg_string) .. "\r\n") mg.write(" reference url: " .. htmlEscape(ref_string) .. "\r\n") -dec_url_string = mg.url_decode(url_string) +dec_mg_string = mg.url_decode(mg_string) dec_ref_string = mg.url_decode(ref_string) -mg.write(" decoded mg-url: " .. htmlEscape(dec_url_string) .. "\r\n") +mg.write(" decoded mg-url: " .. htmlEscape(dec_mg_string) .. "\r\n") mg.write(" decoded reference url: " .. htmlEscape(dec_ref_string) .. "\r\n") -dec_url_string = mg.url_decode(url_string, false) +dec_mg_string = mg.url_decode(mg_string, false) dec_ref_string = mg.url_decode(ref_string, false) -mg.write(" decoded mg-url: " .. htmlEscape(dec_url_string) .. "\r\n") +mg.write(" decoded mg-url: " .. htmlEscape(dec_mg_string) .. "\r\n") mg.write(" decoded reference url: " .. htmlEscape(dec_ref_string) .. "\r\n") -dec_url_string = mg.url_decode(url_string, true) +dec_mg_string = mg.url_decode(mg_string, true) dec_ref_string = mg.url_decode(ref_string, true) -mg.write(" decoded mg-url: " .. htmlEscape(dec_url_string) .. "\r\n") +mg.write(" decoded mg-url: " .. htmlEscape(dec_mg_string) .. "\r\n") mg.write(" decoded reference url: " .. htmlEscape(dec_ref_string) .. "\r\n") mg.write("\r\n") @@ -128,11 +129,25 @@ mg.write("\r\n") mg.write("BASE64 encode/decode test:\r\n") raw_string = [[ !"#$%&'()*+,-./0123456789:;<=>?@]] mg.write(" original string: " .. htmlEscape(raw_string) .. "\r\n") -url_string = mg.base64_encode(raw_string) +mg_string = mg.base64_encode(raw_string) ref_string = "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9A" -- from http://www.base64encode.org/ -mg.write(" mg-base64: " .. htmlEscape(url_string) .. "\r\n") +mg.write(" mg-base64: " .. htmlEscape(mg_string) .. "\r\n") mg.write(" reference base64: " .. htmlEscape(ref_string) .. "\r\n") - +dec_mg_string = mg.base64_decode(mg_string) +dec_ref_string = mg.base64_decode(ref_string) +mg.write(" decoded mg-base64: " .. htmlEscape(dec_mg_string) .. "\r\n") +mg.write(" decoded reference base64: " .. htmlEscape(dec_ref_string) .. "\r\n") +mg.write("\r\n") +raw_string = [[ -?-]] +mg.write(" original string: " .. htmlEscape(raw_string) .. "\r\n") +mg_string = mg.base64_encode(raw_string) +ref_string = "PD8+IC0/LQ==" -- from http://www.base64encode.org/ +mg.write(" mg-base64: " .. htmlEscape(mg_string) .. "\r\n") +mg.write(" reference base64: " .. htmlEscape(ref_string) .. "\r\n") +dec_mg_string = mg.base64_decode(mg_string) +dec_ref_string = mg.base64_decode(ref_string) +mg.write(" decoded mg-base64: " .. htmlEscape(dec_mg_string) .. "\r\n") +mg.write(" decoded reference base64: " .. htmlEscape(dec_ref_string) .. "\r\n") -- end of page mg.write("\r\n\r\n\r\n") From 172c106e7af83f4daa6c9af1abcf700baa2cb214 Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 20 Jan 2014 22:52:34 +0100 Subject: [PATCH 18/80] document mg.base64_encode and mg.base64_decode for Lua --- docs/UserManual.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/UserManual.md b/docs/UserManual.md index cbea78aa..3e188e41 100644 --- a/docs/UserManual.md +++ b/docs/UserManual.md @@ -409,11 +409,13 @@ mg (table): mg.document_root -- a string that holds the document root directory mg.auth_domain -- a string that holds the HTTP authentication domain mg.get_var(str, varname) -- extract variable from (query) string - mg.get_cookie(str, cookie) -- extract cookie from a string + mg.get_cookie(str, cookie) -- extract cookie from a string mg.get_mime_type(filename) -- get MIME type of a file mg.send_file(filename) -- send a file, including MIME type mg.url_encode(str) -- URL encode a string mg.url_decode(str) -- URL decode a string + mg.base64_encode(str) -- BASE64 encode a string + mg.base64_decode(str) -- BASE64 decode a string mg.md5(str) -- return the MD5 hash of a string mg.keep_alive(bool) -- allow/forbid to use http keep-alive for this request mg.request_info -- a table with the following request information @@ -426,7 +428,7 @@ mg (table): .query_string -- query string if present, nil otherwise .script_name -- name of the Lua script .https -- true if accessed by https://, false otherwise - .remote_user -- user name if authenticated, nil otherwise + .remote_user -- user name if authenticated, nil otherwise connect (function): -- Connect to the remote TCP server. This function is an implementation From 08fec89b00507d24c45b6e0b01e8ec108d5c420c Mon Sep 17 00:00:00 2001 From: Jeremy Lin Date: Mon, 20 Jan 2014 16:35:55 -0800 Subject: [PATCH 19/80] Enable Large File Support (LFS) on 32-bit systems by default In line with the structure of the existing code, this is done by adding '#define _FILE_OFFSET_BITS 64' near the top of civetweb.c, before the various system #includes. This might not work well if the code is later split up into more translation units; in that case, it would be better to move the LFS macros into a separate header file that is included as appropriate. It may also be more portable to enable LFS via the makefile instead, for example, by adding something like: ifdef WITH_LFS CFLAGS += $(shell getconf LFS_CFLAGS) LDFLAGS += $(shell getconf LFS_LDFLAGS) endif --- src/civetweb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index 60d615f1..c66620d0 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -27,7 +27,8 @@ #ifdef __linux__ #define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ #endif -#define _LARGEFILE_SOURCE /* Enable 64-bit file offsets */ +#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ +#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ #define __STDC_FORMAT_MACROS /* wants this for C++ */ #define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ #endif From aa44c1a06909763fd329996b7de33104692ddfa0 Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 23 Jan 2014 20:28:03 +0100 Subject: [PATCH 20/80] merge [win32] - get rid of dll.def and export/import functions based on prepro... --- include/civetweb.h | 100 ++++++++++++++++++++++++++------------------- resources/dll.def | 32 --------------- 2 files changed, 57 insertions(+), 75 deletions(-) delete mode 100644 resources/dll.def diff --git a/include/civetweb.h b/include/civetweb.h index b8be9c41..d6f4c55d 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -26,6 +26,20 @@ #define CIVETWEB_VERSION "1.6" #endif +#ifndef CIVETWEB_API + #if defined(_WIN32) + #if defined(CIVETWEB_DLL_EXPORTS) + #define CIVETWEB_API __declspec(dllexport) + #elif defined(CIVETWEB_DLL_IMPORTS) + #define CIVETWEB_API __declspec(dllimport) + #else + #define CIVETWEB_API + #endif + #else + #define CIVETWEB_API + #endif +#endif + #include #include @@ -165,7 +179,7 @@ struct mg_callbacks { Return: web server context, or NULL on error. */ -struct mg_context *mg_start(const struct mg_callbacks *callbacks, +CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks, void *user_data, const char **configuration_options); @@ -175,7 +189,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, Must be called last, when an application wants to stop the web server and release all associated resources. This function blocks until all Civetweb threads are stopped. Context pointer becomes invalid. */ -void mg_stop(struct mg_context *); +CIVETWEB_API void mg_stop(struct mg_context *); /* mg_request_handler @@ -206,7 +220,7 @@ typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata); handler: the callback handler to use when the URI is requested. If NULL, the URI will be removed. cbdata: the callback data to give to the handler when it s requested. */ -void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata); +CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata); /* Get the value of particular configuration parameter. @@ -215,14 +229,14 @@ void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_ If given parameter name is not valid, NULL is returned. For valid names, return value is guaranteed to be non-NULL. If parameter is not set, zero-length string is returned. */ -const char *mg_get_option(const struct mg_context *ctx, const char *name); +CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx, const char *name); /* Return array of strings that represent valid configuration options. For each option, option name and default value is returned, i.e. the number of entries in the array equals to number_of_options x 2. Array is NULL terminated. */ -const char **mg_get_valid_option_names(void); +CIVETWEB_API const char **mg_get_valid_option_names(void); /* Get the list of ports that civetweb is listening on. size is the size of the ports int array and ssl int array to fill. @@ -231,7 +245,7 @@ const char **mg_get_valid_option_names(void); Return value is the number of ports and ssl information filled in. The value returned is read-only. Civetweb does not allow changing configuration at run time. */ -size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* ssl); +CIVETWEB_API size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* ssl); /* Add, edit or delete the entry in the passwords file. @@ -245,14 +259,14 @@ size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* Return: 1 on success, 0 on error. */ -int mg_modify_passwords_file(const char *passwords_file_name, - const char *domain, - const char *user, - const char *password); +CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name, + const char *domain, + const char *user, + const char *password); /* Return information associated with the request. */ -struct mg_request_info *mg_get_request_info(struct mg_connection *); +CIVETWEB_API struct mg_request_info *mg_get_request_info(struct mg_connection *); /* Send data to the client. @@ -260,7 +274,7 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *); 0 when the connection has been closed -1 on error >0 number of bytes written on success */ -int mg_write(struct mg_connection *, const void *buf, size_t len); +CIVETWEB_API int mg_write(struct mg_connection *, const void *buf, size_t len); /* Send data to a websocket client wrapped in a websocket frame. Uses mg_lock @@ -275,16 +289,16 @@ int mg_write(struct mg_connection *, const void *buf, size_t len); 0 when the connection has been closed -1 on error >0 number of bytes written on success */ -int mg_websocket_write(struct mg_connection* conn, int opcode, - const char *data, size_t data_len); +CIVETWEB_API int mg_websocket_write(struct mg_connection* conn, int opcode, + const char *data, size_t data_len); /* Blocks until unique access is obtained to this connection. Intended for use with websockets only. Invoke this before mg_write or mg_printf when communicating with a websocket if your code has server-initiated communication as well as communication in direct response to a message. */ -void mg_lock(struct mg_connection* conn); -void mg_unlock(struct mg_connection* conn); +CIVETWEB_API void mg_lock(struct mg_connection* conn); +CIVETWEB_API void mg_unlock(struct mg_connection* conn); /* Opcodes, from http://tools.ietf.org/html/rfc6455 */ enum { @@ -319,12 +333,12 @@ enum { /* Send data to the client using printf() semantics. Works exactly like mg_write(), but allows to do message formatting. */ -int mg_printf(struct mg_connection *, - PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); +CIVETWEB_API int mg_printf(struct mg_connection *, + PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); /* Send contents of the entire file together with HTTP headers. */ -void mg_send_file(struct mg_connection *conn, const char *path); +CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path); /* Read data from the remote end, return number of bytes read. @@ -332,7 +346,7 @@ void mg_send_file(struct mg_connection *conn, const char *path); 0 connection has been closed by peer. No more data could be read. < 0 read error. No more data could be read from the connection. > 0 number of bytes read into the buffer. */ -int mg_read(struct mg_connection *, void *buf, size_t len); +CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len); /* Get the value of particular HTTP header. @@ -340,7 +354,7 @@ int mg_read(struct mg_connection *, void *buf, size_t len); This is a helper function. It traverses request_info->http_headers array, and if the header is present in the array, returns its value. If it is not present, NULL is returned. */ -const char *mg_get_header(const struct mg_connection *, const char *name); +CIVETWEB_API const char *mg_get_header(const struct mg_connection *, const char *name); /* Get a value of particular form variable. @@ -362,8 +376,8 @@ const char *mg_get_header(const struct mg_connection *, const char *name); Destination buffer is guaranteed to be '\0' - terminated if it is not NULL or zero length. */ -int mg_get_var(const char *data, size_t data_len, - const char *var_name, char *dst, size_t dst_len); +CIVETWEB_API int mg_get_var(const char *data, size_t data_len, + const char *var_name, char *dst, size_t dst_len); /* Get a value of particular form variable. @@ -388,8 +402,8 @@ int mg_get_var(const char *data, size_t data_len, Destination buffer is guaranteed to be '\0' - terminated if it is not NULL or zero length. */ -int mg_get_var2(const char *data, size_t data_len, - const char *var_name, char *dst, size_t dst_len, size_t occurrence); +CIVETWEB_API int mg_get_var2(const char *data, size_t data_len, + const char *var_name, char *dst, size_t dst_len, size_t occurrence); /* Fetch value of certain cookie variable into the destination buffer. @@ -404,8 +418,8 @@ int mg_get_var2(const char *data, size_t data_len, parameter is not found). -2 (destination buffer is NULL, zero length or too small to hold the value). */ -int mg_get_cookie(const char *cookie, const char *var_name, - char *buf, size_t buf_len); +CIVETWEB_API int mg_get_cookie(const char *cookie, const char *var_name, + char *buf, size_t buf_len); /* Download data from the remote web server. @@ -423,35 +437,35 @@ int mg_get_cookie(const char *cookie, const char *var_name, conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf), "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n"); */ -struct mg_connection *mg_download(const char *host, int port, int use_ssl, - char *error_buffer, size_t error_buffer_size, - PRINTF_FORMAT_STRING(const char *request_fmt), - ...) PRINTF_ARGS(6, 7); +CIVETWEB_API struct mg_connection *mg_download(const char *host, int port, int use_ssl, + char *error_buffer, size_t error_buffer_size, + PRINTF_FORMAT_STRING(const char *request_fmt), + ...) PRINTF_ARGS(6, 7); /* Close the connection opened by mg_download(). */ -void mg_close_connection(struct mg_connection *conn); +CIVETWEB_API void mg_close_connection(struct mg_connection *conn); /* File upload functionality. Each uploaded file gets saved into a temporary file and MG_UPLOAD event is sent. Return number of uploaded files. */ -int mg_upload(struct mg_connection *conn, const char *destination_dir); +CIVETWEB_API int mg_upload(struct mg_connection *conn, const char *destination_dir); /* Convenience function -- create detached thread. Return: 0 on success, non-0 on error. */ typedef void * (*mg_thread_func_t)(void *); -int mg_start_thread(mg_thread_func_t f, void *p); +CIVETWEB_API int mg_start_thread(mg_thread_func_t f, void *p); /* Return builtin mime type for the given file name. For unrecognized extensions, "text/plain" is returned. */ -const char *mg_get_builtin_mime_type(const char *file_name); +CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name); /* Return Civetweb version. */ -const char *mg_version(void); +CIVETWEB_API const char *mg_version(void); /* URL-decode input buffer into destination buffer. 0-terminate the destination buffer. @@ -459,13 +473,13 @@ const char *mg_version(void); uses '+' as character for space, see RFC 1866 section 8.2.1 http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt Return: length of the decoded data, or -1 if dst buffer is too small. */ -int mg_url_decode(const char *src, int src_len, char *dst, - int dst_len, int is_form_url_encoded); +CIVETWEB_API int mg_url_decode(const char *src, int src_len, char *dst, + int dst_len, int is_form_url_encoded); /* URL-encode input buffer into destination buffer. returns the length of the resulting buffer or -1 is the buffer is too small. */ -int mg_url_encode(const char *src, char *dst, size_t dst_len); +CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len); /* MD5 hash given strings. Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of @@ -473,7 +487,7 @@ int mg_url_encode(const char *src, char *dst, size_t dst_len); MD5 hash. Example: char buf[33]; mg_md5(buf, "aa", "bb", NULL); */ -char *mg_md5(char buf[33], ...); +CIVETWEB_API char *mg_md5(char buf[33], ...); /* Print error message to the opened error log stream. @@ -483,11 +497,11 @@ char *mg_md5(char buf[33], ...); ...: variable argument list Example: mg_cry(conn,"i like %s", "logging"); */ -void mg_cry(struct mg_connection *conn, - PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); +CIVETWEB_API void mg_cry(struct mg_connection *conn, + PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); /* utility method to compare two buffers, case incensitive. */ -int mg_strncasecmp(const char *s1, const char *s2, size_t len); +CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len); #ifdef __cplusplus } diff --git a/resources/dll.def b/resources/dll.def deleted file mode 100644 index 95b20b5b..00000000 --- a/resources/dll.def +++ /dev/null @@ -1,32 +0,0 @@ -LIBRARY -EXPORTS - mg_start - mg_stop - mg_set_request_handler - mg_get_option - mg_get_valid_option_names - mg_get_ports - mg_modify_passwords_file - mg_get_request_info - mg_write - mg_websocket_write - mg_lock - mg_unlock - mg_printf - mg_send_file - mg_read - mg_get_header - mg_get_var - mg_get_var2 - mg_get_cookie - mg_download - mg_close_connection - mg_upload - mg_start_thread - mg_get_builtin_mime_type - mg_version - mg_url_decode - mg_url_encode - mg_md5 - mg_cry - mg_strncasecmp From 920e2a3dff4cdc186d945bc1033c159d27de81b0 Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 23 Jan 2014 21:07:19 +0100 Subject: [PATCH 21/80] [win32] - get rid of dll.def and export/import functions based on prepro... --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index cc2d3950..66019387 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,7 @@ Release Notes v1.6 (Under Development) Changes ------- +- Win32: Replace dll.def file by export macros in civetweb.h (CSTAJ) - Server should check the nonce for http digest access authentication (bel) - Hide read-only flag in file dialogs opened by the Edit Settings dialog for the Windows executable (bel) - Add all functions to dll.def, that are in the header (bel) From 4fb97687724f5e8736fefabb7473720744c190aa Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 23 Jan 2014 21:11:35 +0100 Subject: [PATCH 22/80] Update RELEASE_NOTES --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 66019387..d6f67144 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,8 @@ Changes ------- - Win32: Replace dll.def file by export macros in civetweb.h (CSTAJ) +- Base64 encode and decode functions for Lua (bel) +- Support pre-loaded files for the Lua environment (bel) - Server should check the nonce for http digest access authentication (bel) - Hide read-only flag in file dialogs opened by the Edit Settings dialog for the Windows executable (bel) - Add all functions to dll.def, that are in the header (bel) From 4127492415a17bdd84ab32464cc4f572ed338ef9 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 24 Jan 2014 17:54:43 +0100 Subject: [PATCH 23/80] patch to compile civetweb.c with Mingw gcc 4.8.0 on windows (by Eric Trinh) --- src/civetweb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/civetweb.c b/src/civetweb.c index c66620d0..145d3d5e 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -187,14 +187,22 @@ typedef struct { pthread_t *waitingthreadhdls; /* The thread handles. */ } pthread_cond_t; +#ifndef __clockid_t_defined typedef DWORD clockid_t; +#endif +#ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC (1) +#endif +#ifndef CLOCK_REALTIME #define CLOCK_REALTIME (2) +#endif +#ifndef _TIMESPEC_DEFINED struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; +#endif #define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ @@ -1197,6 +1205,7 @@ static int pthread_mutex_unlock(pthread_mutex_t *mutex) return ReleaseMutex(*mutex) == 0 ? -1 : 0; } +#ifndef WIN_PTHREADS_TIME_H static int clock_gettime(clockid_t clk_id, struct timespec *tp) { FILETIME ft; @@ -1232,6 +1241,7 @@ static int clock_gettime(clockid_t clk_id, struct timespec *tp) return ok ? 0 : -1; } +#endif static int pthread_cond_init(pthread_cond_t *cv, const void *unused) { From 87a3b74ee58662001a5bd1832e02da10de368cbd Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 24 Jan 2014 19:07:08 +0100 Subject: [PATCH 24/80] Tell Lua which operating system is running, so scripts may handle differences --- src/civetweb.c | 37 +++++++++++++++++++++++++++++++++++++ src/mod_lua.inl | 4 ++++ test/ajax/echo.lp | 5 ++++- test/ajax/echo.lua | 10 +++++----- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 145d3d5e..9fc30e09 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -256,6 +256,7 @@ struct pollfd { #include #include #include +#include #include #include #include @@ -630,6 +631,8 @@ struct mg_context { unsigned long start_time; /* Server start time, used for authentication */ unsigned long nonce_count; /* Used nonces, used for authentication */ + char *systemName; /* What operating system is running */ + /* linked list of uri handlers */ struct mg_request_handler_info *request_handlers; }; @@ -6411,6 +6414,9 @@ static void free_context(struct mg_context *ctx) pthread_key_delete(sTlsKey); } + /* deallocate system name string */ + free(ctx->systemName); + /* Deallocate context itself */ free(ctx); } @@ -6431,6 +6437,35 @@ void mg_stop(struct mg_context *ctx) #endif /* _WIN32 && !__SYMBIAN32__ */ } +void get_system_name(char **sysName) +{ +#if defined(_WIN32) +#if !defined(__SYMBIAN32__) + char name[128]; + DWORD dwVersion = 0; + DWORD dwMajorVersion = 0; + DWORD dwMinorVersion = 0; + DWORD dwBuild = 0; + + dwVersion = GetVersion(); + + dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); + dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); + dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0); + + sprintf(name, "Windows %d.%d", dwMajorVersion, dwMinorVersion); + *sysName = mg_strdup(name); +#else + *sysName = mg_strdup("Symbian"); +#endif +#else + struct utsname name; + memset(&name, 0, sizeof(name)); + uname(&name); + *sysName = mg_strdup(name.sysname); +#endif +} + struct mg_context *mg_start(const struct mg_callbacks *callbacks, void *user_data, const char **options) @@ -6496,6 +6531,8 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, } } + get_system_name(&ctx->systemName); + /* NOTE(lsm): order is important here. SSL certificates must be initialized before listening ports. UID must be set last. */ if (!set_gpass_option(ctx) || diff --git a/src/mod_lua.inl b/src/mod_lua.inl index fce311fe..10044e07 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -751,6 +751,10 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co reg_string(L, "websocket_root", conn->ctx->config[WEBSOCKET_ROOT]); #endif + if (conn->ctx->systemName) { + reg_string(L, "system", conn->ctx->systemName); + } + /* Export request_info */ lua_pushstring(L, "request_info"); lua_newtable(L); diff --git a/test/ajax/echo.lp b/test/ajax/echo.lp index f524ab86..7276f813 100644 --- a/test/ajax/echo.lp +++ b/test/ajax/echo.lp @@ -1,6 +1,9 @@ \ No newline at end of file diff --git a/test/ajax/echo.lua b/test/ajax/echo.lua index 1834a465..13175162 100644 --- a/test/ajax/echo.lua +++ b/test/ajax/echo.lua @@ -24,12 +24,12 @@ resp = resp .. "}"; -mg.write("HTTP/1.1 200 OK\n") -mg.write("Connection: close\n") -mg.write("Content-Type: text/html\n") -mg.write("Cache-Control: no-cache\n") +mg.write("HTTP/1.1 200 OK\r\n") +mg.write("Connection: close\r\n") +mg.write("Content-Type: text/html\r\n") +mg.write("Cache-Control: no-cache\r\n") --mg.write("Content-Length: " .. resp:len() .. "\n") -mg.write("\n") +mg.write("\r\n") mg.write(resp) From e44dd202ef7e591a5b73fc80e3197cec1dcf15c2 Mon Sep 17 00:00:00 2001 From: Scott Nations Date: Thu, 30 Jan 2014 14:44:00 -0600 Subject: [PATCH 25/80] Unconditionally undef WINDOWS_LEAN_AND_MEAN. This makes things build again with vc9 and vc10. Don't know about vc11 or vc12. --- src/civetweb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index 9fc30e09..738c03d6 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -44,7 +44,7 @@ /* Disable WIN32_LEAN_AND_MEAN. This makes windows.h always include winsock2.h */ -#if defined(WIN32_LEAN_AND_MEAN) && (_MSC_VER <= 1400) +#if defined(WIN32_LEAN_AND_MEAN) #undef WIN32_LEAN_AND_MEAN #endif From 28e896c090f282235ae40680a3c300378684512f Mon Sep 17 00:00:00 2001 From: Scott Nations Date: Thu, 30 Jan 2014 14:50:51 -0600 Subject: [PATCH 26/80] typedef in_port_t to unsigned short int when building for ANDROID. --- src/civetweb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/civetweb.c b/src/civetweb.c index 738c03d6..383e1ff9 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -261,6 +261,10 @@ struct pollfd { #include #include +#if defined(ANDROID) +typedef unsigned short int in_port_t; +#endif + #include #include #include From 469faa2b066eb749dd957dca6b28e2ae4e98e7b3 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 2 Feb 2014 21:34:20 +0100 Subject: [PATCH 27/80] Define MAX_WORKER_THREADS may be overwritten, other defines should at least not give redefinition warnings --- src/civetweb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/civetweb.c b/src/civetweb.c index 383e1ff9..2ad9b7fb 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -27,11 +27,19 @@ #ifdef __linux__ #define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ #endif +#ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ +#endif +#ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ +#endif +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS /* wants this for C++ */ +#endif +#ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ #endif +#endif #if defined (_MSC_VER) /* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */ @@ -80,7 +88,9 @@ #include #include +#ifndef MAX_WORKER_THREADS #define MAX_WORKER_THREADS 1024 +#endif #if defined(_WIN32) && !defined(__SYMBIAN32__) /* Windows specific */ #if defined(_MSC_VER) && _MSC_VER <= 1400 From 87324c1d2f7d06383fe6686ea56d21109d091471 Mon Sep 17 00:00:00 2001 From: CSTAJ Date: Wed, 12 Feb 2014 13:59:54 +0100 Subject: [PATCH 28/80] [memleak] - don't leak pthread_cond_t.waitingthreadhdls --- src/civetweb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index 2ad9b7fb..9eceabe2 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -1344,8 +1344,8 @@ static int pthread_cond_destroy(pthread_cond_t *cv) { EnterCriticalSection(&cv->threadIdSec); assert(cv->waitingthreadcount==0); - cv->waitingthreadhdls = 0; free(cv->waitingthreadhdls); + cv->waitingthreadhdls = 0; LeaveCriticalSection(&cv->threadIdSec); DeleteCriticalSection(&cv->threadIdSec); From dcfb78cbc9357a59945181c21277a4d7d4ff38a3 Mon Sep 17 00:00:00 2001 From: bel Date: Wed, 12 Feb 2014 19:45:00 +0100 Subject: [PATCH 29/80] stack smashing detected - see sunsetbrew/civetweb/issues/54 --- src/civetweb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 2ad9b7fb..19b7c82a 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -5480,11 +5480,11 @@ static int set_ports_option(struct mg_context *ctx) struct socket so, *ptr; in_port_t *portPtr; - struct sockaddr_in sin; + union usa usa; socklen_t len; - memset(&sin, 0, sizeof(sin)); - len = sizeof(sin); + memset(&usa, 0, sizeof(usa)); + len = sizeof(usa); while (success && (list = next_option(list, &vec, NULL)) != NULL) { if (!parse_port_string(&vec, &so)) { @@ -5508,7 +5508,7 @@ static int set_ports_option(struct mg_context *ctx) bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ? sizeof(so.lsa.sin) : sizeof(so.lsa)) != 0 || listen(so.sock, SOMAXCONN) != 0 || - getsockname(so.sock, (struct sockaddr *)&sin, &len) != 0) { + getsockname(so.sock, &(usa.sa), &len) != 0) { mg_cry(fc(ctx), "%s: cannot bind to %.*s: %d (%s)", __func__, (int) vec.len, vec.ptr, ERRNO, strerror(errno)); if (so.sock != INVALID_SOCKET) { @@ -5531,7 +5531,7 @@ static int set_ports_option(struct mg_context *ctx) ctx->listening_sockets = ptr; ctx->listening_sockets[ctx->num_listening_sockets] = so; ctx->listening_ports = portPtr; - ctx->listening_ports[ctx->num_listening_sockets] = ntohs(sin.sin_port); + ctx->listening_ports[ctx->num_listening_sockets] = ntohs(usa.sin.sin_port); ctx->num_listening_sockets++; } } From b5d6587e1737a7bd0432de8c148a963edde961f4 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 15 Feb 2014 22:38:01 +0100 Subject: [PATCH 30/80] Enable CORS, step 1 --- src/civetweb.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 2ad5e089..5b173136 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -3477,7 +3477,8 @@ static void handle_file_request(struct mg_connection *conn, const char *path, struct vec mime_vec; int n; char gz_path[PATH_MAX]; - char const* encoding = ""; + const char *encoding = ""; + const char *cors1, *cors2, *cors3; get_mime_type(conn->ctx, path, &mime_vec); cl = filep->size; @@ -3514,7 +3515,7 @@ static void handle_file_request(struct mg_connection *conn, const char *path, return; } conn->status_code = 206; -cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; + cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; mg_snprintf(conn, range, sizeof(range), "Content-Range: bytes " "%" INT64_FMT "-%" @@ -3523,6 +3524,15 @@ cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; msg = "Partial Content"; } + hdr = mg_get_header(conn, "Origin"); + if (hdr) { + cors1 = "Access-Control-Allow-Origin: "; + cors2 = "*"; + cors3 = "\r\n"; + } else { + cors1 = cors2 = cors3 = ""; + } + /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */ gmt_time_string(date, sizeof(date), &curtime); @@ -3531,6 +3541,7 @@ cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n" + "%s%s%s" "Date: %s\r\n" "Last-Modified: %s\r\n" "Etag: %s\r\n" @@ -3539,7 +3550,9 @@ cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; "Connection: %s\r\n" "Accept-Ranges: bytes\r\n" "%s%s\r\n", - conn->status_code, msg, date, lm, etag, (int) mime_vec.len, + conn->status_code, msg, + cors1, cors2, cors3, + date, lm, etag, (int) mime_vec.len, mime_vec.ptr, cl, suggest_connection_header(conn), range, encoding); if (strcmp(conn->request_info.request_method, "HEAD") != 0) { From c498dde851e9d58fe640cfd7100e6ba32d082b8e Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 15 Feb 2014 22:41:29 +0100 Subject: [PATCH 31/80] Add cross-origin resource sharing (CORS) test --- test/cors.html | 66 ++++++++++++++++++++++++++++++++++++++++++++ test/cors.reply.html | 7 +++++ 2 files changed, 73 insertions(+) create mode 100644 test/cors.html create mode 100644 test/cors.reply.html diff --git a/test/cors.html b/test/cors.html new file mode 100644 index 00000000..2d582c3b --- /dev/null +++ b/test/cors.html @@ -0,0 +1,66 @@ + + + +CORS test + + + + + + +

Cross-origin resource sharing test

+ +

More information on CORS: See enable-cors.org and html5rocks.com.

+ + diff --git a/test/cors.reply.html b/test/cors.reply.html new file mode 100644 index 00000000..26d88dfc --- /dev/null +++ b/test/cors.reply.html @@ -0,0 +1,7 @@ + + +CORS test reply - test OK + +Do not load this page directly - use cors.html instead! + + From a5d0ee5f637f9d16dafeec57ed1752d371b5099b Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 16 Feb 2014 16:50:10 +0100 Subject: [PATCH 32/80] CORS step 2: Take Access-Control-Allow-Origin from the config --- src/civetweb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index 5b173136..e0e709f3 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -558,6 +558,7 @@ enum { #if defined(USE_LUA) && defined(USE_WEBSOCKET) LUA_WEBSOCKET_EXTENSIONS, #endif + ACCESS_CONTROL_ALLOW_ORIGIN, NUM_OPTIONS }; @@ -604,6 +605,7 @@ static const char *config_options[] = { #if defined(USE_LUA) && defined(USE_WEBSOCKET) "lua_websocket_pattern", "**.lua$", #endif + "access_control_allow_origin", "*", NULL }; @@ -3526,8 +3528,10 @@ static void handle_file_request(struct mg_connection *conn, const char *path, hdr = mg_get_header(conn, "Origin"); if (hdr) { + /* Cross-origin resource sharing (CORS), see http://www.html5rocks.com/en/tutorials/cors/, + http://www.html5rocks.com/static/images/cors_server_flowchart.png - preflight is not supported for files. */ cors1 = "Access-Control-Allow-Origin: "; - cors2 = "*"; + cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; cors3 = "\r\n"; } else { cors1 = cors2 = cors3 = ""; From c8684b89f575db24fcfebb7e5f52501df0117466 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 16 Feb 2014 17:38:18 +0100 Subject: [PATCH 33/80] Enhance option parsing. In particular, empty strings might be valid options --- src/main.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index 90929a8d..27ce9d27 100644 --- a/src/main.c +++ b/src/main.c @@ -196,7 +196,7 @@ static char *sdup(const char *str) return p; } -static void set_option(char **options, const char *name, const char *value) +static int set_option(char **options, const char *name, const char *value) { int i; @@ -216,13 +216,16 @@ static void set_option(char **options, const char *name, const char *value) if (i == MAX_OPTIONS - 3) { die("%s", "Too many options specified"); } + + /* TODO: check if this option is defined */ + return 1; } static void read_config_file(const char *config_file, char **options) { - char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p; + char line[MAX_CONF_FILE_LINE_SIZE], *p; FILE *fp = NULL; - size_t i, cmd_line_opts_start = 1, line_no = 0; + size_t i, j, cmd_line_opts_start = 1, line_no = 0; fp = fopen(config_file, "r"); @@ -252,11 +255,23 @@ static void read_config_file(const char *config_file, char **options) continue; } - if (sscanf(p, "%s %[^\r\n#]", opt, val) != 2) { + /* Skip spaces, \r and \n at the end of the line */ + for (j = strlen(line)-1; isspace(* (unsigned char *) &line[j]) || iscntrl(* (unsigned char *) &line[j]); ) line[j--]=0; + + /* Find the space character between option name and value */ + for (j=i; !isspace(* (unsigned char *) &line[j]) && (line[j]!=0); ) j++; + + /* Terminate the string - then the string at (line+i) contains the option name */ + line[j] = 0; + j++; + + /* Trim additional spaces between option name and value - then (line+j) contains the option value */ + while (isspace(line[j])) j++; + + /* Set option */ + if (!set_option(options, line+i, line+j)) { printf("%s: line %d is invalid, ignoring it:\n %s", config_file, (int) line_no, p); - } else { - set_option(options, opt, val); } } From 60cfbf144432a4404dab07823ca0c062fc6c80e9 Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 17 Feb 2014 18:24:45 +0100 Subject: [PATCH 34/80] CORS support (Step 3) --- RELEASE_NOTES.md | 15 ++++++++------- src/civetweb.c | 2 ++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d6f67144..c2d34f3d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,7 @@ Release Notes v1.6 (Under Development) Changes ------- +- Support Cross-Origin Resource Sharing (CORS) for static files and scripts (bel) - Win32: Replace dll.def file by export macros in civetweb.h (CSTAJ) - Base64 encode and decode functions for Lua (bel) - Support pre-loaded files for the Lua environment (bel) @@ -14,7 +15,7 @@ Changes - Added Lua extensions: send_file, get_var, get_mime_type, get_cookie, url_decode, url_encode (bel) - mg_set_request_handler() mod to use pattern (bel, Patch from Toni Wilk) - Solved, tested and documented SSL support for Windows (bel) -- Fixed: select for Linux needs the nfds parameter set correctly (bel) +- Fixed: select for Linux needs the nfds parameter set correctly (bel) - Add methods for returning the ports civetweb is listening on (keithel) - Fixes for Lua Server Pages, as described within the google groups thread. (bel) - Added support for plain Lua Scripts, and an example script. (bel) @@ -36,7 +37,7 @@ Changes - Corrected bad mask flag/opcode passing to websocket callback (William Greathouse) - Moved CEVITWEB_VERSION define into civetweb.h -- Added new simple zip deployment build for Windows. +- Added new simple zip deployment build for Windows. - Removed windows install package build. - Fixes page violation in mod_lua.inl (apkbox) - Use C style comments to enable compiling most of civetweb with -ansi. (F-Secure Corporation) @@ -85,7 +86,7 @@ Changes - Conformed source files to UNIX line endings for consistency. - Unified the coding style to improve reability. -Release Notes v1.3 +Release Notes v1.3 === ### Objectives: *Buildroot Integration* @@ -97,7 +98,7 @@ Changes - Updated documentation - Updated Buildroot config example -Release Notes v1.2 +Release Notes v1.2 === ### Objectives: *Installation Improvements, buildroot, cross compile support* The objective of this release is to make installation seamless. @@ -121,7 +122,7 @@ Known Issues - The prebuilt Window's version requires [Visual C++ Redistributable for Visual Studio 2012](http://www.microsoft.com/en-us/download/details.aspx?id=30679) -Release Notes v1.1 +Release Notes v1.1 === ### Objectives: *Build, Documentation, License Improvements* The objective of this release is to establish a maintable code base, ensure MIT license rights and improve usability and documentation. @@ -150,7 +151,7 @@ Changes + Removed yaSSL from the OSX build, not needed. - Added new Visual Studio projects for Windows builds. + Removed Windows support from Makefiles - + Provided additional, examples with Lua, and another with yaSSL. + + Provided additional, examples with Lua, and another with yaSSL. - Changed Zombie Reaping policy to not ignore SIGCHLD. + The previous method caused trouble in applciations that spawn children. @@ -160,7 +161,7 @@ Known Issues - Build support for VS6 and some other has been deprecated. + This does not impact embedded programs, just the stand-alone build. + The old Makefile was renamed to Makefile.deprecated. - + This is partcially do to lack fo testing. + + This is partcially do to lack fo testing. + Need to find out what is actually in demand. - Build changes may impact current users. + As with any change of this type, changes may impact some users. diff --git a/src/civetweb.c b/src/civetweb.c index e0e709f3..2e57a2cd 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -5338,6 +5338,8 @@ static void handle_request(struct mg_connection *conn) use_request_handler(conn)) { /* Do nothing, callback has served the request */ } else if (!is_script_resource && !strcmp(ri->request_method, "OPTIONS")) { + /* Scripts should support the OPTIONS method themselves, to allow a maximum flexibility. + Lua and CGI scripts may fully support CORS this way (including preflights). */ send_options(conn); } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) { send_http_error(conn, 404, "Not Found", "Not Found"); From ee3ef0af4e96f10f4bf6f57dd0f83bfeb029f9e2 Mon Sep 17 00:00:00 2001 From: Sangwhan Moon Date: Thu, 20 Feb 2014 03:25:20 +0900 Subject: [PATCH 35/80] Make the Websockets example compile on Mac OS X, along with some other little patches. --- examples/websocket/WebSockCallbacks.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/websocket/WebSockCallbacks.c b/examples/websocket/WebSockCallbacks.c index 36cf68f4..df547e43 100644 --- a/examples/websocket/WebSockCallbacks.c +++ b/examples/websocket/WebSockCallbacks.c @@ -3,6 +3,10 @@ #include #include "WebSockCallbacks.h" +#ifdef __APPLE__ +#include +#endif + #ifdef _WIN32 #include typedef HANDLE pthread_mutex_t; @@ -73,7 +77,7 @@ void websocket_ready_handler(struct mg_connection *conn) { break; } } - printf("\nNew websocket attached: %08x:%u\n", rq->remote_ip, rq->remote_port); + printf("\nNew websocket attached: %08lx:%u\n", rq->remote_ip, rq->remote_port); pthread_mutex_unlock(&sMutex); } @@ -88,7 +92,7 @@ static void websocket_done(tWebSockInfo * wsock) { break; } } - printf("\nClose websocket attached: %08x:%u\n", mg_get_request_info(wsock->conn)->remote_ip, mg_get_request_info(wsock->conn)->remote_port); + printf("\nClose websocket attached: %08lx:%u\n", mg_get_request_info(wsock->conn)->remote_ip, mg_get_request_info(wsock->conn)->remote_port); free(wsock); } } @@ -107,7 +111,7 @@ int websocket_data_handler(struct mg_connection *conn, int flags, char *data, si pthread_mutex_unlock(&sMutex); return 1; } - if ((data_len>=5) && (data_len<100) && (flags==129) || (flags==130)) { + if (((data_len>=5) && (data_len<100) && (flags==129)) || (flags==130)) { // init command if ((wsock->webSockState==1) && (!memcmp(data,"init ",5))) { From b23e1c2a550b0f3805f38a71059186372a322154 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 21 Feb 2014 22:05:22 +0100 Subject: [PATCH 36/80] Prepare better parameter checking --- include/civetweb.h | 17 +++++++++ src/civetweb.c | 90 +++++++++++++++++++++++++--------------------- src/main.c | 2 +- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/include/civetweb.h b/include/civetweb.h index d6f4c55d..a31ebeaa 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -238,6 +238,23 @@ CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx, const char Array is NULL terminated. */ CIVETWEB_API const char **mg_get_valid_option_names(void); + +struct mg_option { + const char * name; + int type; + const char * default_value; +}; + +enum { + CONFIG_TYPE_UNKNOWN = 0x0, + CONFIG_TYPE_NUMBER = 0x1, + CONFIG_TYPE_STRING = 0x2, + CONFIG_TYPE_FILE = 0x3, + CONFIG_TYPE_DIRECTORY = 0x4, + CONFIG_TYPE_BOOLEAN = 0x5, + CONFIG_TYPE_EXT_PATTERN = 0x6 +}; + /* Get the list of ports that civetweb is listening on. size is the size of the ports int array and ssl int array to fill. It is the caller's responsibility to make sure ports and ssl each diff --git a/src/civetweb.c b/src/civetweb.c index 2e57a2cd..d240e78a 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -563,51 +563,51 @@ enum { NUM_OPTIONS }; -static const char *config_options[] = { - "cgi_pattern", "**.cgi$|**.pl$|**.php$", - "cgi_environment", NULL, - "put_delete_auth_file", NULL, - "cgi_interpreter", NULL, - "protect_uri", NULL, - "authentication_domain", "mydomain.com", - "ssi_pattern", "**.shtml$|**.shtm$", - "throttle", NULL, - "access_log_file", NULL, - "enable_directory_listing", "yes", - "error_log_file", NULL, - "global_auth_file", NULL, - "index_files", +static struct mg_option config_options[] = { + {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, + {"cgi_environment", CONFIG_TYPE_STRING, NULL}, + {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL}, + {"cgi_interpreter", CONFIG_TYPE_FILE, NULL}, + {"protect_uri", 12345, NULL}, + {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"}, + {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"}, + {"throttle", 12345, NULL}, + {"access_log_file", CONFIG_TYPE_FILE, NULL}, + {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"}, + {"error_log_file", CONFIG_TYPE_FILE, NULL}, + {"global_auth_file", CONFIG_TYPE_FILE, NULL}, + {"index_files", 12345, #ifdef USE_LUA - "index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php", + "index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php"}, #else - "index.html,index.htm,index.cgi,index.shtml,index.php", + "index.html,index.htm,index.cgi,index.shtml,index.php"}, #endif - "enable_keep_alive", "no", - "access_control_list", NULL, - "extra_mime_types", NULL, - "listening_ports", "8080", - "document_root", NULL, - "ssl_certificate", NULL, - "num_threads", "50", - "run_as_user", NULL, - "url_rewrite_patterns", NULL, - "hide_files_patterns", NULL, - "request_timeout_ms", "30000", + {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"}, + {"access_control_list", 12345, NULL}, + {"extra_mime_types", 12345, NULL}, + {"listening_ports", 12345, "8080"}, + {"document_root", CONFIG_TYPE_DIRECTORY, NULL}, + {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, + {"num_threads", CONFIG_TYPE_NUMBER, "50"}, + {"run_as_user", 12345, NULL}, + {"url_rewrite_patterns", 12345, NULL}, + {"hide_files_patterns", 12345, NULL}, + {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, #if defined(USE_LUA) - "lua_preload_file", NULL, - "lua_script_pattern", "**.lua$", - "lua_server_page_pattern", "**.lp$|**.lsp$", + {"lua_preload_file", CONFIG_TYPE_FILE, NULL}, + {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, + {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, #endif #if defined(USE_WEBSOCKET) - "websocket_root", NULL, + {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL}, #endif #if defined(USE_LUA) && defined(USE_WEBSOCKET) - "lua_websocket_pattern", "**.lua$", + {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, #endif - "access_control_allow_origin", "*", + {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"}, - NULL + {NULL, CONFIG_TYPE_UNKNOWN, NULL} }; struct mg_request_handler_info { @@ -702,10 +702,20 @@ struct de { static int is_websocket_request(const struct mg_connection *conn); #endif +/*#if defined(MG_LEGACY_INTERFACE)*/ const char **mg_get_valid_option_names(void) { - return config_options; + static const char * data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; + int i; + + for (i=0; config_options[i].name != NULL; i++) { + data[i * 2] = config_options[i].name; + data[i * 2 + 1] = config_options[i].default_value; + } + + return data; } +/*#endif*/ static int is_file_in_memory(struct mg_connection *conn, const char *path, struct file *filep) @@ -753,8 +763,8 @@ static int get_option_index(const char *name) { int i; - for (i = 0; config_options[i * 2] != NULL; i++) { - if (strcmp(config_options[i * 2], name) == 0) { + for (i = 0; config_options[i].name != NULL; i++) { + if (strcmp(config_options[i].name, name) == 0) { return i; } } @@ -6517,7 +6527,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, /* Check if the config_options and the corresponding enum have compatible sizes. */ /* Could use static_assert, once it is verified that all compilers support this. */ - assert(sizeof(config_options)/sizeof(config_options[0]) == NUM_OPTIONS*2+1); + assert(sizeof(config_options)/sizeof(config_options[0]) == NUM_OPTIONS+1); /* Allocate context and initialize reasonable general case defaults. TODO(lsm): do proper error handling here. */ @@ -6557,8 +6567,8 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, } /* Set default value if needed */ - for (i = 0; config_options[i * 2] != NULL; i++) { - default_value = config_options[i * 2 + 1]; + for (i = 0; config_options[i].name != NULL; i++) { + default_value = config_options[i].default_value; if (ctx->config[i] == NULL && default_value != NULL) { ctx->config[i] = mg_strdup(default_value); } diff --git a/src/main.c b/src/main.c index 27ce9d27..e67ec8b7 100644 --- a/src/main.c +++ b/src/main.c @@ -217,7 +217,7 @@ static int set_option(char **options, const char *name, const char *value) die("%s", "Too many options specified"); } - /* TODO: check if this option is defined */ + /* TODO: check if this option is defined and the correct data type, return 1 (OK) or 0 (false) */ return 1; } From 7ebe84d9dbde4dcf7363141b969c9dc33df2379d Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 21 Feb 2014 22:55:37 +0100 Subject: [PATCH 37/80] civetweb_lua build may use websockets --- VS2012/civetweb_lua/civetweb_lua.vcxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/VS2012/civetweb_lua/civetweb_lua.vcxproj b/VS2012/civetweb_lua/civetweb_lua.vcxproj index 35b0e22a..c8a1f6b4 100644 --- a/VS2012/civetweb_lua/civetweb_lua.vcxproj +++ b/VS2012/civetweb_lua/civetweb_lua.vcxproj @@ -88,7 +88,7 @@ Level3 Disabled - LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) $(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories) @@ -102,7 +102,7 @@ Level3 Disabled - LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) $(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories) @@ -118,7 +118,7 @@ MaxSpeed true true - LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) $(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories) @@ -136,7 +136,7 @@ MaxSpeed true true - LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) $(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories) From 4b12cd36737d7ba90669a8bf941395ec92ddb328 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 21 Feb 2014 22:56:56 +0100 Subject: [PATCH 38/80] New interface for reading config options --- include/civetweb.h | 7 ++++ src/civetweb.c | 12 +++++-- src/main.c | 80 +++++++++++++++++++++++----------------------- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/include/civetweb.h b/include/civetweb.h index a31ebeaa..24d52465 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -232,11 +232,14 @@ CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, const char *uri CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx, const char *name); +#if defined(MG_LEGACY_INTERFACE) /* Return array of strings that represent valid configuration options. For each option, option name and default value is returned, i.e. the number of entries in the array equals to number_of_options x 2. Array is NULL terminated. */ +/* Deprecated: Use mg_get_valid_options instead. */ CIVETWEB_API const char **mg_get_valid_option_names(void); +#endif struct mg_option { @@ -255,6 +258,10 @@ enum { CONFIG_TYPE_EXT_PATTERN = 0x6 }; +CIVETWEB_API const struct mg_option *mg_get_valid_options(void); + + + /* Get the list of ports that civetweb is listening on. size is the size of the ports int array and ssl int array to fill. It is the caller's responsibility to make sure ports and ssl each diff --git a/src/civetweb.c b/src/civetweb.c index d240e78a..37c080bf 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -702,12 +702,12 @@ struct de { static int is_websocket_request(const struct mg_connection *conn); #endif -/*#if defined(MG_LEGACY_INTERFACE)*/ +#if defined(MG_LEGACY_INTERFACE) const char **mg_get_valid_option_names(void) { static const char * data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; int i; - + for (i=0; config_options[i].name != NULL; i++) { data[i * 2] = config_options[i].name; data[i * 2 + 1] = config_options[i].default_value; @@ -715,7 +715,13 @@ const char **mg_get_valid_option_names(void) return data; } -/*#endif*/ +#endif + +const struct mg_option *mg_get_valid_options(void) +{ + return config_options; +} + static int is_file_in_memory(struct mg_connection *conn, const char *path, struct file *filep) diff --git a/src/main.c b/src/main.c index e67ec8b7..f421b529 100644 --- a/src/main.c +++ b/src/main.c @@ -116,7 +116,7 @@ static void die(const char *fmt, ...) static void show_usage_and_exit(void) { - const char **names; + const struct mg_option *options; int i; fprintf(stderr, "Civetweb v%s, built on %s\n", @@ -127,10 +127,10 @@ static void show_usage_and_exit(void) fprintf(stderr, " civetweb [-option value ...]\n"); fprintf(stderr, "\nOPTIONS:\n"); - names = mg_get_valid_option_names(); - for (i = 0; names[i] != NULL; i += 2) { + options = mg_get_valid_options(); + for (i = 0; options[i].name != NULL; i++) { fprintf(stderr, " -%s %s\n", - names[i], names[i + 1] == NULL ? "" : names[i + 1]); + options[i].name[i], options[i].default_value == NULL ? "" : options[i].default_value); } exit(EXIT_FAILURE); } @@ -165,7 +165,8 @@ static const char *get_url_to_first_open_port(const struct mg_context *ctx) static void create_config_file(const char *path) { - const char **names, *value; + const struct mg_option *options; + const char *value; FILE *fp; int i; @@ -174,10 +175,10 @@ static void create_config_file(const char *path) fclose(fp); } else if ((fp = fopen(path, "a+")) != NULL) { fprintf(fp, "%s", config_file_top_comment); - names = mg_get_valid_option_names(); - for (i = 0; names[i * 2] != NULL; i++) { - value = mg_get_option(ctx, names[i * 2]); - fprintf(fp, "# %s %s\n", names[i * 2], value ? value : ""); + options = mg_get_valid_options(); + for (i = 0; options[i].name != NULL; i++) { + value = mg_get_option(ctx, options[i].name); + fprintf(fp, "# %s %s\n", options[i].name, value ? value : ""); } fclose(fp); } @@ -597,25 +598,25 @@ static int is_numeric_options(const char *option_name) static void save_config(HWND hDlg, FILE *fp) { char value[2000] = ""; - const char **options, *name, *default_value; + const char *default_value; + const struct mg_option *options; int i, id; fprintf(fp, "%s", config_file_top_comment); - options = mg_get_valid_option_names(); - for (i = 0; options[i * 2] != NULL; i++) { - name = options[i * 2]; + options = mg_get_valid_options(); + for (i = 0; options[i].name != NULL; i++) { id = ID_CONTROLS + i; - if (is_boolean_option(name)) { + if (is_boolean_option(options[i].name)) { snprintf(value, sizeof(value)-1, "%s", IsDlgButtonChecked(hDlg, id) ? "yes" : "no"); value[sizeof(value)-1] = 0; } else { GetDlgItemText(hDlg, id, value, sizeof(value)); } - default_value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1]; + default_value = options[i].default_value == NULL ? "" : options[i].default_value; /* If value is the same as default, skip it */ if (strcmp(value, default_value) != 0) { - fprintf(fp, "%s %s\n", name, value); + fprintf(fp, "%s %s\n", options[i].name, value); } } } @@ -625,7 +626,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) FILE *fp; int i, j; const char *name, *value; - const char **default_options = mg_get_valid_option_names(); + const struct mg_option *default_options = mg_get_valid_options(); char *file_options[MAX_OPTIONS] = {0}; switch (msg) { @@ -648,9 +649,9 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) break; case ID_RESET_DEFAULTS: - for (i = 0; default_options[i * 2] != NULL; i++) { - name = default_options[i * 2]; - value = default_options[i * 2 + 1] == NULL ? "" : default_options[i * 2 + 1]; + for (i = 0; default_options[i].name != NULL; i++) { + name = default_options[i].name; + value = default_options[i].default_value == NULL ? "" : default_options[i].default_value; if (is_boolean_option(name)) { CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? BST_CHECKED : BST_UNCHECKED); @@ -662,9 +663,9 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) case ID_RESET_FILE: read_config_file(config_file, file_options); - for (i = 0; default_options[i * 2] != NULL; i++) { - name = default_options[i * 2]; - value = default_options[i * 2 + 1]; + for (i = 0; default_options[i].name != NULL; i++) { + name = default_options[i].name; + value = default_options[i].default_value; for (j = 0; file_options[j * 2] != NULL; j++) { if (!strcmp(name, file_options[j * 2])) { value = file_options[j * 2 + 1]; @@ -685,8 +686,8 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) break; case ID_RESET_ACTIVE: - for (i = 0; default_options[i * 2] != NULL; i++) { - name = default_options[i * 2]; + for (i = 0; default_options[i].name != NULL; i++) { + name = default_options[i].name; value = mg_get_option(ctx, name); if (is_boolean_option(name)) { CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? @@ -698,8 +699,8 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) break; } - for (i = 0; default_options[i * 2] != NULL; i++) { - name = default_options[i * 2]; + for (i = 0; default_options[i].name != NULL; i++) { + name = default_options[i].name; if ((is_filename_option(name) || is_directory_option(name)) && LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) { OPENFILENAME of; @@ -737,8 +738,8 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon); SetWindowText(hDlg, "Civetweb settings"); SetFocus(GetDlgItem(hDlg, ID_SAVE)); - for (i = 0; default_options[i * 2] != NULL; i++) { - name = default_options[i * 2]; + for (i = 0; default_options[i].name != NULL; i++) { + name = default_options[i].name; value = mg_get_option(ctx, name); if (is_boolean_option(name)) { CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? @@ -793,11 +794,11 @@ static void add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type, static void show_settings_dialog() { #define HEIGHT 15 -#define WIDTH 450 -#define LABEL_WIDTH 85 +#define WIDTH 460 +#define LABEL_WIDTH 90 unsigned char mem[4096], *p; - const char **option_names, *long_option_name; + const struct mg_option *options; DWORD style; DLGTEMPLATE *dia = (DLGTEMPLATE *) mem; WORD i, cl, x, y, width, nelems = 0; @@ -826,22 +827,21 @@ static void show_settings_dialog() (void) memcpy(mem, &dialog_header, sizeof(dialog_header)); p = mem + sizeof(dialog_header); - option_names = mg_get_valid_option_names(); - for (i = 0; option_names[i * 2] != NULL; i++) { - long_option_name = option_names[i * 2]; + options = mg_get_valid_options(); + for (i = 0; options[i].name != NULL; i++) { style = WS_CHILD | WS_VISIBLE | WS_TABSTOP; x = 10 + (WIDTH / 2) * (nelems % 2); y = (nelems/2 + 1) * HEIGHT + 5; width = WIDTH / 2 - 20 - LABEL_WIDTH; - if (is_numeric_options(long_option_name)) { + if (is_numeric_options(options[i].name)) { style |= ES_NUMBER; cl = 0x81; style |= WS_BORDER | ES_AUTOHSCROLL; - } else if (is_boolean_option(long_option_name)) { + } else if (is_boolean_option(options[i].name)) { cl = 0x80; style |= BS_AUTOCHECKBOX; - } else if (is_filename_option(long_option_name) || - is_directory_option(long_option_name)) { + } else if (is_filename_option(options[i].name) || + is_directory_option(options[i].name)) { style |= WS_BORDER | ES_AUTOHSCROLL; width -= 20; cl = 0x81; @@ -855,7 +855,7 @@ static void show_settings_dialog() style |= WS_BORDER | ES_AUTOHSCROLL; } add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD, - x, y, LABEL_WIDTH, HEIGHT, long_option_name); + x, y, LABEL_WIDTH, HEIGHT, options[i].name); add_control(&p, dia, cl, ID_CONTROLS + i, style, (WORD) (x + LABEL_WIDTH), y, width, 12, ""); nelems++; From 674c00dfe00036a1ae7646bbfc54629abca6eb0c Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 21 Feb 2014 23:45:43 +0100 Subject: [PATCH 39/80] Replace is_boolean_option --- src/main.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index f421b529..bc7c1ab1 100644 --- a/src/main.c +++ b/src/main.c @@ -561,6 +561,7 @@ static void *align(void *ptr, DWORD alig) return ((void *) ul); } +/* static int is_boolean_option(const char *option_name) { return !strcmp(option_name, "enable_directory_listing") || @@ -594,6 +595,7 @@ static int is_numeric_options(const char *option_name) return !strcmp(option_name, "num_threads") || !strcmp(option_name, "request_timeout_ms"); } +*/ static void save_config(HWND hDlg, FILE *fp) { @@ -606,7 +608,7 @@ static void save_config(HWND hDlg, FILE *fp) options = mg_get_valid_options(); for (i = 0; options[i].name != NULL; i++) { id = ID_CONTROLS + i; - if (is_boolean_option(options[i].name)) { + if (options[i].type == CONFIG_TYPE_BOOLEAN) { snprintf(value, sizeof(value)-1, "%s", IsDlgButtonChecked(hDlg, id) ? "yes" : "no"); value[sizeof(value)-1] = 0; @@ -652,7 +654,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) for (i = 0; default_options[i].name != NULL; i++) { name = default_options[i].name; value = default_options[i].default_value == NULL ? "" : default_options[i].default_value; - if (is_boolean_option(name)) { + if (default_options[i].type == CONFIG_TYPE_BOOLEAN) { CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? BST_CHECKED : BST_UNCHECKED); } else { @@ -674,7 +676,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) if (value == NULL) { value = ""; } - if (is_boolean_option(name)) { + if (default_options[i].type == CONFIG_TYPE_BOOLEAN) { CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? BST_CHECKED : BST_UNCHECKED); } else { SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value); @@ -689,7 +691,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) for (i = 0; default_options[i].name != NULL; i++) { name = default_options[i].name; value = mg_get_option(ctx, name); - if (is_boolean_option(name)) { + if (default_options[i].type == CONFIG_TYPE_BOOLEAN) { CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? BST_CHECKED : BST_UNCHECKED); } else { @@ -741,7 +743,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) for (i = 0; default_options[i].name != NULL; i++) { name = default_options[i].name; value = mg_get_option(ctx, name); - if (is_boolean_option(name)) { + if (default_options[i].type == CONFIG_TYPE_BOOLEAN) { CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? BST_CHECKED : BST_UNCHECKED); } else { @@ -837,7 +839,7 @@ static void show_settings_dialog() style |= ES_NUMBER; cl = 0x81; style |= WS_BORDER | ES_AUTOHSCROLL; - } else if (is_boolean_option(options[i].name)) { + } else if (options[i].type == CONFIG_TYPE_BOOLEAN) { cl = 0x80; style |= BS_AUTOCHECKBOX; } else if (is_filename_option(options[i].name) || From 82cdef76c12a6dd77323ff795284563b4593e87b Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 21 Feb 2014 23:47:35 +0100 Subject: [PATCH 40/80] Replace is_numeric_option --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index bc7c1ab1..abae8107 100644 --- a/src/main.c +++ b/src/main.c @@ -835,7 +835,7 @@ static void show_settings_dialog() x = 10 + (WIDTH / 2) * (nelems % 2); y = (nelems/2 + 1) * HEIGHT + 5; width = WIDTH / 2 - 20 - LABEL_WIDTH; - if (is_numeric_options(options[i].name)) { + if (options[i].type == CONFIG_TYPE_NUMBER) { style |= ES_NUMBER; cl = 0x81; style |= WS_BORDER | ES_AUTOHSCROLL; From 5ca2eb6250c709fa09307519dc7263e29abc18ea Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 21 Feb 2014 23:52:10 +0100 Subject: [PATCH 41/80] Replace is_directory_option --- src/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index abae8107..b185ed23 100644 --- a/src/main.c +++ b/src/main.c @@ -703,7 +703,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) for (i = 0; default_options[i].name != NULL; i++) { name = default_options[i].name; - if ((is_filename_option(name) || is_directory_option(name)) && + if ((is_filename_option(name) || (default_options[i].type == CONFIG_TYPE_DIRECTORY)) && LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) { OPENFILENAME of; BROWSEINFO bi; @@ -722,7 +722,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) bi.lpszTitle = "Choose WWW root directory:"; bi.ulFlags = BIF_RETURNONLYFSDIRS; - if (is_directory_option(name)) { + if (default_options[i].type == CONFIG_TYPE_DIRECTORY) { SHGetPathFromIDList(SHBrowseForFolder(&bi), path); } else { GetOpenFileName(&of); @@ -843,7 +843,7 @@ static void show_settings_dialog() cl = 0x80; style |= BS_AUTOCHECKBOX; } else if (is_filename_option(options[i].name) || - is_directory_option(options[i].name)) { + (options[i].type == CONFIG_TYPE_DIRECTORY)) { style |= WS_BORDER | ES_AUTOHSCROLL; width -= 20; cl = 0x81; From 55324460ac2064f927e6ceffc916954d4e4fc2f7 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 00:11:40 +0100 Subject: [PATCH 42/80] Replace is_file_option --- src/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index b185ed23..1e7f1aed 100644 --- a/src/main.c +++ b/src/main.c @@ -703,7 +703,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) for (i = 0; default_options[i].name != NULL; i++) { name = default_options[i].name; - if ((is_filename_option(name) || (default_options[i].type == CONFIG_TYPE_DIRECTORY)) && + if (((default_options[i].type == CONFIG_TYPE_FILE) || (default_options[i].type == CONFIG_TYPE_DIRECTORY)) && LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) { OPENFILENAME of; BROWSEINFO bi; @@ -842,7 +842,7 @@ static void show_settings_dialog() } else if (options[i].type == CONFIG_TYPE_BOOLEAN) { cl = 0x80; style |= BS_AUTOCHECKBOX; - } else if (is_filename_option(options[i].name) || + } else if ((options[i].type == CONFIG_TYPE_FILE) || (options[i].type == CONFIG_TYPE_DIRECTORY)) { style |= WS_BORDER | ES_AUTOHSCROLL; width -= 20; From 96bfe243d5b2e23aae05f3689142c9ae0226b937 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 09:34:20 +0100 Subject: [PATCH 43/80] Check if an option with the given name exists --- src/main.c | 50 +++++++++++++------------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/src/main.c b/src/main.c index 1e7f1aed..0510c456 100644 --- a/src/main.c +++ b/src/main.c @@ -199,7 +199,19 @@ static char *sdup(const char *str) static int set_option(char **options, const char *name, const char *value) { - int i; + int i, found; + const struct mg_option *default_options = mg_get_valid_options(); + + found = 0; + for (i = 0; default_options[i].name != 0; i++) { + if (!strcmp(default_options[i].name, name)) { + found = 1; + } + } + if (!found) { + /* unknown option */ + return 0; + } for (i = 0; i < MAX_OPTIONS - 3; i++) { if (options[i] == NULL) { @@ -561,42 +573,6 @@ static void *align(void *ptr, DWORD alig) return ((void *) ul); } -/* -static int is_boolean_option(const char *option_name) -{ - return !strcmp(option_name, "enable_directory_listing") || - !strcmp(option_name, "enable_keep_alive"); -} - -static int is_filename_option(const char *option_name) -{ - return !strcmp(option_name, "cgi_interpreter") || - !strcmp(option_name, "global_auth_file") || - !strcmp(option_name, "put_delete_auth_file") || - !strcmp(option_name, "access_log_file") || - !strcmp(option_name, "error_log_file") || -#ifdef USE_LUA - !strcmp(option_name, "lua_preload_file") || -#endif - !strcmp(option_name, "ssl_certificate"); -} - -static int is_directory_option(const char *option_name) -{ - return !strcmp(option_name, "document_root") || -#if defined(USE_WEBSOCKET) - !strcmp(option_name, "websocket_root") || -#endif - 0; -} - -static int is_numeric_options(const char *option_name) -{ - return !strcmp(option_name, "num_threads") || - !strcmp(option_name, "request_timeout_ms"); -} -*/ - static void save_config(HWND hDlg, FILE *fp) { char value[2000] = ""; From aa4ebb5e9d5872c1a5d79cf49568ff56408888bf Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 10:17:03 +0100 Subject: [PATCH 44/80] Check option type --- src/main.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index 0510c456..7eacf3a3 100644 --- a/src/main.c +++ b/src/main.c @@ -199,18 +199,31 @@ static char *sdup(const char *str) static int set_option(char **options, const char *name, const char *value) { - int i, found; + int i, type; const struct mg_option *default_options = mg_get_valid_options(); - found = 0; + type = CONFIG_TYPE_UNKNOWN; for (i = 0; default_options[i].name != 0; i++) { if (!strcmp(default_options[i].name, name)) { - found = 1; + type = default_options[i].type; } } - if (!found) { - /* unknown option */ - return 0; + switch (type) { + case CONFIG_TYPE_UNKNOWN: + /* unknown option */ + return 0; + case CONFIG_TYPE_NUMBER: + if (atol(value)<1) { + /* invalid number */ + return 0; + } + break; + case CONFIG_TYPE_BOOLEAN: + if ((0!=strcmp(value,"yes")) && (0!=strcmp(value,"no"))) { + /* invalid boolean */ + return 0; + } + break; } for (i = 0; i < MAX_OPTIONS - 3; i++) { From b35c6742dd59715b0145576d28e2080f63838ad3 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 21:18:25 +0100 Subject: [PATCH 45/80] Enhance CORS GET test --- test/cors.html | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/cors.html b/test/cors.html index 2d582c3b..2443ea4b 100644 --- a/test/cors.html +++ b/test/cors.html @@ -32,7 +32,7 @@ function getTitle(text) { // Make the actual CORS request. function makeCorsRequest() { - var url = "http://localhost/cors.reply.html"; + var url = "http://localhost/cors.reply.html"; var xhr = createCORSRequest('GET', url); if (!xhr) { alert('CORS not supported'); @@ -53,14 +53,20 @@ function makeCorsRequest() { xhr.send(); } -function start() {} - +function start() { + var el = document.getElementById("from"); + el.innerHTML = "Test CORS from " + document.URL + " to http://localhost/cors.reply.*"; + if ((document.URL.indexOf("localhost") >= 0) || (document.URL.indexOf("127.0.0.1") >= 0)) { + alert("This CORS test is only meaningful, if you open this site with a different url than \'localhost\' (127.0.0.1).\nYou may use a different IP of the same machine."); + } +}

Cross-origin resource sharing test

- +

*** Error: Javascript is not activated. This test will not work. ***

+

More information on CORS: See enable-cors.org and html5rocks.com.

From b640ffecda58d65366e61cc00da15c3fcc5c37bf Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 21:26:31 +0100 Subject: [PATCH 46/80] Enhance CORS GET test --- test/cors.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/cors.html b/test/cors.html index 2443ea4b..2586c2c8 100644 --- a/test/cors.html +++ b/test/cors.html @@ -31,11 +31,11 @@ function getTitle(text) { } // Make the actual CORS request. -function makeCorsRequest() { - var url = "http://localhost/cors.reply.html"; - var xhr = createCORSRequest('GET', url); +function makeCorsRequest(method, resource) { + var url = "http://localhost/cors.reply." + resource; + var xhr = createCORSRequest(method, url); if (!xhr) { - alert('CORS not supported'); + alert('ERROR: CORS not supported'); return; } @@ -43,11 +43,11 @@ function makeCorsRequest() { xhr.onload = function() { var text = xhr.responseText; var title = getTitle(text); - alert('Response from CORS request to ' + url + ': ' + title); + alert('Response from CORS request to ' + url + ':\n' + title); }; xhr.onerror = function() { - alert('Woops, there was an error making the request.'); + alert('ERROR: the request failed.'); }; xhr.send(); @@ -66,7 +66,7 @@ function start() {

Cross-origin resource sharing test

*** Error: Javascript is not activated. This test will not work. ***

- +

More information on CORS: See enable-cors.org and html5rocks.com.

From 1e3d8ff8ca512a2d2a59bc4f2a031cf012599813 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 22:26:22 +0100 Subject: [PATCH 47/80] Full CORS support for dynamic resources --- test/cors.html | 4 ++- test/cors.reply.lua | 73 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 test/cors.reply.lua diff --git a/test/cors.html b/test/cors.html index 2586c2c8..2fa32991 100644 --- a/test/cors.html +++ b/test/cors.html @@ -66,7 +66,9 @@ function start() {

Cross-origin resource sharing test

*** Error: Javascript is not activated. This test will not work. ***

- + + +

More information on CORS: See enable-cors.org and html5rocks.com.

diff --git a/test/cors.reply.lua b/test/cors.reply.lua new file mode 100644 index 00000000..9a659a95 --- /dev/null +++ b/test/cors.reply.lua @@ -0,0 +1,73 @@ +-- http://www.html5rocks.com/static/images/cors_server_flowchart.png + +if not mg.request_info.http_headers.Origin then + mg.write("HTTP/1.0 200 OK\r\n") + mg.write("Connection: close\r\n") + mg.write("Content-Type: text/html; charset=utf-8\r\n") + mg.write("\r\n") + mg.write("This test page should not be used directly. Open cors.html instead.") + return +end + +if mg.request_info.request_method == "OPTIONS" then + + local acrm = mg.request_info.http_headers['Access-Control-Request-Method']; + if (acrm) then + local acrh = nil -- mg.request_info.http_headers['Access-Control-Request-Header']; + if (acrm~='PUT') then + -- invalid request + mg.write("HTTP/1.0 403 Forbidden\r\n") + mg.write("Connection: close\r\n") + mg.write("\r\n") + return + else + -- preflight request + mg.write("HTTP/1.0 200 OK\r\n") + mg.write("Access-Control-Allow-Methods: PUT\r\n") + if (acrh) then + mg.write("Access-Control-Allow-Headers: " .. acrh .. "\r\n") + end + mg.write("Access-Control-Allow-Origin: *\r\n") + mg.write("Connection: close\r\n") + mg.write("Content-Type: text/html; charset=utf-8\r\n") + mg.write("\r\n") + return + end + end +end + +-- actual request +if mg.request_info.request_method == "GET" then + mg.write("HTTP/1.0 200 OK\r\n") + mg.write("Access-Control-Allow-Origin: *\r\n") + mg.write("Connection: close\r\n") + mg.write("Content-Type: text/html; charset=utf-8\r\n") + mg.write("\r\n") + mg.write([[ + + CORS dynamic GET test reply - test OK + This should never be shown + + ]]) + return +end + + +if mg.request_info.request_method == "PUT" then + mg.write("HTTP/1.0 200 OK\r\n") + mg.write("Access-Control-Allow-Origin: *\r\n") + mg.write("Connection: close\r\n") + mg.write("Content-Type: text/html; charset=utf-8\r\n") + mg.write("\r\n") + mg.write([[ + + CORS dynamic PUT test reply - test OK + This should never be shown + + ]]) + return +end + +mg.write("HTTP/1.0 403 Forbidden\r\n") +mg.write("Connection: close\r\n") +mg.write("\r\n") From c62ded82e87c7361b195759c117888e67022694e Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 22:48:49 +0100 Subject: [PATCH 48/80] CORS for SSI pages --- src/civetweb.c | 14 +++++++++++++- test/cors.html | 1 + test/cors.reply.shtml | 7 +++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 test/cors.reply.shtml diff --git a/src/civetweb.c b/src/civetweb.c index 37c080bf..a1cae473 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -4407,6 +4407,16 @@ static void handle_ssi_file_request(struct mg_connection *conn, struct file file = STRUCT_FILE_INITIALIZER; char date[64]; time_t curtime = time(NULL); + const char *cors1, *cors2, *cors3; + + if (mg_get_header(conn, "Origin")) { + /* Cross-origin resource sharing (CORS). */ + cors1 = "Access-Control-Allow-Origin: "; + cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; + cors3 = "\r\n"; + } else { + cors1 = cors2 = cors3 = ""; + } if (!mg_fopen(conn, path, "rb", &file)) { send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path, @@ -4416,10 +4426,12 @@ static void handle_ssi_file_request(struct mg_connection *conn, gmt_time_string(date, sizeof(date), &curtime); fclose_on_exec(&file, conn); mg_printf(conn, "HTTP/1.1 200 OK\r\n" + "%s%s%s" "Date: %s\r\n" "Content-Type: text/html\r\n" "Connection: %s\r\n\r\n", - date, suggest_connection_header(conn)); + cors1, cors2, cors3, + date, suggest_connection_header(conn)); send_ssi_file(conn, path, &file, 0); mg_fclose(&file); } diff --git a/test/cors.html b/test/cors.html index 2fa32991..5ab53606 100644 --- a/test/cors.html +++ b/test/cors.html @@ -67,6 +67,7 @@ function start() {

Cross-origin resource sharing test

*** Error: Javascript is not activated. This test will not work. ***

+

More information on CORS: See enable-cors.org and html5rocks.com.

diff --git a/test/cors.reply.shtml b/test/cors.reply.shtml new file mode 100644 index 00000000..26d88dfc --- /dev/null +++ b/test/cors.reply.shtml @@ -0,0 +1,7 @@ + + +CORS test reply - test OK + +Do not load this page directly - use cors.html instead! + + From 4e62bd5b10b7873df7a9db959a3c7de30e0993b7 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 22 Feb 2014 23:27:28 +0100 Subject: [PATCH 49/80] Issue #8: Possible issue and possible fix in websocket handling (by celeron55) --- src/civetweb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index a1cae473..b19d83af 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -4837,7 +4837,7 @@ static void read_websocket(struct mg_connection *conn) error = 0; while (len < data_len) { int n = pull(NULL, conn, data + len, (int)(data_len - len)); - if (n < 0) { + if (n <= 0) { error = 1; break; } From 38bbd731f4265b732903dd58feca14deca157826 Mon Sep 17 00:00:00 2001 From: bel Date: Wed, 26 Feb 2014 21:39:22 +0100 Subject: [PATCH 50/80] Prepare shared Lua Websockets --- src/civetweb.c | 13 +++++++--- src/mod_lua.inl | 67 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index b19d83af..f485fd19 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -651,6 +651,11 @@ struct mg_context { /* linked list of uri handlers */ struct mg_request_handler_info *request_handlers; + +#if defined(USE_LUA) && defined(USE_WEBSOCKET) + /* linked list of shared lua websockets */ + struct mg_shared_lua_websocket *shared_lua_websockets; +#endif }; struct mg_connection { @@ -4967,6 +4972,7 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat path) : 0; if (lua_websock || shared_lua_websock) { + /* TODO */ shared_lua_websock=1; conn->lua_websocket_state = lua_websocket_new(path, conn, !!shared_lua_websock); if (conn->lua_websocket_state) { send_websocket_handshake(conn); @@ -5950,9 +5956,6 @@ void mg_close_connection(struct mg_connection *conn) free(conn); } -struct mg_connection *mg_connect(const char *host, int port, int use_ssl, - char *ebuf, size_t ebuf_len); - struct mg_connection *mg_connect(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len) { @@ -6566,6 +6569,10 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, ctx->user_data = user_data; ctx->request_handlers = 0; +#if defined(USE_LUA) && defined(USE_WEBSOCKET) + ctx->shared_lua_websockets = 0; +#endif + while (options && (name = *options++) != NULL) { if ((i = get_option_index(name)) == -1) { mg_cry(fc(ctx), "Invalid option: %s", name); diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 10044e07..364266e0 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -909,9 +909,16 @@ struct file *filep, struct lua_State *ls) struct lua_websock_data { lua_State *main; lua_State *thread; + char * script; + unsigned shared; struct mg_connection *conn; }; +struct mg_shared_lua_websocket { + struct lua_websock_data *sock; + struct mg_shared_lua_websocket *next; +}; + static void websock_cry(struct mg_connection *conn, int err, lua_State * L, const char * ws_operation, const char * lua_operation) { switch (err) { @@ -942,18 +949,46 @@ static void websock_cry(struct mg_connection *conn, int err, lua_State * L, cons static void * lua_websocket_new(const char * script, struct mg_connection *conn, int is_shared) { struct lua_websock_data *lws_data; + struct mg_shared_lua_websocket **shared_websock_list = &(conn->ctx->shared_lua_websockets); int ok = 0; + int found = 0; int err, nargs; assert(conn->lua_websocket_state == NULL); - lws_data = (struct lua_websock_data *) malloc(sizeof(*lws_data)); + + if (is_shared) { + (void)pthread_mutex_lock(&conn->ctx->mutex); + while (*shared_websock_list) { + if (!strcmp((*shared_websock_list)->sock->script, script)) { + lws_data = (*shared_websock_list)->sock; + lws_data->shared++; + found = 1; + } + shared_websock_list = &((*shared_websock_list)->next); + } + (void)pthread_mutex_unlock(&conn->ctx->mutex); + } + + if (!found) { + lws_data = (struct lua_websock_data *) malloc(sizeof(*lws_data)); + lws_data->shared = is_shared; + } if (lws_data) { lws_data->conn = conn; + lws_data->script = mg_strdup(script); - if (is_shared) { + if (is_shared && !found) { (void)pthread_mutex_lock(&conn->ctx->mutex); - // TODO: add_to_websocket_list(lws_data); + shared_websock_list = &(conn->ctx->shared_lua_websockets); + while (*shared_websock_list) { + shared_websock_list = &((*shared_websock_list)->next); + } + *shared_websock_list = (struct mg_shared_lua_websocket *)malloc(sizeof(struct mg_shared_lua_websocket)); + if (*shared_websock_list) { + (*shared_websock_list)->sock = lws_data; + (*shared_websock_list)->next = 0; + } (void)pthread_mutex_unlock(&conn->ctx->mutex); } @@ -1054,6 +1089,7 @@ static int lua_websocket_ready(struct mg_connection *conn) static void lua_websocket_close(struct mg_connection *conn) { struct lua_websock_data *lws_data = (struct lua_websock_data *)(conn->lua_websocket_state); + struct mg_shared_lua_websocket **shared_websock_list; int err; assert(lws_data != NULL); @@ -1063,8 +1099,29 @@ static void lua_websocket_close(struct mg_connection *conn) lua_pushboolean(lws_data->thread, 0); err = lua_resume(lws_data->thread, NULL, 1); - lua_close(lws_data->main); - free(lws_data); + if (lws_data->shared) { + (void)pthread_mutex_lock(&conn->ctx->mutex); + lws_data->shared--; + if (lws_data->shared==0) { + shared_websock_list = &(conn->ctx->shared_lua_websockets); + while (*shared_websock_list) { + if ((*shared_websock_list)->sock == lws_data) { + *shared_websock_list = (*shared_websock_list)->next; + } else { + shared_websock_list = &((*shared_websock_list)->next); + } + } + + lua_close(lws_data->main); + free(lws_data->script); + lws_data->script=0; + free(lws_data); + } + (void)pthread_mutex_unlock(&conn->ctx->mutex); + } else { + lua_close(lws_data->main); + free(lws_data); + } conn->lua_websocket_state = NULL; } #endif From 6cfa6675007b1f0b2a8903af652d1e77a8433517 Mon Sep 17 00:00:00 2001 From: bel Date: Wed, 26 Feb 2014 23:55:47 +0100 Subject: [PATCH 51/80] All memory allocation should use mg local functions --- src/civetweb.c | 113 ++++++++++++++++++++++++++++-------------------- src/mod_lua.inl | 55 ++++++++++++----------- 2 files changed, 96 insertions(+), 72 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index f485fd19..0b82bd3a 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -319,6 +319,27 @@ typedef int SOCKET; #endif #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +void * mg_malloc(size_t size) { + return malloc(size); +} + +void * mg_calloc(size_t count, size_t size) { + return calloc(count, size); +} + +void * mg_realloc(void * memory, size_t newsize) { + return realloc(memory, newsize); +} + +void mg_free(void * memory) { + free(memory); +} + +#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc +#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc +#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc +#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free + #ifdef _WIN32 static CRITICAL_SECTION global_log_file_lock; static DWORD pthread_self(void) @@ -935,7 +956,7 @@ static char * mg_strndup(const char *ptr, size_t len) { char *p; - if ((p = (char *) malloc(len + 1)) != NULL) { + if ((p = (char *) mg_malloc(len + 1)) != NULL) { mg_strlcpy(p, ptr, len + 1); } @@ -1288,7 +1309,7 @@ static int pthread_cond_init(pthread_cond_t *cv, const void *unused) (void) unused; InitializeCriticalSection(&cv->threadIdSec); cv->waitingthreadcount = 0; - cv->waitingthreadhdls = calloc(MAX_WORKER_THREADS, sizeof(pthread_t)); + cv->waitingthreadhdls = mg_calloc(MAX_WORKER_THREADS, sizeof(pthread_t)); return (cv->waitingthreadhdls!=NULL) ? 0 : -1; } @@ -1367,7 +1388,7 @@ static int pthread_cond_destroy(pthread_cond_t *cv) { EnterCriticalSection(&cv->threadIdSec); assert(cv->waitingthreadcount==0); - free(cv->waitingthreadhdls); + mg_free(cv->waitingthreadhdls); cv->waitingthreadhdls = 0; LeaveCriticalSection(&cv->threadIdSec); DeleteCriticalSection(&cv->threadIdSec); @@ -1539,7 +1560,7 @@ static DIR * opendir(const char *name) if (name == NULL) { SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) { + } else if ((dir = (DIR *) mg_malloc(sizeof(*dir))) == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } else { to_unicode(name, wpath, ARRAY_SIZE(wpath)); @@ -1550,7 +1571,7 @@ static DIR * opendir(const char *name) dir->handle = FindFirstFileW(wpath, &dir->info); dir->result.d_name[0] = '\0'; } else { - free(dir); + mg_free(dir); dir = NULL; } } @@ -1566,7 +1587,7 @@ static int closedir(DIR *dir) if (dir->handle != INVALID_HANDLE_VALUE) result = FindClose(dir->handle) ? 0 : -1; - free(dir); + mg_free(dir); } else { result = -1; SetLastError(ERROR_BAD_ARGUMENTS); @@ -2126,8 +2147,8 @@ static int alloc_vprintf2(char **buf, const char *fmt, va_list ap) *buf = NULL; while (len == -1) { - if (*buf) free(*buf); - *buf = (char *)malloc(size *= 4); + if (*buf) mg_free(*buf); + *buf = (char *)mg_malloc(size *= 4); if (!*buf) break; va_copy(ap_copy, ap); len = vsnprintf(*buf, size, fmt, ap_copy); @@ -2164,7 +2185,7 @@ static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) va_end(ap_copy); } else if (len > (int) size && (size = len + 1) > 0 && - (*buf = (char *) malloc(size)) == NULL) { + (*buf = (char *) mg_malloc(size)) == NULL) { len = -1; /* Allocation failed, mark failure */ } else { va_copy(ap_copy, ap); @@ -2186,7 +2207,7 @@ int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) len = mg_write(conn, buf, (size_t) len); } if (buf != mem && buf != NULL) { - free(buf); + mg_free(buf); } return len; @@ -3335,9 +3356,9 @@ struct dir_scan_data { /* Behaves like realloc(), but frees original pointer on failure */ static void *realloc2(void *ptr, size_t size) { - void *new_ptr = realloc(ptr, size); + void *new_ptr = mg_realloc(ptr, size); if (new_ptr == NULL) { - free(ptr); + mg_free(ptr); } return new_ptr; } @@ -3411,9 +3432,9 @@ static void handle_directory_request(struct mg_connection *conn, sizeof(data.entries[0]), compare_dir_entries); for (i = 0; i < data.num_entries; i++) { print_dir_entry(&data.entries[i]); - free(data.entries[i].file_name); + mg_free(data.entries[i].file_name); } - free(data.entries); + mg_free(data.entries); } conn->num_bytes_sent += mg_printf(conn, "%s", ""); @@ -4053,7 +4074,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) Do not send anything back to client, until we buffer in all HTTP headers. */ data_len = 0; - buf = malloc(buflen); + buf = mg_malloc(buflen); if (buf == NULL) { send_http_error(conn, 500, http_500_error, "Not enough memory for buffer (%u bytes)", @@ -4136,7 +4157,7 @@ done: close(fdout[0]); } if (buf != NULL) { - free(buf); + mg_free(buf); } } #endif /* !NO_CGI */ @@ -4815,11 +4836,11 @@ static void read_websocket(struct mg_connection *conn) /* Allocate space to hold websocket payload */ data = mem; if (data_len > sizeof(mem)) { - data = (char *)malloc(data_len); + data = (char *)mg_malloc(data_len); if (data == NULL) { /* Allocation failed, exit the loop and then close the connection */ - mg_cry(conn, "websocket malloc() failed; closing connection"); + mg_cry(conn, "websocket out of memory; closing connection"); break; } } @@ -4893,7 +4914,7 @@ static void read_websocket(struct mg_connection *conn) } if (data != mem) { - free(data); + mg_free(data); } /* Not breaking the loop, process next websocket frame. */ } else { @@ -4972,7 +4993,7 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat path) : 0; if (lua_websock || shared_lua_websock) { - /* TODO */ shared_lua_websock=1; + /* TODO */ shared_lua_websock = 1; conn->lua_websocket_state = lua_websocket_new(path, conn, !!shared_lua_websock); if (conn->lua_websocket_state) { send_websocket_handshake(conn); @@ -5252,8 +5273,8 @@ void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_ lastref->next = tmp_rh->next; else ctx->request_handlers = tmp_rh->next; - free(tmp_rh->uri); - free(tmp_rh); + mg_free(tmp_rh->uri); + mg_free(tmp_rh); } return; } @@ -5274,7 +5295,7 @@ void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_ return; } - tmp_rh = (struct mg_request_handler_info *)malloc(sizeof(struct mg_request_handler_info)); + tmp_rh = (struct mg_request_handler_info *)mg_malloc(sizeof(struct mg_request_handler_info)); if (tmp_rh == NULL) { mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM"); return; @@ -5470,7 +5491,7 @@ static void close_all_listening_sockets(struct mg_context *ctx) for (i = 0; i < ctx->num_listening_sockets; i++) { closesocket(ctx->listening_sockets[i].sock); } - free(ctx->listening_sockets); + mg_free(ctx->listening_sockets); } static int is_valid_port(unsigned int port) @@ -5568,12 +5589,12 @@ static int set_ports_option(struct mg_context *ctx) closesocket(so.sock); } success = 0; - } else if ((ptr = (struct socket *) realloc(ctx->listening_sockets, + } else if ((ptr = (struct socket *) mg_realloc(ctx->listening_sockets, (ctx->num_listening_sockets + 1) * sizeof(ctx->listening_sockets[0]))) == NULL) { closesocket(so.sock); success = 0; - } else if ((portPtr = (in_port_t*) realloc(ctx->listening_ports, + } else if ((portPtr = (in_port_t*) mg_realloc(ctx->listening_ports, (ctx->num_listening_sockets + 1) * sizeof(ctx->listening_ports[0]))) == NULL) { closesocket(so.sock); @@ -5822,7 +5843,7 @@ static int set_ssl_option(struct mg_context *ctx) /* Initialize locking callbacks, needed for thread safety. http://www.openssl.org/support/faq.html#PROG1 */ size = sizeof(pthread_mutex_t) * CRYPTO_num_locks(); - if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) { + if ((ssl_mutexes = (pthread_mutex_t *) mg_malloc((size_t)size)) == NULL) { mg_cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error()); return 0; } @@ -5953,7 +5974,7 @@ void mg_close_connection(struct mg_connection *conn) #endif close_connection(conn); (void) pthread_mutex_destroy(&conn->mutex); - free(conn); + mg_free(conn); } struct mg_connection *mg_connect(const char *host, int port, int use_ssl, @@ -5966,7 +5987,7 @@ struct mg_connection *mg_connect(const char *host, int port, int use_ssl, if ((sock = conn2(&fake_ctx, host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) { } else if ((conn = (struct mg_connection *) - calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { + mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO)); closesocket(sock); #ifndef NO_SSL @@ -5974,7 +5995,7 @@ struct mg_connection *mg_connect(const char *host, int port, int use_ssl, SSL_CTX_new(SSLv23_client_method())) == NULL) { snprintf(ebuf, ebuf_len, "SSL_CTX_new error"); closesocket(sock); - free(conn); + mg_free(conn); conn = NULL; #endif /* NO_SSL */ } else { @@ -6100,7 +6121,7 @@ static void process_new_connection(struct mg_connection *conn) log_access(conn); } if (ri->remote_user != NULL) { - free((void *) ri->remote_user); + mg_free((void *) ri->remote_user); /* Important! When having connections with and without auth would cause double free and then crash */ ri->remote_user = NULL; @@ -6167,7 +6188,7 @@ static void *worker_thread_run(void *thread_func_param) tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL); #endif - conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE); + conn = (struct mg_connection *) mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE); if (conn == NULL) { mg_cry(fc(ctx), "%s", "Cannot create new connection struct, OOM"); } else { @@ -6219,7 +6240,7 @@ static void *worker_thread_run(void *thread_func_param) #if defined(_WIN32) && !defined(__SYMBIAN32__) CloseHandle(tls.pthread_cond_helper_mutex); #endif - free(conn); + mg_free(conn); DEBUG_TRACE(("exiting")); return NULL; @@ -6351,7 +6372,7 @@ static void master_thread_run(void *thread_func_param) ctx->start_time = (unsigned long)time(NULL); /* Allocate memory for the listening sockets, and start the server */ - pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0])); + pfd = (struct pollfd *) mg_calloc(ctx->num_listening_sockets, sizeof(pfd[0])); while (pfd != NULL && ctx->stop_flag == 0) { for (i = 0; i < ctx->num_listening_sockets; i++) { pfd[i].fd = ctx->listening_sockets[i].sock; @@ -6371,7 +6392,7 @@ static void master_thread_run(void *thread_func_param) } } } - free(pfd); + mg_free(pfd); DEBUG_TRACE(("stopping workers")); /* Stop signal received: somebody called mg_stop. Quit. */ @@ -6445,15 +6466,15 @@ static void free_context(struct mg_context *ctx) #ifdef WIN32 #pragma warning(suppress: 6001) #endif - free(ctx->config[i]); + mg_free(ctx->config[i]); } /* Deallocate request handlers */ while (ctx->request_handlers) { tmp_rh = ctx->request_handlers; ctx->request_handlers = tmp_rh->next; - free(tmp_rh->uri); - free(tmp_rh); + mg_free(tmp_rh->uri); + mg_free(tmp_rh); } #ifndef NO_SSL @@ -6462,14 +6483,14 @@ static void free_context(struct mg_context *ctx) SSL_CTX_free(ctx->ssl_ctx); } if (ssl_mutexes != NULL) { - free(ssl_mutexes); + mg_free(ssl_mutexes); ssl_mutexes = NULL; } #endif /* !NO_SSL */ /* Deallocate worker thread ID array */ if (ctx->workerthreadids != NULL) { - free(ctx->workerthreadids); + mg_free(ctx->workerthreadids); } /* Deallocate the tls variable */ @@ -6479,10 +6500,10 @@ static void free_context(struct mg_context *ctx) } /* deallocate system name string */ - free(ctx->systemName); + mg_free(ctx->systemName); /* Deallocate context itself */ - free(ctx); + mg_free(ctx); } void mg_stop(struct mg_context *ctx) @@ -6552,14 +6573,14 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, /* Allocate context and initialize reasonable general case defaults. TODO(lsm): do proper error handling here. */ - if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) { + if ((ctx = (struct mg_context *) mg_calloc(1, sizeof(*ctx))) == NULL) { return NULL; } if (sTlsInit==0) { if (0 != pthread_key_create(&sTlsKey, NULL)) { mg_cry(fc(ctx), "Cannot initialize thread local storage"); - free(ctx); + mg_free(ctx); return NULL; } sTlsInit++; @@ -6585,7 +6606,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, } if (ctx->config[i] != NULL) { mg_cry(fc(ctx), "warning: %s: duplicate option", name); - free(ctx->config[i]); + mg_free(ctx->config[i]); } ctx->config[i] = mg_strdup(value); DEBUG_TRACE(("[%s] -> [%s]", name, value)); @@ -6637,7 +6658,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, if (workerthreadcount > 0) { ctx->workerthreadcount = workerthreadcount; - ctx->workerthreadids = calloc(workerthreadcount, sizeof(pthread_t)); + ctx->workerthreadids = mg_calloc(workerthreadcount, sizeof(pthread_t)); if (ctx->workerthreadids == NULL) { mg_cry(fc(ctx), "Not enough memory for worker thread ID array"); free_context(ctx); diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 364266e0..1f67577e 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -570,11 +570,11 @@ static int lsp_base64_encode(lua_State *L) if (num_args==1) { text = lua_tolstring(L, 1, &text_len); if (text) { - dst = malloc(text_len*8/6+4); + dst = mg_malloc(text_len*8/6+4); if (dst) { base64_encode(text, text_len, dst); lua_pushstring(L, dst); - free(dst); + mg_free(dst); } else { return luaL_error(L, "out of memory in base64_encode() call"); } @@ -600,15 +600,15 @@ static int lsp_base64_decode(lua_State *L) if (num_args==1) { text = lua_tolstring(L, 1, &text_len); if (text) { - dst = malloc(text_len); + dst = mg_malloc(text_len); if (dst) { ret = base64_decode(text, text_len, dst, &dst_len); if (ret != -1) { - free(dst); + mg_free(dst); return luaL_error(L, "illegal character in lsp_base64_decode() call"); } else { lua_pushlstring(L, dst, dst_len); - free(dst); + mg_free(dst); } } else { return luaL_error(L, "out of memory in lsp_base64_decode() call"); @@ -970,29 +970,30 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn, } if (!found) { - lws_data = (struct lua_websock_data *) malloc(sizeof(*lws_data)); - lws_data->shared = is_shared; + lws_data = (struct lua_websock_data *) mg_malloc(sizeof(*lws_data)); } if (lws_data) { - lws_data->conn = conn; - lws_data->script = mg_strdup(script); - - if (is_shared && !found) { - (void)pthread_mutex_lock(&conn->ctx->mutex); - shared_websock_list = &(conn->ctx->shared_lua_websockets); - while (*shared_websock_list) { - shared_websock_list = &((*shared_websock_list)->next); + if (!found) { + lws_data->shared = is_shared; + lws_data->conn = conn; + lws_data->script = mg_strdup(script); + lws_data->main = luaL_newstate(); + if (is_shared) { + (void)pthread_mutex_lock(&conn->ctx->mutex); + shared_websock_list = &(conn->ctx->shared_lua_websockets); + while (*shared_websock_list) { + shared_websock_list = &((*shared_websock_list)->next); + } + *shared_websock_list = (struct mg_shared_lua_websocket *)mg_malloc(sizeof(struct mg_shared_lua_websocket)); + if (*shared_websock_list) { + (*shared_websock_list)->sock = lws_data; + (*shared_websock_list)->next = 0; + } + (void)pthread_mutex_unlock(&conn->ctx->mutex); } - *shared_websock_list = (struct mg_shared_lua_websocket *)malloc(sizeof(struct mg_shared_lua_websocket)); - if (*shared_websock_list) { - (*shared_websock_list)->sock = lws_data; - (*shared_websock_list)->next = 0; - } - (void)pthread_mutex_unlock(&conn->ctx->mutex); } - lws_data->main = luaL_newstate(); if (lws_data->main) { prepare_lua_environment(conn, lws_data->main, script, LUA_ENV_TYPE_LUA_WEBSOCKET); if (conn->ctx->callbacks.init_lua != NULL) { @@ -1019,7 +1020,7 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn, if (!ok) { if (lws_data->main) lua_close(lws_data->main); - free(lws_data); + mg_free(lws_data); lws_data=0; } } else { @@ -1103,6 +1104,7 @@ static void lua_websocket_close(struct mg_connection *conn) (void)pthread_mutex_lock(&conn->ctx->mutex); lws_data->shared--; if (lws_data->shared==0) { + /* shared_websock_list = &(conn->ctx->shared_lua_websockets); while (*shared_websock_list) { if ((*shared_websock_list)->sock == lws_data) { @@ -1113,14 +1115,15 @@ static void lua_websocket_close(struct mg_connection *conn) } lua_close(lws_data->main); - free(lws_data->script); + mg_free(lws_data->script); lws_data->script=0; - free(lws_data); + mg_free(lws_data); + */ } (void)pthread_mutex_unlock(&conn->ctx->mutex); } else { lua_close(lws_data->main); - free(lws_data); + mg_free(lws_data); } conn->lua_websocket_state = NULL; } From 51852668b4bf79a6b7b1a597b4648545d0999322 Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 27 Feb 2014 00:24:38 +0100 Subject: [PATCH 52/80] Alloc/Free debugging for Windows (temporary instrumentation) --- src/civetweb.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 0b82bd3a..6cca9d24 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -320,21 +320,49 @@ typedef int SOCKET; #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) void * mg_malloc(size_t size) { + void * data = malloc(size); + + char mallocStr[256]; + sprintf(mallocStr, "malloc(%u) -> %p\n", size, data); + OutputDebugStringA(mallocStr); + return malloc(size); } void * mg_calloc(size_t count, size_t size) { - return calloc(count, size); -} -void * mg_realloc(void * memory, size_t newsize) { - return realloc(memory, newsize); + void * data = mg_malloc(size); + if (data) memset(data, 0, size); + + return data; } void mg_free(void * memory) { + + char mallocStr[256]; + sprintf(mallocStr, "free(%p)\n", memory); + OutputDebugStringA(mallocStr); + free(memory); } +void * mg_realloc(void * memory, size_t newsize) { + + void * data; + if (newsize) { + data = mg_malloc(newsize); + if ((data!=NULL) && (memory!=NULL)) { + memcpy(data, memory, newsize); + mg_free(memory); + } + } else { + data = 0; + mg_free(memory); + } + + return data; +} + #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc From 950d47e198d7fd1b4ac3cc25d136670e672d43ab Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 27 Feb 2014 21:12:57 +0100 Subject: [PATCH 53/80] Memory debugging for Windows --- src/civetweb.c | 50 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 6cca9d24..f91876e3 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -319,45 +319,60 @@ typedef int SOCKET; #endif #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -void * mg_malloc(size_t size) { - void * data = malloc(size); - +static unsigned long totalMemUsed = 0; + +static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { + + void * data = malloc(size + sizeof(size_t)); + void * memory = 0; char mallocStr[256]; - sprintf(mallocStr, "malloc(%u) -> %p\n", size, data); + + if (data) { + *(size_t*)data = size; + totalMemUsed += size; + memory = (void *)(((char*)data)+sizeof(size_t)); + } + sprintf(mallocStr, "malloc(%u) -> %p (%u) --- %s:%u\n", size, memory, totalMemUsed, file, line); OutputDebugStringA(mallocStr); - return malloc(size); + return memory; } -void * mg_calloc(size_t count, size_t size) { +static void * mg_calloc_ex(size_t count, size_t size, const char * file, unsigned line) { - void * data = mg_malloc(size); + void * data = mg_malloc_ex(size*count, file, line); if (data) memset(data, 0, size); return data; } -void mg_free(void * memory) { +static void mg_free_ex(void * memory, const char * file, unsigned line) { char mallocStr[256]; - sprintf(mallocStr, "free(%p)\n", memory); - OutputDebugStringA(mallocStr); + void * data = (void *)(((char*)memory)-sizeof(size_t)); + size_t size = *(size_t*)data; - free(memory); + if (memory) { + totalMemUsed -= size; + sprintf(mallocStr, "free(%p) (%u, %u) --- %s:%u\n", memory, size, totalMemUsed, file, line); + OutputDebugStringA(mallocStr); + + free(data); + } } -void * mg_realloc(void * memory, size_t newsize) { +static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, unsigned line) { void * data; if (newsize) { - data = mg_malloc(newsize); + data = mg_malloc_ex(newsize, file, line); if ((data!=NULL) && (memory!=NULL)) { memcpy(data, memory, newsize); - mg_free(memory); + mg_free_ex(memory, file, line); } } else { data = 0; - mg_free(memory); + mg_free_ex(memory, file, line); } return data; @@ -368,6 +383,11 @@ void * mg_realloc(void * memory, size_t newsize) { #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free +#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) +#define mg_calloc(a,b) mg_calloc_ex(a, b, __FILE__, __LINE__) +#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__) +#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) + #ifdef _WIN32 static CRITICAL_SECTION global_log_file_lock; static DWORD pthread_self(void) From b1f119f253d20b541cd2330dc3ba96131ac4d236 Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 27 Feb 2014 21:49:03 +0100 Subject: [PATCH 54/80] Memory debugging for Windows --- src/civetweb.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index f91876e3..af77d7ad 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -319,6 +319,7 @@ typedef int SOCKET; #endif #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#if defined(MEMORY_DEBUGGING) || 0 static unsigned long totalMemUsed = 0; static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { @@ -332,7 +333,7 @@ static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { totalMemUsed += size; memory = (void *)(((char*)data)+sizeof(size_t)); } - sprintf(mallocStr, "malloc(%u) -> %p (%u) --- %s:%u\n", size, memory, totalMemUsed, file, line); + sprintf(mallocStr, "MEM: %p %5u alloc %7u --- %s:%u\n", memory, size, totalMemUsed, file, line); OutputDebugStringA(mallocStr); return memory; @@ -354,7 +355,7 @@ static void mg_free_ex(void * memory, const char * file, unsigned line) { if (memory) { totalMemUsed -= size; - sprintf(mallocStr, "free(%p) (%u, %u) --- %s:%u\n", memory, size, totalMemUsed, file, line); + sprintf(mallocStr, "MEM: %p %5u free %7u --- %s:%u\n", memory, size, totalMemUsed, file, line); OutputDebugStringA(mallocStr); free(data); @@ -378,16 +379,23 @@ static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, un return data; } -#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc -#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc -#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc -#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free - #define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) #define mg_calloc(a,b) mg_calloc_ex(a, b, __FILE__, __LINE__) #define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__) #define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) +#else +__inline void * mg_malloc(size_t a) {return malloc(a);} +__inline void * mg_calloc(size_t a, size_t b) {return calloc(a, b);} +__inline void * mg_realloc(void * a, size_t b) {return realloc(a, b);} +__inline void mg_free(void * a) {free(a);} +#endif + +#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc +#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc +#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc +#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free + #ifdef _WIN32 static CRITICAL_SECTION global_log_file_lock; static DWORD pthread_self(void) From c428ad34e3a93d1d88ba3a6b9dc46013d0092d5c Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 27 Feb 2014 21:52:15 +0100 Subject: [PATCH 55/80] Fix minor memory leak (listening_ports) --- src/civetweb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index af77d7ad..3f11db78 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -319,7 +319,7 @@ typedef int SOCKET; #endif #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -#if defined(MEMORY_DEBUGGING) || 0 +#if defined(MEMORY_DEBUGGING) static unsigned long totalMemUsed = 0; static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { @@ -5548,6 +5548,9 @@ static void close_all_listening_sockets(struct mg_context *ctx) closesocket(ctx->listening_sockets[i].sock); } mg_free(ctx->listening_sockets); + ctx->listening_sockets=0; + mg_free(ctx->listening_ports); + ctx->listening_ports=0; } static int is_valid_port(unsigned int port) From d77938189e6c42db0205b353fd9c59caa3777483 Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 27 Feb 2014 22:33:00 +0100 Subject: [PATCH 56/80] Count alloc/free in Windows memory debugging --- src/civetweb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 3f11db78..7d322c3e 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -320,6 +320,7 @@ typedef int SOCKET; #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) #if defined(MEMORY_DEBUGGING) +static unsigned long blockCount = 0; static unsigned long totalMemUsed = 0; static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { @@ -331,9 +332,10 @@ static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { if (data) { *(size_t*)data = size; totalMemUsed += size; + blockCount++; memory = (void *)(((char*)data)+sizeof(size_t)); } - sprintf(mallocStr, "MEM: %p %5u alloc %7u --- %s:%u\n", memory, size, totalMemUsed, file, line); + sprintf(mallocStr, "MEM: %p %5u alloc %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); OutputDebugStringA(mallocStr); return memory; @@ -351,11 +353,13 @@ static void mg_free_ex(void * memory, const char * file, unsigned line) { char mallocStr[256]; void * data = (void *)(((char*)memory)-sizeof(size_t)); - size_t size = *(size_t*)data; + size_t size; if (memory) { + size = *(size_t*)data; totalMemUsed -= size; - sprintf(mallocStr, "MEM: %p %5u free %7u --- %s:%u\n", memory, size, totalMemUsed, file, line); + blockCount--; + sprintf(mallocStr, "MEM: %p %5u free %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); OutputDebugStringA(mallocStr); free(data); From bd896a0fa0815225e5f1f5fe950a5f1e31410a9e Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 27 Feb 2014 22:35:57 +0100 Subject: [PATCH 57/80] Create a Lua allocator, to use mg_malloc/mg_free --- src/mod_lua.inl | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 1f67577e..79dbedef 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -817,6 +817,17 @@ static int lua_error_handler(lua_State *L) return 0; } +static void * lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize) { + + (void)ud; (void)osize; /* not used */ + + if (nsize == 0) { + mg_free(ptr); + return NULL; + } + return mg_realloc(ptr, nsize); +} + void mg_exec_lua_script(struct mg_connection *conn, const char *path, const void **exports) { @@ -827,7 +838,7 @@ void mg_exec_lua_script(struct mg_connection *conn, const char *path, conn->must_close=1; /* Execute a plain Lua script. */ - if (path != NULL && (L = luaL_newstate()) != NULL) { + if (path != NULL && (L = lua_newstate(lua_allocator, NULL)) != NULL) { prepare_lua_environment(conn, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE); lua_pushcclosure(L, &lua_error_handler, 0); @@ -885,7 +896,7 @@ struct file *filep, struct lua_State *ls) fileno(filep->fp), 0)) == MAP_FAILED) { lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size, fileno(filep->fp), strerror(errno)); - } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) { + } else if ((L = ls != NULL ? ls : lua_newstate(lua_allocator, NULL)) == NULL) { send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed"); } else { /* We're not sending HTTP headers here, Lua page must do it. */ @@ -978,7 +989,7 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn, lws_data->shared = is_shared; lws_data->conn = conn; lws_data->script = mg_strdup(script); - lws_data->main = luaL_newstate(); + lws_data->main = lua_newstate(lua_allocator, NULL); if (is_shared) { (void)pthread_mutex_lock(&conn->ctx->mutex); shared_websock_list = &(conn->ctx->shared_lua_websockets); From 25395465555a573992d7ddac3ba9bfab7f2aec7a Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 27 Feb 2014 23:05:15 +0100 Subject: [PATCH 58/80] Instrumentation of realloc --- src/civetweb.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 7d322c3e..51fb5ef9 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -335,7 +335,7 @@ static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { blockCount++; memory = (void *)(((char*)data)+sizeof(size_t)); } - sprintf(mallocStr, "MEM: %p %5u alloc %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); + sprintf(mallocStr, "MEM: %p %5u alloc %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); OutputDebugStringA(mallocStr); return memory; @@ -359,7 +359,7 @@ static void mg_free_ex(void * memory, const char * file, unsigned line) { size = *(size_t*)data; totalMemUsed -= size; blockCount--; - sprintf(mallocStr, "MEM: %p %5u free %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); + sprintf(mallocStr, "MEM: %p %5u free %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); OutputDebugStringA(mallocStr); free(data); @@ -368,12 +368,29 @@ static void mg_free_ex(void * memory, const char * file, unsigned line) { static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, unsigned line) { + char mallocStr[256]; void * data; + size_t oldsize; + if (newsize) { - data = mg_malloc_ex(newsize, file, line); - if ((data!=NULL) && (memory!=NULL)) { - memcpy(data, memory, newsize); - mg_free_ex(memory, file, line); + if (memory) { + data = (void *)(((char*)memory)-sizeof(size_t)); + oldsize = *(size_t*)data; + data = realloc(data, newsize+sizeof(size_t)); + if (data) { + totalMemUsed -= oldsize; + sprintf(mallocStr, "MEM: %p %5u r-free %7u %4u --- %s:%u\n", memory, oldsize, totalMemUsed, blockCount, file, line); + OutputDebugStringA(mallocStr); + totalMemUsed += newsize; + sprintf(mallocStr, "MEM: %p %5u r-alloc %7u %4u --- %s:%u\n", memory, newsize, totalMemUsed, blockCount, file, line); + OutputDebugStringA(mallocStr); + *(size_t*)data = newsize; + data = (void *)(((char*)data)+sizeof(size_t)); + } else { + OutputDebugStringA("MEM: realloc failed\n"); + } + } else { + data = mg_malloc_ex(newsize, file, line); } } else { data = 0; From b24b72c685e401e0294a45b75634f197863d1754 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 28 Feb 2014 00:06:30 +0100 Subject: [PATCH 59/80] Fix minor memory leak (script name) --- src/mod_lua.inl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 79dbedef..62551b34 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -1031,6 +1031,7 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn, if (!ok) { if (lws_data->main) lua_close(lws_data->main); + mg_free(lws_data->script); mg_free(lws_data); lws_data=0; } @@ -1134,6 +1135,7 @@ static void lua_websocket_close(struct mg_connection *conn) (void)pthread_mutex_unlock(&conn->ctx->mutex); } else { lua_close(lws_data->main); + mg_free(lws_data->script); mg_free(lws_data); } conn->lua_websocket_state = NULL; From f9c0cc2d17d54a5e4f7e6704b2c5a5b5ef3bbc5c Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 2 Mar 2014 21:26:18 +0100 Subject: [PATCH 60/80] Update release information for V1.6 --- RELEASE_NOTES.md | 4 +++- src/civetweb.c | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c2d34f3d..336aa69c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,10 +1,12 @@ Release Notes v1.6 (Under Development) === -### Objectives: *???* +### Objectives: *Enhance Lua support, bug fixes and updates" Changes ------- +- Fix minor memory leaks (bel) +- Redirect all memory allocation/deallocation through mg functions which may be overwritten (bel) - Support Cross-Origin Resource Sharing (CORS) for static files and scripts (bel) - Win32: Replace dll.def file by export macros in civetweb.h (CSTAJ) - Base64 encode and decode functions for Lua (bel) diff --git a/src/civetweb.c b/src/civetweb.c index 51fb5ef9..3f6aba23 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2004-2013 Sergey Lyubka +/* Copyright (c) 2013-2014 the civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -379,7 +380,7 @@ static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, un data = realloc(data, newsize+sizeof(size_t)); if (data) { totalMemUsed -= oldsize; - sprintf(mallocStr, "MEM: %p %5u r-free %7u %4u --- %s:%u\n", memory, oldsize, totalMemUsed, blockCount, file, line); + sprintf(mallocStr, "MEM: %p %5u r-free %7u %4u --- %s:%u\n", memory, oldsize, totalMemUsed, blockCount, file, line); OutputDebugStringA(mallocStr); totalMemUsed += newsize; sprintf(mallocStr, "MEM: %p %5u r-alloc %7u %4u --- %s:%u\n", memory, newsize, totalMemUsed, blockCount, file, line); @@ -5070,7 +5071,7 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat path) : 0; if (lua_websock || shared_lua_websock) { - /* TODO */ shared_lua_websock = 1; + /* TODO */ shared_lua_websock = 0; conn->lua_websocket_state = lua_websocket_new(path, conn, !!shared_lua_websock); if (conn->lua_websocket_state) { send_websocket_handshake(conn); From 57c12b6ff3af0d6ad93b6139dcc44443445e4283 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 2 Mar 2014 23:26:18 +0100 Subject: [PATCH 61/80] Memory debugging for non-Windows systems --- src/civetweb.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/civetweb.c b/src/civetweb.c index 3f6aba23..289ef4a6 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -336,8 +336,13 @@ static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { blockCount++; memory = (void *)(((char*)data)+sizeof(size_t)); } + sprintf(mallocStr, "MEM: %p %5u alloc %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); +#if defined(_WIN32) OutputDebugStringA(mallocStr); +#else + puts(mallocStr); +#endif return memory; } @@ -361,7 +366,11 @@ static void mg_free_ex(void * memory, const char * file, unsigned line) { totalMemUsed -= size; blockCount--; sprintf(mallocStr, "MEM: %p %5u free %7u %4u --- %s:%u\n", memory, size, totalMemUsed, blockCount, file, line); +#if defined(_WIN32) OutputDebugStringA(mallocStr); +#else + puts(mallocStr); +#endif free(data); } @@ -381,14 +390,26 @@ static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, un if (data) { totalMemUsed -= oldsize; sprintf(mallocStr, "MEM: %p %5u r-free %7u %4u --- %s:%u\n", memory, oldsize, totalMemUsed, blockCount, file, line); +#if defined(_WIN32) OutputDebugStringA(mallocStr); +#else + puts(mallocStr); +#endif totalMemUsed += newsize; sprintf(mallocStr, "MEM: %p %5u r-alloc %7u %4u --- %s:%u\n", memory, newsize, totalMemUsed, blockCount, file, line); +#if defined(_WIN32) OutputDebugStringA(mallocStr); +#else + puts(mallocStr); +#endif *(size_t*)data = newsize; data = (void *)(((char*)data)+sizeof(size_t)); } else { +#if defined(_WIN32) OutputDebugStringA("MEM: realloc failed\n"); +#else + puts("MEM: realloc failed\n"); +#endif } } else { data = mg_malloc_ex(newsize, file, line); From 7b6635b657c193577e25320a13c9afcc85b577e8 Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 3 Mar 2014 20:32:50 +0100 Subject: [PATCH 62/80] Move DEBUG_TRACE to the top of the file --- src/civetweb.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 289ef4a6..ea239790 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -320,6 +320,26 @@ typedef int SOCKET; #endif #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#ifdef DEBUG_TRACE +#undef DEBUG_TRACE +#define DEBUG_TRACE(x) +#else +#if defined(DEBUG) +#define DEBUG_TRACE(x) do { \ + flockfile(stdout); \ + printf("*** %lu.%p.%s.%d: ", \ + (unsigned long) time(NULL), (void *) pthread_self(), \ + __func__, __LINE__); \ + printf x; \ + putchar('\n'); \ + fflush(stdout); \ + funlockfile(stdout); \ +} while (0) +#else +#define DEBUG_TRACE(x) +#endif /* DEBUG */ +#endif /* DEBUG_TRACE */ + #if defined(MEMORY_DEBUGGING) static unsigned long blockCount = 0; static unsigned long totalMemUsed = 0; @@ -475,26 +495,6 @@ void *pthread_getspecific(pthread_key_t key) #define MD5_STATIC static #include "md5.inl" -#ifdef DEBUG_TRACE -#undef DEBUG_TRACE -#define DEBUG_TRACE(x) -#else -#if defined(DEBUG) -#define DEBUG_TRACE(x) do { \ - flockfile(stdout); \ - printf("*** %lu.%p.%s.%d: ", \ - (unsigned long) time(NULL), (void *) pthread_self(), \ - __func__, __LINE__); \ - printf x; \ - putchar('\n'); \ - fflush(stdout); \ - funlockfile(stdout); \ -} while (0) -#else -#define DEBUG_TRACE(x) -#endif /* DEBUG */ -#endif /* DEBUG_TRACE */ - /* Darwin prior to 7.0 and Win32 do not have socklen_t */ #ifdef NO_SOCKLEN_T typedef int socklen_t; From c76d649307e2b0aa2b699a797f257a76cabc8e0e Mon Sep 17 00:00:00 2001 From: bel Date: Mon, 3 Mar 2014 20:41:27 +0100 Subject: [PATCH 63/80] Use DEBUG_TRACE in memory debugging --- src/civetweb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index ea239790..c561f8c2 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -361,7 +361,7 @@ static void * mg_malloc_ex(size_t size, const char * file, unsigned line) { #if defined(_WIN32) OutputDebugStringA(mallocStr); #else - puts(mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif return memory; @@ -389,7 +389,7 @@ static void mg_free_ex(void * memory, const char * file, unsigned line) { #if defined(_WIN32) OutputDebugStringA(mallocStr); #else - puts(mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif free(data); @@ -413,14 +413,14 @@ static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, un #if defined(_WIN32) OutputDebugStringA(mallocStr); #else - puts(mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif totalMemUsed += newsize; sprintf(mallocStr, "MEM: %p %5u r-alloc %7u %4u --- %s:%u\n", memory, newsize, totalMemUsed, blockCount, file, line); #if defined(_WIN32) OutputDebugStringA(mallocStr); #else - puts(mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif *(size_t*)data = newsize; data = (void *)(((char*)data)+sizeof(size_t)); @@ -428,7 +428,7 @@ static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, un #if defined(_WIN32) OutputDebugStringA("MEM: realloc failed\n"); #else - puts("MEM: realloc failed\n"); + DEBUG_TRACE("MEM: realloc failed\n"); #endif } } else { From 492d3f567da273db8bd6ae488e401ff0cda65cca Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 7 Mar 2014 17:51:01 +0100 Subject: [PATCH 64/80] Windows: Use same caps style for all context menu items --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 7eacf3a3..cecd183a 100644 --- a/src/main.c +++ b/src/main.c @@ -986,7 +986,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, ID_REMOVE_SERVICE, "Deinstall service"); AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, ""); AppendMenu(hMenu, MF_STRING, ID_CONNECT, "Start browser"); - AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit Settings"); + AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit settings"); AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, ""); AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit"); GetCursorPos(&pt); From 78b4a458869d77e412419d196cc363c83a6105b4 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 7 Mar 2014 18:28:28 +0100 Subject: [PATCH 65/80] Add version to the settings menu title, change management of the server_name variable, and add some assert to check buffer sizes --- src/main.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main.c b/src/main.c index cecd183a..90f2638e 100644 --- a/src/main.c +++ b/src/main.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "civetweb.h" @@ -76,10 +77,10 @@ extern char *_getcwd(char *buf, size_t size); #define MAX_CONF_FILE_LINE_SIZE (8 * 1024) static int exit_flag; -static char server_name[40]; /* Set by init_server_name() */ -static char config_file[PATH_MAX] = ""; /* Set by - process_command_line_arguments() */ -static struct mg_context *ctx; /* Set by start_civetweb() */ +static char server_base_name[40]; /* Set by init_server_name() */ +static char *server_name; /* Set by init_server_name() */ +static char config_file[PATH_MAX] = ""; /* Set by process_command_line_arguments() */ +static struct mg_context *ctx; /* Set by start_civetweb() */ #if !defined(CONFIG_FILE) #define CONFIG_FILE "civetweb.conf" @@ -360,8 +361,10 @@ static void process_command_line_arguments(char *argv[], char **options) static void init_server_name(void) { - snprintf(server_name, sizeof(server_name), "Civetweb v%s", + assert((strlen(mg_version())+12)cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30; DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) NULL); @@ -879,7 +893,7 @@ static void show_settings_dialog() static int manage_service(int action) { - static const char *service_name = "Civetweb"; + static const char *service_name = "Civetweb"; /* TODO: check using server_name instead of service_name */ SC_HANDLE hSCM = NULL, hService = NULL; SERVICE_DESCRIPTION descr = {server_name}; char path[PATH_MAX + 20] = "";/* Path to executable plus magic argument */ @@ -924,16 +938,16 @@ static int manage_service(int action) static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static SERVICE_TABLE_ENTRY service_table[] = { - {server_name, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, - {NULL, NULL} - }; + static SERVICE_TABLE_ENTRY service_table[2] = {0}; int service_installed; char buf[200], *service_argv[] = {__argv[0], NULL}; POINT pt; HMENU hMenu; static UINT s_uTaskbarRestart; /* for taskbar creation */ + service_table[0].lpServiceName = server_name; + service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; + switch (msg) { case WM_CREATE: if (__argv[1] != NULL && From e9e9ff3fa90fe06b45641d653a7e252097e18896 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 7 Mar 2014 19:22:25 +0100 Subject: [PATCH 66/80] Allow to specify a title for the service, so an admin may distinguish two instances of civetweb on the same system --- src/main.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index 90f2638e..c3baaef3 100644 --- a/src/main.c +++ b/src/main.c @@ -203,6 +203,11 @@ static int set_option(char **options, const char *name, const char *value) int i, type; const struct mg_option *default_options = mg_get_valid_options(); + if (0==strcmp(name, "title")) { + /* This option is evaluated by main.c, not civetweb.c - just skip it and return OK */ + return 1; + } + type = CONFIG_TYPE_UNKNOWN; for (i = 0; default_options[i].name != 0; i++) { if (!strcmp(default_options[i].name, name)) { @@ -359,12 +364,19 @@ static void process_command_line_arguments(char *argv[], char **options) } } -static void init_server_name(void) +static void init_server_name(int argc, const char *argv[]) { + int i; assert((strlen(mg_version())+12) Date: Fri, 7 Mar 2014 20:35:18 +0100 Subject: [PATCH 67/80] Handle options of main.c in a similar way as options of civetweb.c --- src/main.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index c3baaef3..0adfae20 100644 --- a/src/main.c +++ b/src/main.c @@ -91,6 +91,18 @@ static struct mg_context *ctx; /* Set by start_civetweb() */ #define CONFIG_FILE2 "/usr/local/etc/civetweb.conf" #endif +enum { + OPTION_TITLE, + OPTION_ICON, + NUM_MAIN_OPTIONS +}; + +static struct mg_option main_config_options[] = { + {"title", CONFIG_TYPE_STRING, NULL}, + {"icon", CONFIG_TYPE_STRING, NULL}, + {NULL, CONFIG_TYPE_UNKNOWN, NULL} +}; + static void WINCDECL signal_handler(int sig_num) { exit_flag = sig_num; @@ -203,9 +215,11 @@ static int set_option(char **options, const char *name, const char *value) int i, type; const struct mg_option *default_options = mg_get_valid_options(); - if (0==strcmp(name, "title")) { - /* This option is evaluated by main.c, not civetweb.c - just skip it and return OK */ - return 1; + for (i = 0; main_config_options[i].name != 0; i++) { + if (0==strcmp(name, main_config_options[OPTION_TITLE].name)) { + /* This option is evaluated by main.c, not civetweb.c - just skip it and return OK */ + return 1; + } } type = CONFIG_TYPE_UNKNOWN; @@ -367,13 +381,14 @@ static void process_command_line_arguments(char *argv[], char **options) static void init_server_name(int argc, const char *argv[]) { int i; + assert(sizeof(main_config_options)/sizeof(main_config_options[0]) == NUM_MAIN_OPTIONS+1); assert((strlen(mg_version())+12) Date: Fri, 7 Mar 2014 21:21:08 +0100 Subject: [PATCH 68/80] Parameter checking for the standalone server --- src/civetweb.c | 5 +++-- src/main.c | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index c561f8c2..997fe752 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014 the civetweb developers +/* Copyright (c) 2013-2014 the Civetweb developers * Copyright (c) 2004-2013 Sergey Lyubka * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -683,6 +683,7 @@ enum { NUM_OPTIONS }; +/* TODO: replace 12345 by proper config types */ static struct mg_option config_options[] = { {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, {"cgi_environment", CONFIG_TYPE_STRING, NULL}, @@ -709,7 +710,7 @@ static struct mg_option config_options[] = { {"document_root", CONFIG_TYPE_DIRECTORY, NULL}, {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, {"num_threads", CONFIG_TYPE_NUMBER, "50"}, - {"run_as_user", 12345, NULL}, + {"run_as_user", CONFIG_TYPE_STRING, NULL}, {"url_rewrite_patterns", 12345, NULL}, {"hide_files_patterns", 12345, NULL}, {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, diff --git a/src/main.c b/src/main.c index 0adfae20..429dc6c7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2004-2013 Sergey Lyubka +/* Copyright (c) 2013-2014 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -142,9 +143,14 @@ static void show_usage_and_exit(void) options = mg_get_valid_options(); for (i = 0; options[i].name != NULL; i++) { - fprintf(stderr, " -%s %s\n", - options[i].name[i], options[i].default_value == NULL ? "" : options[i].default_value); + fprintf(stderr, " -%s %s\n", options[i].name, ((options[i].default_value == NULL) ? "" : options[i].default_value)); } + + options = main_config_options; + for (i = 0; options[i].name != NULL; i++) { + fprintf(stderr, " -%s %s\n", options[i].name, ((options[i].default_value == NULL) ? "" : options[i].default_value)); + } + exit(EXIT_FAILURE); } @@ -373,7 +379,10 @@ static void process_command_line_arguments(char *argv[], char **options) if (argv[i][0] != '-' || argv[i + 1] == NULL) { show_usage_and_exit(); } - set_option(options, &argv[i][1], argv[i + 1]); + if (!set_option(options, &argv[i][1], argv[i + 1])) { + printf("command line option is invalid, ignoring it:\n %s %s\n", + argv[i], argv[i + 1]); + } } } } From 32cbb4871021ce6ddb5819e808b3994ff567365d Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 8 Mar 2014 19:56:54 +0100 Subject: [PATCH 69/80] Allow to specify title and tray icon for the Windows standalone server --- RELEASE_NOTES.md | 1 + src/main.c | 19 +++++++++++++++---- test/test.ico | Bin 0 -> 1406 bytes 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 test/test.ico diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 336aa69c..51a03720 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,7 @@ Release Notes v1.6 (Under Development) Changes ------- +- Allow to specify title and tray icon for the Windows standalone server (bel) - Fix minor memory leaks (bel) - Redirect all memory allocation/deallocation through mg functions which may be overwritten (bel) - Support Cross-Origin Resource Sharing (CORS) for static files and scripts (bel) diff --git a/src/main.c b/src/main.c index 429dc6c7..00f2dd83 100644 --- a/src/main.c +++ b/src/main.c @@ -80,6 +80,7 @@ extern char *_getcwd(char *buf, size_t size); static int exit_flag; static char server_base_name[40]; /* Set by init_server_name() */ static char *server_name; /* Set by init_server_name() */ +static char *icon_name; /* Set by init_server_name() */ static char config_file[PATH_MAX] = ""; /* Set by process_command_line_arguments() */ static struct mg_context *ctx; /* Set by start_civetweb() */ @@ -222,7 +223,7 @@ static int set_option(char **options, const char *name, const char *value) const struct mg_option *default_options = mg_get_valid_options(); for (i = 0; main_config_options[i].name != 0; i++) { - if (0==strcmp(name, main_config_options[OPTION_TITLE].name)) { + if (0==strcmp(name, main_config_options[i].name)) { /* This option is evaluated by main.c, not civetweb.c - just skip it and return OK */ return 1; } @@ -401,6 +402,12 @@ static void init_server_name(int argc, const char *argv[]) server_name = (char*)(argv[i+1]); } } + icon_name = 0; + for (i=0; ix$x1r}(so}0`K`#;q8g99-;FvwMfiWUC1`DgXcg literal 0 HcmV?d00001 From c75487463e05f8e671e82c713eafa64dc11da243 Mon Sep 17 00:00:00 2001 From: bel Date: Sat, 8 Mar 2014 21:46:43 +0100 Subject: [PATCH 70/80] remove TODO marker (won't do): CGI scripts may support all HTTP methods --- src/civetweb.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 997fe752..7b3c3859 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -5563,15 +5563,7 @@ static void handle_request(struct mg_connection *conn) } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), path) > 0) { - /* TODO: check unsupported methods -> 501 - if (strcmp(ri->request_method, "POST") && - strcmp(ri->request_method, "HEAD") && - strcmp(ri->request_method, "GET")) { - send_http_error(conn, 501, "Not Implemented", - "Method %s is not implemented", ri->request_method); - } else { - handle_cgi_request(conn, path); - } */ + /* CGI scripts may support all HTTP methods */ handle_cgi_request(conn, path); #endif /* !NO_CGI */ } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], From b96d5337fa9633621bf5298cd030d1522f62280f Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 9 Mar 2014 20:23:04 +0100 Subject: [PATCH 71/80] Redesign websocket for Lua --- src/civetweb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/civetweb.c b/src/civetweb.c index 7b3c3859..ea6c7d3b 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -5093,7 +5093,7 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat path) : 0; if (lua_websock || shared_lua_websock) { - /* TODO */ shared_lua_websock = 0; + /* TODO */ shared_lua_websock = 1; conn->lua_websocket_state = lua_websocket_new(path, conn, !!shared_lua_websock); if (conn->lua_websocket_state) { send_websocket_handshake(conn); From 8a514b8ed1faab9a4f748e21105f4b99872a07ea Mon Sep 17 00:00:00 2001 From: bel Date: Tue, 11 Mar 2014 19:41:24 +0100 Subject: [PATCH 72/80] Add comments for mg_get_valid_options and format spaces in the header --- include/civetweb.h | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/include/civetweb.h b/include/civetweb.h index 24d52465..a6ca3e72 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -1,4 +1,5 @@ -/* Copyright (c) 2004-2013 Sergey Lyubka +/* Copyright (c) 2013-2014 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -154,6 +155,7 @@ struct mg_callbacks { int (*http_error)(struct mg_connection *, int status); }; + /* Start web server. Parameters: @@ -191,6 +193,7 @@ CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks, threads are stopped. Context pointer becomes invalid. */ CIVETWEB_API void mg_stop(struct mg_context *); + /* mg_request_handler Called when a new request comes in. This callback is URI based @@ -204,6 +207,7 @@ CIVETWEB_API void mg_stop(struct mg_context *); 1: the handler processed the request. */ typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata); + /* mg_set_request_handler Sets or removes a URI mapping for a request handler. @@ -258,8 +262,11 @@ enum { CONFIG_TYPE_EXT_PATTERN = 0x6 }; -CIVETWEB_API const struct mg_option *mg_get_valid_options(void); +/* Return array of struct mg_option, representing all valid configuration + options of civetweb.c. + The array is terminated by a NULL name option. */ +CIVETWEB_API const struct mg_option *mg_get_valid_options(void); /* Get the list of ports that civetweb is listening on. @@ -271,6 +278,7 @@ CIVETWEB_API const struct mg_option *mg_get_valid_options(void); configuration at run time. */ CIVETWEB_API size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* ssl); + /* Add, edit or delete the entry in the passwords file. This function allows an application to manipulate .htpasswd files on the @@ -316,6 +324,7 @@ CIVETWEB_API int mg_write(struct mg_connection *, const void *buf, size_t len); CIVETWEB_API int mg_websocket_write(struct mg_connection* conn, int opcode, const char *data, size_t data_len); + /* Blocks until unique access is obtained to this connection. Intended for use with websockets only. Invoke this before mg_write or mg_printf when communicating with a @@ -324,6 +333,7 @@ CIVETWEB_API int mg_websocket_write(struct mg_connection* conn, int opcode, CIVETWEB_API void mg_lock(struct mg_connection* conn); CIVETWEB_API void mg_unlock(struct mg_connection* conn); + /* Opcodes, from http://tools.ietf.org/html/rfc6455 */ enum { WEBSOCKET_OPCODE_CONTINUATION = 0x0, @@ -355,7 +365,6 @@ enum { #endif /* Send data to the client using printf() semantics. - Works exactly like mg_write(), but allows to do message formatting. */ CIVETWEB_API int mg_printf(struct mg_connection *, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); @@ -403,6 +412,7 @@ CIVETWEB_API const char *mg_get_header(const struct mg_connection *, const char CIVETWEB_API int mg_get_var(const char *data, size_t data_len, const char *var_name, char *dst, size_t dst_len); + /* Get a value of particular form variable. Parameters: @@ -429,6 +439,7 @@ CIVETWEB_API int mg_get_var(const char *data, size_t data_len, CIVETWEB_API int mg_get_var2(const char *data, size_t data_len, const char *var_name, char *dst, size_t dst_len, size_t occurrence); + /* Fetch value of certain cookie variable into the destination buffer. Destination buffer is guaranteed to be '\0' - terminated. In case of @@ -491,6 +502,7 @@ CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name); /* Return Civetweb version. */ CIVETWEB_API const char *mg_version(void); + /* URL-decode input buffer into destination buffer. 0-terminate the destination buffer. form-url-encoded data differs from URI encoding in a way that it @@ -500,11 +512,13 @@ CIVETWEB_API const char *mg_version(void); CIVETWEB_API int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded); + /* URL-encode input buffer into destination buffer. returns the length of the resulting buffer or -1 is the buffer is too small. */ CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len); + /* MD5 hash given strings. Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of ASCIIz strings. When function returns, buf will contain human-readable @@ -524,9 +538,11 @@ CIVETWEB_API char *mg_md5(char buf[33], ...); CIVETWEB_API void mg_cry(struct mg_connection *conn, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); + /* utility method to compare two buffers, case incensitive. */ CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len); + #ifdef __cplusplus } #endif /* __cplusplus */ From 773866707e12549d1a3f5b7a54c318bb726ef513 Mon Sep 17 00:00:00 2001 From: bel Date: Tue, 11 Mar 2014 20:10:27 +0100 Subject: [PATCH 73/80] Apply patch from spuschhof / issue #9: Fix for Makefile for MinGW32/MSYS --- Makefile | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 133a1efe..392fa505 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# +# # Copyright (c) 2013 No Face Press, LLC # License http://opensource.org/licenses/mit-license.php MIT License # @@ -24,6 +24,8 @@ DOCDIR = $(DATAROOTDIR)/doc/$(CPROG) SYSCONFDIR = $(PREFIX)/etc HTMLDIR = $(DOCDIR) +UNAME := $(shell uname) + # desired configuration of the document root # never assume that the document_root actually # exists on the build machine. When building @@ -92,7 +94,7 @@ LIB_OBJECTS = $(filter-out $(MAIN_OBJECTS), $(BUILD_OBJECTS)) LIBS = -lpthread -lm -ifeq ($(TARGET_OS),LINUX) +ifeq ($(TARGET_OS),LINUX) LIBS += -ldl endif @@ -100,6 +102,13 @@ ifeq ($(TARGET_OS),LINUX) CAN_INSTALL = 1 endif +ifneq (, $(findstring MINGW32, $(UNAME))) + LIBS += -lws2_32 -lcomdlg32 + SHARED_LIB=dll +else + SHARED_LIB=so +endif + all: build help: @@ -174,7 +183,7 @@ endif lib: lib$(CPROG).a -slib: lib$(CPROG).so +slib: lib$(CPROG).$(SHARED_LIB) clean: rm -rf $(BUILD_DIR) @@ -182,16 +191,20 @@ clean: distclean: clean @rm -rf VS2012/Debug VS2012/*/Debug VS2012/*/*/Debug @rm -rf VS2012/Release VS2012/*/Release VS2012/*/*/Release - rm -f $(CPROG) lib$(CPROG).so lib$(CPROG).a *.dmg *.msi *.exe + rm -f $(CPROG) lib$(CPROG).so lib$(CPROG).a *.dmg *.msi *.exe lib$(CPROG).dll lib$(CPROG).dll.a lib$(CPROG).a: $(LIB_OBJECTS) - @rm -f $@ + @rm -f $@ ar cq $@ $(LIB_OBJECTS) lib$(CPROG).so: CFLAGS += -fPIC lib$(CPROG).so: $(LIB_OBJECTS) $(LCC) -shared -o $@ $(CFLAGS) $(LDFLAGS) $(LIB_OBJECTS) +lib$(CPROG).dll: CFLAGS += -fPIC +lib$(CPROG).dll: $(LIB_OBJECTS) + $(LCC) -shared -o $@ $(CFLAGS) $(LDFLAGS) $(LIB_OBJECTS) $(LIBS) -Wl,--out-implib,lib$(CPROG).dll.a + $(CPROG): $(BUILD_OBJECTS) $(LCC) -o $@ $(CFLAGS) $(LDFLAGS) $(BUILD_OBJECTS) $(LIBS) From 71fb5b98ce72b950b2c00cf9aceb4c870eb31685 Mon Sep 17 00:00:00 2001 From: bel Date: Tue, 11 Mar 2014 21:33:10 +0100 Subject: [PATCH 74/80] Issue #13: mg_start_thread is not 64 bit safe --- src/civetweb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index ea6c7d3b..90554d16 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -1765,9 +1765,9 @@ int mg_start_thread(mg_thread_func_t f, void *p) { #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 */ - return (long)_beginthread((void (__cdecl *)(void *)) f, USE_STACK_SIZE, p) == -1L ? -1 : 0; + return ((_beginthread((void (__cdecl *)(void *)) f, USE_STACK_SIZE, p) == ((uintptr_t)(-1L))) ? -1 : 0); #else - return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0; + return ((_beginthread((void (__cdecl *)(void *)) f, 0, p) == ((uintptr_t)(-1L))) ? -1 : 0); #endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ } @@ -1778,15 +1778,15 @@ static int mg_start_thread_with_id(unsigned (__stdcall *f)(void *), void *p, { uintptr_t uip; HANDLE threadhandle; - int result; + int result = 0; uip = _beginthreadex(NULL, 0, (unsigned (__stdcall *)(void *)) f, p, 0, NULL); threadhandle = (HANDLE) uip; - if (threadidptr != NULL) { + if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) { *threadidptr = threadhandle; + result = 1; } - result = (threadhandle == NULL) ? -1 : 0; return result; } From 0a4c02b948d9cc88435ea4c1855f10b7bc737167 Mon Sep 17 00:00:00 2001 From: bel Date: Tue, 11 Mar 2014 21:42:06 +0100 Subject: [PATCH 75/80] Issue #11: Method to get POST request parameters via C++ interface --- src/CivetServer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp index fc5a5700..8deb9e9a 100644 --- a/src/CivetServer.cpp +++ b/src/CivetServer.cpp @@ -151,7 +151,10 @@ CivetServer::getParam(struct mg_connection *conn, const char *name, std::string &dst, size_t occurrence) { const char *query = mg_get_request_info(conn)->query_string; - return getParam(query, strlen(query), name, dst, occurrence); + if (query) { + return getParam(query, strlen(query), name, dst, occurrence); + } + return false; } bool From 9ae753eac5294b4eb4a552261e26598138c88a25 Mon Sep 17 00:00:00 2001 From: bel Date: Wed, 12 Mar 2014 12:09:08 +0100 Subject: [PATCH 76/80] A Redesign for Shared Websockets for Lua is Required --- src/civetweb.c | 2 +- src/mod_lua.inl | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 90554d16..26290e92 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -5093,7 +5093,7 @@ static void handle_websocket_request(struct mg_connection *conn, const char *pat path) : 0; if (lua_websock || shared_lua_websock) { - /* TODO */ shared_lua_websock = 1; + /* TODO */ shared_lua_websock = 0; conn->lua_websocket_state = lua_websocket_new(path, conn, !!shared_lua_websock); if (conn->lua_websocket_state) { send_websocket_handshake(conn); diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 62551b34..89fb28fc 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -818,7 +818,7 @@ static int lua_error_handler(lua_State *L) } static void * lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize) { - + (void)ud; (void)osize; /* not used */ if (nsize == 0) { @@ -967,6 +967,15 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn, assert(conn->lua_websocket_state == NULL); + /* + lock + check if in list + yes: inc rec counter + no: create state, add to list + call add + unlock + */ + if (is_shared) { (void)pthread_mutex_lock(&conn->ctx->mutex); while (*shared_websock_list) { @@ -1052,6 +1061,12 @@ static int lua_websocket_data(struct mg_connection *conn, int bits, char *data, assert(lws_data->main != NULL); assert(lws_data->thread != NULL); + /* + lock + call data + unlock + */ + do { retry=0; @@ -1109,6 +1124,15 @@ static void lua_websocket_close(struct mg_connection *conn) assert(lws_data->main != NULL); assert(lws_data->thread != NULL); + /* + lock + call remove + dec ref counter + if ref counter == 0 close state and remove from list + unlock + */ + + lua_pushboolean(lws_data->thread, 0); err = lua_resume(lws_data->thread, NULL, 1); From 46832264cce8dcafc74dce32d0bab36f2c4cb46a Mon Sep 17 00:00:00 2001 From: bel Date: Wed, 12 Mar 2014 12:53:01 +0100 Subject: [PATCH 77/80] A Redesign for Shared Websockets for Lua is Required --- src/mod_lua.inl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/mod_lua.inl b/src/mod_lua.inl index 89fb28fc..189aabcf 100644 --- a/src/mod_lua.inl +++ b/src/mod_lua.inl @@ -923,6 +923,7 @@ struct lua_websock_data { char * script; unsigned shared; struct mg_connection *conn; + pthread_mutex_t mutex; }; struct mg_shared_lua_websocket { @@ -968,12 +969,14 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn, assert(conn->lua_websocket_state == NULL); /* - lock + lock list (mg_context global) check if in list yes: inc rec counter no: create state, add to list + lock list element + unlock list (mg_context global) call add - unlock + unlock list element */ if (is_shared) { @@ -1062,9 +1065,9 @@ static int lua_websocket_data(struct mg_connection *conn, int bits, char *data, assert(lws_data->thread != NULL); /* - lock + lock list element call data - unlock + unlock list element */ do { @@ -1125,11 +1128,13 @@ static void lua_websocket_close(struct mg_connection *conn) assert(lws_data->thread != NULL); /* - lock - call remove + lock list element + lock list (mg_context global) + call remove dec ref counter if ref counter == 0 close state and remove from list - unlock + unlock list element + unlock list (mg_context global) */ From dd7f89f0a5e428f4271abde6e8121aa53a7212bb Mon Sep 17 00:00:00 2001 From: bel Date: Wed, 12 Mar 2014 23:19:26 +0100 Subject: [PATCH 78/80] Comment CivetServer::getParam and check request_info (Issue #11) --- src/CivetServer.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp index 8deb9e9a..70a414da 100644 --- a/src/CivetServer.cpp +++ b/src/CivetServer.cpp @@ -150,10 +150,18 @@ bool CivetServer::getParam(struct mg_connection *conn, const char *name, std::string &dst, size_t occurrence) { - const char *query = mg_get_request_info(conn)->query_string; - if (query) { - return getParam(query, strlen(query), name, dst, occurrence); + const char *formParams = NULL; + mg_request_info * ri = mg_get_request_info(conn); + + if (ri) { + // get requests do store html
field values in the http query_string + formParams = ri->query_string; } + + if (formParams) { + return getParam(formParams, strlen(formParams), name, dst, occurrence); + } + return false; } From 51971ce6c7ff878be0f6eb0858aa1f5e71d7dd4e Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 13 Mar 2014 21:41:17 +0100 Subject: [PATCH 79/80] Show 'usage' information on console also for Windows --- src/main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main.c b/src/main.c index 00f2dd83..404dd01c 100644 --- a/src/main.c +++ b/src/main.c @@ -134,6 +134,15 @@ static void show_usage_and_exit(void) const struct mg_option *options; int i; +#ifdef WIN32 + if (!AttachConsole(ATTACH_PARENT_PROCESS)) { + AllocConsole(); + AttachConsole(GetCurrentProcessId()); + } + freopen("CON", "a", stdout); + freopen("CON", "a", stderr); +#endif + fprintf(stderr, "Civetweb v%s, built on %s\n", mg_version(), __DATE__); fprintf(stderr, "Usage:\n"); From a39c256884687603afc8f3ffbcb91409764ae3e7 Mon Sep 17 00:00:00 2001 From: bel Date: Thu, 13 Mar 2014 21:59:58 +0100 Subject: [PATCH 80/80] Fix compile error on Clang (Issue #14) --- src/civetweb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/civetweb.c b/src/civetweb.c index 26290e92..b681f460 100644 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -448,10 +448,10 @@ static void * mg_realloc_ex(void * memory, size_t newsize, const char * file, un #define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) #else -__inline void * mg_malloc(size_t a) {return malloc(a);} -__inline void * mg_calloc(size_t a, size_t b) {return calloc(a, b);} -__inline void * mg_realloc(void * a, size_t b) {return realloc(a, b);} -__inline void mg_free(void * a) {free(a);} +static __inline void * mg_malloc(size_t a) {return malloc(a);} +static __inline void * mg_calloc(size_t a, size_t b) {return calloc(a, b);} +static __inline void * mg_realloc(void * a, size_t b) {return realloc(a, b);} +static __inline void mg_free(void * a) {free(a);} #endif #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc