Browse Source

Merge branch 'master' into patch-2

bel2125 5 years ago
parent
commit
6f414dfa2e

+ 9 - 1
.gitignore

@@ -267,5 +267,13 @@ requests.db
 ci/lua
 ci/lua
 
 
 
 
-# Conan test cache
+##########################
+## Conan test cache
+##########################
 conan/test_package/build
 conan/test_package/build
+
+
+##########################
+## Visual Studio Code
+##########################
+.vscode

+ 16 - 8
CREDITS.md

@@ -11,6 +11,7 @@
 * beaver
 * beaver
 * bel2125
 * bel2125
 * Ben M. Ward
 * Ben M. Ward
+* Bernhard Lehner
 * BigJoe
 * BigJoe
 * Bjoern Petri
 * Bjoern Petri
 * Braedy Kuzma
 * Braedy Kuzma
@@ -20,9 +21,9 @@
 * Brian Spratke
 * Brian Spratke
 * cdbishop
 * cdbishop
 * celeron55
 * celeron55
+* Charles Olivi
 * Chris Jones
 * Chris Jones
 * Chris Rehn
 * Chris Rehn
-* Charles Olivi
 * Christian Mauderer
 * Christian Mauderer
 * Christopher Galas
 * Christopher Galas
 * cjh
 * cjh
@@ -38,6 +39,7 @@
 * David Loffredo
 * David Loffredo
 * Dialga
 * Dialga
 * Domenico Di Iorio
 * Domenico Di Iorio
+* Drew Wells
 * duong2179
 * duong2179
 * ehlertjd
 * ehlertjd
 * Eric Tsau
 * Eric Tsau
@@ -45,11 +47,11 @@
 * Erik Beran
 * Erik Beran
 * Erik Partridge
 * Erik Partridge
 * extergnoto
 * extergnoto
+* F-Secure Corporation
 * Fabrice Fontaine
 * Fabrice Fontaine
 * feneuilflo
 * feneuilflo
 * Fernando G. Aranda
 * Fernando G. Aranda
 * Frank Hilliger
 * Frank Hilliger
-* F-Secure Corporation
 * Grahack
 * Grahack
 * Gregor Jasny
 * Gregor Jasny
 * grenclave
 * grenclave
@@ -61,20 +63,21 @@
 * Herumb Shandilya
 * Herumb Shandilya
 * Herve Codina
 * Herve Codina
 * Iain Morton
 * Iain Morton
-* Ivan Dlugos
 * ImgBotApp
 * ImgBotApp
+* Ivan Dlugos
 * Jack
 * Jack
 * Jacob Repp
 * Jacob Repp
 * Jacob Skillin
 * Jacob Skillin
 * Jan Kowalewski
 * Jan Kowalewski
 * Jan Willem Janssen
 * Jan Willem Janssen
 * Jeremy Lin
 * Jeremy Lin
+* Jesse Williamson
 * Jim Evans
 * Jim Evans
 * jmc-
 * jmc-
 * Joakim L. Gilje
 * Joakim L. Gilje
 * Jochen Scheib
 * Jochen Scheib
-* Joel Gallant
 * Joe Mucchiello
 * Joe Mucchiello
+* Joel Gallant
 * Johan De Taeye
 * Johan De Taeye
 * John Faith
 * John Faith
 * Jordan
 * Jordan
@@ -94,9 +97,10 @@
 * Kimmo Mustonen
 * Kimmo Mustonen
 * Krzysztof Kozlowski
 * Krzysztof Kozlowski
 * Lammert Bies
 * Lammert Bies
+* Lars Immisch
 * Lawrence
 * Lawrence
-* Lianghui
 * Li Peng
 * Li Peng
+* Lianghui
 * Luka Rahne
 * Luka Rahne
 * Maarten Fremouw
 * Maarten Fremouw
 * makrsmark
 * makrsmark
@@ -120,19 +124,22 @@
 * palortoff
 * palortoff
 * Patrick Drechsler
 * Patrick Drechsler
 * Patrick Trinkle
 * Patrick Trinkle
-* Paulo Brizolara
 * Paul Sokolovsky
 * Paul Sokolovsky
+* Paulo Brizolara
 * pavel.pimenov
 * pavel.pimenov
 * PavelVozenilek
 * PavelVozenilek
 * Perttu Ahola
 * Perttu Ahola
 * Peter Foerster
 * Peter Foerster
 * Philipp Friedenberger
 * Philipp Friedenberger
 * Philipp Hasper
 * Philipp Hasper
+* Piotr Zierhoffer
 * pkvamme
 * pkvamme
 * Radoslaw Zarzynski
 * Radoslaw Zarzynski
 * Red54
 * Red54
 * Retallack Mark mark.retallack
 * Retallack Mark mark.retallack
 * Richard Screene
 * Richard Screene
+* Rimas Misevi-ìius
+* Rinat Dobrokhotov
 * ryankopf
 * ryankopf
 * Sage Weil
 * Sage Weil
 * Sangwhan Moon
 * Sangwhan Moon
@@ -147,17 +154,18 @@
 * SpaceLord
 * SpaceLord
 * sunfch
 * sunfch
 * suzukibitman
 * suzukibitman
+* Símal Rasmussen
 * Tamotsu Kanoh
 * Tamotsu Kanoh
 * thewaterymoon
 * thewaterymoon
 * Thiago Macedo
 * Thiago Macedo
 * THILMANT, Bernard
 * THILMANT, Bernard
 * Thomas Davis
 * Thomas Davis
 * Thomas Klausner
 * Thomas Klausner
-* Tomasz Gorochowik
 * Thorsten Horstmann
 * Thorsten Horstmann
 * tnoho
 * tnoho
-* Tomas Andrle
 * Tom Deblauwe
 * Tom Deblauwe
+* Tomas Andrle
+* Tomasz Gorochowik
 * Toni Wilk
 * Toni Wilk
 * Torben Jonas
 * Torben Jonas
 * Uilian Ries
 * Uilian Ries

+ 10 - 0
RELEASE_NOTES.md

@@ -5,6 +5,16 @@ Release Notes v1.12
 Changes
 Changes
 -------
 -------
 
 
+- Format configuration dialogs for Windows
+- Add option "hide_tray" to start without Windows systray icon
+- URI checking according to "remove_dot_segments" algorithm from RFC
+- Experimental support for a new server and client start API
+- Additional callbacks to initialize external SSL context
+- More cache control options for static files
+- Trace function for Lua server pages
+- Access to client certificate data for Lua pages
+- Allow to configure SOMAXCONN (max. number of waiting connections)
+- Include some build options for Zephyr
 - Support for flawed CGI interpreters returning only <LF> instead of <CR><LF>
 - Support for flawed CGI interpreters returning only <LF> instead of <CR><LF>
 - Add NO_FILESYSTEM flag for (embedded) system without any file system
 - Add NO_FILESYSTEM flag for (embedded) system without any file system
 - Several fixes for server side Lua scripts
 - Several fixes for server side Lua scripts

+ 15 - 1
docs/UserManual.md

@@ -617,6 +617,20 @@ than the depth set here connection is refused.
 ### ssl\_verify\_peer `no`
 ### ssl\_verify\_peer `no`
 Enable client's certificate verification by the server.
 Enable client's certificate verification by the server.
 
 
+### static\_file\_cache\_control
+Set the `Cache-Control` header of static files responses.
+The string value will be used directly.
+
+E.g. this config:
+
+`static_file_cache_control no-cache, max-age=31536000`
+
+Will result in this header being added:
+
+`Cache-Control: no-cache, max-age=31536000`
+
+This will take precedence over the static\_file\_max\_age option.
+
 ### static\_file\_max\_age `3600`
 ### static\_file\_max\_age `3600`
 Set the maximum time (in seconds) a cache may store a static files.
 Set the maximum time (in seconds) a cache may store a static files.
 
 
@@ -626,7 +640,7 @@ must send cache control headers by themselves.
 
 
 A value >0 corresponds to a maximum allowed caching time in seconds.
 A value >0 corresponds to a maximum allowed caching time in seconds.
 This value should not exceed one year (RFC 2616, Section 14.21).
 This value should not exceed one year (RFC 2616, Section 14.21).
-A value of 0 will send "do not cache" headers for all static files.
+A value of 0 will send "do not cache at all" headers for all static files.
 For values <0 and values >31622400, the behaviour is undefined.
 For values <0 and values >31622400, the behaviour is undefined.
 
 
 ### strict\_transport\_security\_max\_age
 ### strict\_transport\_security\_max\_age

+ 8 - 4
docs/api/mg_callbacks.md

@@ -20,10 +20,14 @@
 | |The callback function `init_context()` is called after the CivetWeb server has been started and initialized, but before any requests are served. This allowes the application to perform some initialization activities before the first requests are handled.|
 | |The callback function `init_context()` is called after the CivetWeb server has been started and initialized, but before any requests are served. This allowes the application to perform some initialization activities before the first requests are handled.|
 |**`init_lua`**|**`void (*init_lua)( const struct mg_connection *conn, void *lua_context );`**|
 |**`init_lua`**|**`void (*init_lua)( const struct mg_connection *conn, void *lua_context );`**|
 | |The callback function `init_lua()` is called just before a Lua server page is to be served. Lua page serving must have been enabled at compile time for this callback function to be called. The parameter `lua_context` is a `lua_State *` pointer.|
 | |The callback function `init_lua()` is called just before a Lua server page is to be served. Lua page serving must have been enabled at compile time for this callback function to be called. The parameter `lua_context` is a `lua_State *` pointer.|
-|**`external_ssl_ctx`**|**`int (*external_ssl_ctx)(void **ssl_ctx, void *user_data);`**|
-| |The callback function `external_ssl_ctx()` is called when civetweb is about to create (`*ssl_ctx` is `NULL`) or free (`*ssl_ctx` is not `NULL`) a SSL context. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL context. With a return value of 1 the callback function signals CivetWeb that the SSL context has already been setup and no further processing is necessary. Also with a return value of 1 another callback function `init_ssl()` is not called. The value -1 should be returned when the SSL context initialization fails.|
-|**`init_ssl`**|**`int (*init_ssl)( void *ssl_context, void *user_data );`**|
-| |The callback function `init_ssl()` is called when CivetWeb initializes the SSL library. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL certificate. With a return value of 1 the callback function signals CivetWeb that the certificate has already been setup and no further processing is necessary. The value -1 should be returned when the SSL initialization fails.|
+|**`external_ssl_ctx`**|**`int (*external_ssl_ctx)( void **ssl_ctx, void *user_data );`**|
+| |The callback function `external_ssl_ctx()` is called when civetweb is about to create (`*ssl_ctx` is `NULL`) or free (`*ssl_ctx` is not `NULL`) a SSL context. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL context. With a return value of 1 the callback function signals CivetWeb that the SSL context has already been setup and no further processing is necessary. Also with a return value of 1 other callback functions `init_ssl()` and `init_ssl_domain()` are not called. The value -1 should be returned when the SSL context initialization fails.|
+|**`external_ssl_ctx_domain`**|**`int (*external_ssl_ctx_domain)( const char *server_domain, void **ssl_ctx, void *user_data );`**|
+| |The callback function `external_ssl_ctx_domain()` is called when civetweb is about to create (`*ssl_ctx` is `NULL`) or free (`*ssl_ctx` is not `NULL`) a SSL context. The parameter `server_domain` is a pointer to the `authentication_domain` config parameter of the domain being created or freed. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL context. With a return value of 1 the callback function signals CivetWeb that the SSL context has already been setup and no further processing is necessary. Also with a return value of 1 other callback functions `init_ssl()` and `init_ssl_domain()` are not called. The value -1 should be returned when the SSL context initialization fails.|
+|**`init_ssl`**|**`int (*init_ssl)( void *ssl_ctx, void *user_data );`**|
+| |The callback function `init_ssl()` is called when CivetWeb initializes the SSL library. The `ssl_ctx` parameter is a pointer to the SSL context being configure. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL certificate. With a return value of 1 the callback function signals CivetWeb that the certificate has already been setup and no further processing is necessary. The value -1 should be returned when the SSL initialization fails.|
+|**`init_ssl_domain`**|**`int (*init_ssl_domain)( const char *server_domain, void *ssl_ctx, void *user_data );`**|
+| |The callback function `init_ssl_domain()` is called when CivetWeb initializes the SSL library. The parameter `server_domain` is a pointer to the `authentication_domain` config parameter of the domain being configured. The `ssl_ctx` parameter is a pointer to the SSL context being configure. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL certificate. With a return value of 1 the callback function signals CivetWeb that the certificate has already been setup and no further processing is necessary. The value -1 should be returned when the SSL initialization fails.|
 |**`init_thread`**|**`void * (*init_thread)( const struct mg_context *ctx, int thread_type );`**|
 |**`init_thread`**|**`void * (*init_thread)( const struct mg_context *ctx, int thread_type );`**|
 | |The callback function `init_thread()` is called when a new thread is created by CivetWeb. The `thread_type` parameter indicates which type of thread has been created. following thread types are recognized:|
 | |The callback function `init_thread()` is called when a new thread is created by CivetWeb. The `thread_type` parameter indicates which type of thread has been created. following thread types are recognized:|
 | |**0** - The master thread is created |
 | |**0** - The master thread is created |

+ 1 - 1
examples/embedded_c/Makefile

@@ -18,7 +18,7 @@ LIBS = -lpthread
 include $(TOP)/resources/Makefile.in-os
 include $(TOP)/resources/Makefile.in-os
 
 
 ifeq ($(TARGET_OS),LINUX) 
 ifeq ($(TARGET_OS),LINUX) 
-	LIBS += -ldl
+	LIBS += -ldl -lrt
 endif
 endif
 
 
 all: $(PROG)
 all: $(PROG)

+ 2 - 2
examples/embedded_c/embedded_c.c

@@ -916,10 +916,10 @@ get_dh2236()
 
 
 #ifndef TEST_WITHOUT_SSL
 #ifndef TEST_WITHOUT_SSL
 int
 int
-init_ssl(void *ssl_context, void *user_data)
+init_ssl(void *ssl_ctx, void *user_data)
 {
 {
 	/* Add application specific SSL initialization */
 	/* Add application specific SSL initialization */
-	struct ssl_ctx_st *ctx = (struct ssl_ctx_st *)ssl_context;
+	struct ssl_ctx_st *ctx = (struct ssl_ctx_st *)ssl_ctx;
 
 
 #ifdef USE_SSL_DH
 #ifdef USE_SSL_DH
 	/* example from https://github.com/civetweb/civetweb/issues/347 */
 	/* example from https://github.com/civetweb/civetweb/issues/347 */

+ 1 - 1
examples/embedded_cpp/Makefile

@@ -18,7 +18,7 @@ LIBS = -lpthread
 include $(TOP)/resources/Makefile.in-os
 include $(TOP)/resources/Makefile.in-os
 
 
 ifeq ($(TARGET_OS),LINUX) 
 ifeq ($(TARGET_OS),LINUX) 
-	LIBS += -ldl
+	LIBS += -ldl -lrt
 endif
 endif
 
 
 all: $(PROG)
 all: $(PROG)

+ 98 - 18
include/civetweb.h

@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2018 the Civetweb developers
+/* Copyright (c) 2013-2019 the Civetweb developers
  * Copyright (c) 2004-2013 Sergey Lyubka
  * Copyright (c) 2004-2013 Sergey Lyubka
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -247,28 +247,54 @@ struct mg_callbacks {
 
 
 	/* Called when civetweb initializes SSL library.
 	/* Called when civetweb initializes SSL library.
 	   Parameters:
 	   Parameters:
+	     ssl_ctx: SSL_CTX pointer.
 	     user_data: parameter user_data passed when starting the server.
 	     user_data: parameter user_data passed when starting the server.
 	   Return value:
 	   Return value:
 	     0: civetweb will set up the SSL certificate.
 	     0: civetweb will set up the SSL certificate.
 	     1: civetweb assumes the callback already set up the certificate.
 	     1: civetweb assumes the callback already set up the certificate.
 	    -1: initializing ssl fails. */
 	    -1: initializing ssl fails. */
-	int (*init_ssl)(void *ssl_context, void *user_data);
+	int (*init_ssl)(void *ssl_ctx, void *user_data);
 
 
-	/* Called when civetweb is about to create or free a SSL_CTX.
-	Parameters:
-	   ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when mg_context
-	            will be freed
+	/* Called when civetweb initializes SSL library for a domain.
+	   Parameters:
+	     server_domain: authentication_domain from the domain config.
+	     ssl_ctx: SSL_CTX pointer.
 	     user_data: parameter user_data passed when starting the server.
 	     user_data: parameter user_data passed when starting the server.
 	   Return value:
 	   Return value:
-	     0: civetweb will continue to create the context, just as if the
-	        callback would not be present.
-	        The value in *ssl_ctx when the function returns is ignored.
-	     1: civetweb will copy the value from *ssl_ctx to the civetweb context
-	        and doesn't create its own.
-	    -1: initializing ssl fails.*/
+	     0: civetweb will set up the SSL certificate.
+	     1: civetweb assumes the callback already set up the certificate.
+	    -1: initializing ssl fails. */
+	int (*init_ssl_domain)(const char *server_domain,
+	                       void *ssl_ctx,
+	                       void *user_data);
+
+	/* Called when civetweb is about to create or free a SSL_CTX.
+	Parameters:
+	     ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when
+	mg_context will be freed user_data: parameter user_data passed when starting
+	the server. Return value: 0: civetweb will continue to create the context,
+	just as if the callback would not be present. The value in *ssl_ctx when the
+	function returns is ignored. 1: civetweb will copy the value from *ssl_ctx
+	to the civetweb context and doesn't create its own. -1: initializing ssl
+	fails.*/
 	int (*external_ssl_ctx)(void **ssl_ctx, void *user_data);
 	int (*external_ssl_ctx)(void **ssl_ctx, void *user_data);
 
 
-#if defined(MG_LEGACY_INTERFACE) /* 2015-08-19 */
+	/* Called when civetweb is about to create or free a SSL_CTX for a domain.
+	Parameters:
+	     server_domain: authentication_domain from the domain config.
+	     ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when
+	mg_context will be freed user_data: parameter user_data passed when starting
+	the server. Return value: 0: civetweb will continue to create the context,
+	just as if the callback would not be present. The value in *ssl_ctx when the
+	function returns is ignored. 1: civetweb will copy the value from *ssl_ctx
+	to the civetweb context and doesn't create its own. -1: initializing ssl
+	fails.*/
+	int (*external_ssl_ctx_domain)(const char *server_domain,
+	                               void **ssl_ctx,
+	                               void *user_data);
+
+#if defined(MG_LEGACY_INTERFACE)           /* 2015-08-19 */                    \
+    || defined(MG_EXPERIMENTAL_INTERFACES) /* 2019-11-03 */
 	/* Called when websocket request is received, before websocket handshake.
 	/* Called when websocket request is received, before websocket handshake.
 	   Return value:
 	   Return value:
 	     0: civetweb proceeds with websocket handshake.
 	     0: civetweb proceeds with websocket handshake.
@@ -381,7 +407,6 @@ struct mg_callbacks {
 	                    int thread_type,
 	                    int thread_type,
 	                    void *thread_pointer);
 	                    void *thread_pointer);
 
 
-
 	/* Called when initializing a new connection object.
 	/* Called when initializing a new connection object.
 	 * Can be used to initialize the connection specific user data
 	 * Can be used to initialize the connection specific user data
 	 * (mg_request_info->conn_data, mg_get_user_connection_data).
 	 * (mg_request_info->conn_data, mg_get_user_connection_data).
@@ -618,6 +643,10 @@ mg_get_context(const struct mg_connection *conn);
 CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
 CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
 
 
 
 
+/* Get user data passed to mg_start from connection. */
+CIVETWEB_API void *mg_get_user_context_data(const struct mg_connection *conn);
+
+
 /* Get user defined thread pointer for server threads (see init_thread). */
 /* Get user defined thread pointer for server threads (see init_thread). */
 CIVETWEB_API void *mg_get_thread_pointer(const struct mg_connection *conn);
 CIVETWEB_API void *mg_get_thread_pointer(const struct mg_connection *conn);
 
 
@@ -1149,6 +1178,10 @@ CIVETWEB_API int mg_get_cookie(const char *cookie,
      struct mg_connection *conn;
      struct mg_connection *conn;
      conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
      conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
                         "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
                         "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
+
+   mg_download is equivalent to calling mg_connect_client followed by
+   mg_printf and mg_get_response. Using these three functions directly may
+   allow more control as compared to using mg_download.
  */
  */
 CIVETWEB_API struct mg_connection *
 CIVETWEB_API struct mg_connection *
 mg_download(const char *host,
 mg_download(const char *host,
@@ -1425,7 +1458,9 @@ mg_connect_client_secure(const struct mg_client_options *client_options,
                          size_t error_buffer_size);
                          size_t error_buffer_size);
 
 
 
 
+#if defined(MG_LEGACY_INTERFACE) /* 2019-11-02 */
 enum { TIMEOUT_INFINITE = -1 };
 enum { TIMEOUT_INFINITE = -1 };
+#endif
 enum { MG_TIMEOUT_INFINITE = -1 };
 enum { MG_TIMEOUT_INFINITE = -1 };
 
 
 /* Wait for a response from the server
 /* Wait for a response from the server
@@ -1464,13 +1499,17 @@ CIVETWEB_API int mg_get_response(struct mg_connection *conn,
         64  support server side JavaScript (USE_DUKTAPE is set)
         64  support server side JavaScript (USE_DUKTAPE is set)
        128  support caching (NO_CACHING not set)
        128  support caching (NO_CACHING not set)
        256  support server statistics (USE_SERVER_STATS is set)
        256  support server statistics (USE_SERVER_STATS is set)
+       512  support for on the fly compression (USE_ZLIB is set)
+
+       These values are defined as MG_FEATURES_*
+
        The result is undefined, if bits are set that do not represent a
        The result is undefined, if bits are set that do not represent a
-       defined feature (currently: feature >= 512).
+       defined feature (currently: feature >= 1024).
        The result is undefined, if no bit is set (feature == 0).
        The result is undefined, if no bit is set (feature == 0).
 
 
    Return:
    Return:
-     If feature is available, the corresponding bit is set
-     If feature is not available, the bit is 0
+     If a feature is available, the corresponding bit is set
+     If a feature is not available, the bit is 0
 */
 */
 CIVETWEB_API unsigned mg_check_feature(unsigned feature);
 CIVETWEB_API unsigned mg_check_feature(unsigned feature);
 
 
@@ -1510,7 +1549,7 @@ CIVETWEB_API int
 mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen);
 mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen);
 
 
 
 
-#ifdef MG_EXPERIMENTAL_INTERFACES
+#if defined(MG_EXPERIMENTAL_INTERFACES)
 /* Get connection information. Useful for server diagnosis.
 /* Get connection information. Useful for server diagnosis.
    Parameters:
    Parameters:
      ctx: Context handle
      ctx: Context handle
@@ -1534,6 +1573,47 @@ CIVETWEB_API int mg_get_connection_info(const struct mg_context *ctx,
 #endif
 #endif
 
 
 
 
+/* New APIs for enhanced option and error handling.
+   These mg_*2 API functions have the same purpose as their original versions,
+   but provide additional options and/or provide improved error diagnostics.
+
+   Note: Experimental interfaces may change
+*/
+struct mg_error_data {
+	unsigned *code;          /* error code (number) */
+	char *text;              /* buffer for error text */
+	size_t text_buffer_size; /* size of buffer of "text" */
+};
+
+struct mg_init_data {
+	const struct mg_callbacks *callbacks; /* callback function pointer */
+	void *user_data;                      /* data */
+	const char **configuration_options;
+};
+
+
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client2(const char *host,
+                   const char *protocol,
+                   int port,
+                   const char *path,
+                   struct mg_init_data *init,
+                   struct mg_error_data *error);
+
+CIVETWEB_API int mg_get_response2(struct mg_connection *conn,
+                                  struct mg_error_data *error,
+                                  int timeout);
+
+CIVETWEB_API struct mg_context *mg_start2(struct mg_init_data *init,
+                                          struct mg_error_data *error);
+
+CIVETWEB_API int mg_start_domain2(struct mg_context *ctx,
+                                  const char **configuration_options,
+                                  struct mg_error_data *error);
+#endif
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif /* __cplusplus */
 #endif /* __cplusplus */

File diff suppressed because it is too large
+ 594 - 100
src/civetweb.c


+ 130 - 93
src/main.c

@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2018 the Civetweb developers
+/* Copyright (c) 2013-2020 the Civetweb developers
  * Copyright (c) 2004-2013 Sergey Lyubka
  * Copyright (c) 2004-2013 Sergey Lyubka
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -184,7 +184,8 @@ static int g_num_add_domains;     /* Default from init_server_name,
                                    * updated later from the server config */
                                    * updated later from the server config */
 static const char **g_add_domain; /* Default from init_server_name,
 static const char **g_add_domain; /* Default from init_server_name,
                                    * updated later from the server config */
                                    * updated later from the server config */
-
+static int g_hide_tray = 0;       /* Default = do not hide (0),
+                                   * updated later from the server config */
 
 
 static char *g_system_info; /* Set by init_system_info() */
 static char *g_system_info; /* Set by init_system_info() */
 static char g_config_file_name[PATH_MAX] =
 static char g_config_file_name[PATH_MAX] =
@@ -213,6 +214,7 @@ enum {
 	OPTION_ICON,
 	OPTION_ICON,
 	OPTION_WEBPAGE,
 	OPTION_WEBPAGE,
 	OPTION_ADD_DOMAIN,
 	OPTION_ADD_DOMAIN,
+	OPTION_HIDE_TRAY,
 	NUM_MAIN_OPTIONS
 	NUM_MAIN_OPTIONS
 };
 };
 
 
@@ -221,6 +223,7 @@ static struct mg_option main_config_options[] = {
     {"icon", MG_CONFIG_TYPE_STRING, NULL},
     {"icon", MG_CONFIG_TYPE_STRING, NULL},
     {"website", MG_CONFIG_TYPE_STRING, NULL},
     {"website", MG_CONFIG_TYPE_STRING, NULL},
     {"add_domain", MG_CONFIG_TYPE_STRING_LIST, NULL},
     {"add_domain", MG_CONFIG_TYPE_STRING_LIST, NULL},
+    {"hide_tray", MG_CONFIG_TYPE_BOOLEAN, NULL},
     {NULL, MG_CONFIG_TYPE_UNKNOWN, NULL}};
     {NULL, MG_CONFIG_TYPE_UNKNOWN, NULL}};
 
 
 
 
@@ -584,6 +587,14 @@ set_option(char **options, const char *name, const char *value)
 			g_website = sdup(value);
 			g_website = sdup(value);
 			return 1;
 			return 1;
 		}
 		}
+		if (!strcmp(name, main_config_options[OPTION_HIDE_TRAY].name)) {
+			if (!strcmp(value, "yes")) {
+				g_hide_tray = 1;
+			} else if (!strcmp(value, "no")) {
+				g_hide_tray = 0;
+			}
+			return 1;
+		}
 		if (!strcmp(name, main_config_options[OPTION_ADD_DOMAIN].name)) {
 		if (!strcmp(name, main_config_options[OPTION_ADD_DOMAIN].name)) {
 			if (g_num_add_domains > 0) {
 			if (g_num_add_domains > 0) {
 				g_add_domain = (const char **)realloc(
 				g_add_domain = (const char **)realloc(
@@ -1562,13 +1573,16 @@ struct dlg_proc_param {
 };
 };
 
 
 struct dlg_header_param {
 struct dlg_header_param {
+	/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-dlgtemplate
+	 */
 	DLGTEMPLATE dlg_template; /* 18 bytes */
 	DLGTEMPLATE dlg_template; /* 18 bytes */
 	WORD menu, dlg_class;
 	WORD menu, dlg_class;
 	wchar_t caption[1];
 	wchar_t caption[1];
-	WORD fontsiz;
+	WORD fontsize;
 	wchar_t fontface[7];
 	wchar_t fontface[7];
 };
 };
 
 
+
 static struct dlg_header_param
 static struct dlg_header_param
 GetDlgHeader(const short width)
 GetDlgHeader(const short width)
 {
 {
@@ -1577,26 +1591,29 @@ GetDlgHeader(const short width)
 #pragma warning(push)
 #pragma warning(push)
 #pragma warning(disable : 4204)
 #pragma warning(disable : 4204)
 #endif /* if defined(_MSC_VER) */
 #endif /* if defined(_MSC_VER) */
-	struct dlg_header_param dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU
-	                                              | WS_VISIBLE | DS_SETFONT
-	                                              | WS_DLGFRAME,
-	                                          WS_EX_TOOLWINDOW,
-	                                          0,
-	                                          200,
-	                                          200,
-	                                          width,
-	                                          0},
-	                                         0,
-	                                         0,
-	                                         L"",
-	                                         8,
-	                                         L"Tahoma"};
+
+	struct dlg_header_param dialog_header = {
+	    /* DLGTEMPLATE */
+	    {/* style */ WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE
+	         | DS_SETFONT | DS_CENTER | WS_DLGFRAME,
+	     /* extstyle */ WS_EX_TOOLWINDOW,
+	     /* cdit */ 0,
+	     /* x ignored by DS_CENTER */ 0,
+	     /* y ignored by DS_CENTER */ 0,
+	     width,
+	     /* height - to be calculated */ 0},
+	    /* menu */ 0,
+	    /* dlg_class */ 0,
+	    /* caption */ L"",
+	    /* fontsize */ 8,
+	    /* font */ L"Tahoma"};
 #if defined(_MSC_VER)
 #if defined(_MSC_VER)
 #pragma warning(pop)
 #pragma warning(pop)
 #endif /* if defined(_MSC_VER) */
 #endif /* if defined(_MSC_VER) */
 	return dialog_header;
 	return dialog_header;
 }
 }
 
 
+
 /* Dialog proc for settings dialog */
 /* Dialog proc for settings dialog */
 static INT_PTR CALLBACK
 static INT_PTR CALLBACK
 SettingsDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 SettingsDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
@@ -1888,10 +1905,12 @@ get_password(const char *user,
              char *passwd,
              char *passwd,
              unsigned passwd_len)
              unsigned passwd_len)
 {
 {
-#define HEIGHT (15)
-#define WIDTH (280)
-#define LABEL_WIDTH (90)
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short WIDTH = 280;
+	short LABEL_WIDTH = 90;
 
 
+	/* Other variables */
 	unsigned char mem[4096], *p;
 	unsigned char mem[4096], *p;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	int ok;
 	int ok;
@@ -2030,16 +2049,12 @@ get_password(const char *user,
 
 
 	ok = (IDOK
 	ok = (IDOK
 	      == DialogBoxIndirectParam(
 	      == DialogBoxIndirectParam(
-	             NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
+	          NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
 
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
 	s_dlg_proc_param.guard = 0;
 
 
 	return ok;
 	return ok;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 }
 
 
 
 
@@ -2163,19 +2178,28 @@ add_control(unsigned char **mem,
 static void
 static void
 show_settings_dialog()
 show_settings_dialog()
 {
 {
-#define HEIGHT (15)
-#define WIDTH (460)
-#define LABEL_WIDTH (90)
-
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short BORDER_WIDTH = 10;
+	short CELL_WIDTH = 125;
+	short LABEL_WIDTH = 115;
+	short FILE_DIALOG_BUTTON_WIDTH = 15;
+	short NO_OF_COLUMNS = 3;
+
+	/* Calculates size */
+	short COLUMN_WIDTH = LABEL_WIDTH + CELL_WIDTH + BORDER_WIDTH;
+	short DIALOG_WIDTH = BORDER_WIDTH + NO_OF_COLUMNS * COLUMN_WIDTH;
+
+	/* All other variables */
 	unsigned char mem[16 * 1024], *p;
 	unsigned char mem[16 * 1024], *p;
 	const struct mg_option *options;
 	const struct mg_option *options;
 	DWORD style;
 	DWORD style;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	WORD i, cl, nelems = 0;
 	WORD i, cl, nelems = 0;
-	short width, x, y;
+	short x, y, next_cell_width;
 	static struct dlg_proc_param s_dlg_proc_param;
 	static struct dlg_proc_param s_dlg_proc_param;
 
 
-	const struct dlg_header_param dialog_header = GetDlgHeader(WIDTH);
+	const struct dlg_header_param dialog_header = GetDlgHeader(DIALOG_WIDTH);
 
 
 	if (s_dlg_proc_param.guard == 0) {
 	if (s_dlg_proc_param.guard == 0) {
 		memset(&s_dlg_proc_param, 0, sizeof(s_dlg_proc_param));
 		memset(&s_dlg_proc_param, 0, sizeof(s_dlg_proc_param));
@@ -2193,9 +2217,11 @@ show_settings_dialog()
 	options = mg_get_valid_options();
 	options = mg_get_valid_options();
 	for (i = 0; options[i].name != NULL; i++) {
 	for (i = 0; options[i].name != NULL; i++) {
 		style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
 		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;
+
+		x = BORDER_WIDTH + COLUMN_WIDTH * (nelems % NO_OF_COLUMNS);
+		y = BORDER_WIDTH / 2 + HEIGHT + HEIGHT * (nelems / NO_OF_COLUMNS);
+		next_cell_width = CELL_WIDTH;
+
 		if (options[i].type == MG_CONFIG_TYPE_NUMBER) {
 		if (options[i].type == MG_CONFIG_TYPE_NUMBER) {
 			style |= ES_NUMBER;
 			style |= ES_NUMBER;
 			cl = 0x81;
 			cl = 0x81;
@@ -2206,19 +2232,24 @@ show_settings_dialog()
 		} else if ((options[i].type == MG_CONFIG_TYPE_FILE)
 		} else if ((options[i].type == MG_CONFIG_TYPE_FILE)
 		           || (options[i].type == MG_CONFIG_TYPE_DIRECTORY)) {
 		           || (options[i].type == MG_CONFIG_TYPE_DIRECTORY)) {
 			style |= WS_BORDER | ES_AUTOHSCROLL;
 			style |= WS_BORDER | ES_AUTOHSCROLL;
-			width -= 20;
 			cl = 0x81;
 			cl = 0x81;
+
+			/* Additional button for file dialog */
 			add_control(&p,
 			add_control(&p,
 			            dia,
 			            dia,
 			            0x80,
 			            0x80,
 			            ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA,
 			            ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA,
 			            WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
 			            WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
-			            x + width + LABEL_WIDTH + 5,
+			            x + LABEL_WIDTH + CELL_WIDTH - FILE_DIALOG_BUTTON_WIDTH,
 			            y,
 			            y,
-			            15,
-			            12,
+			            FILE_DIALOG_BUTTON_WIDTH,
+			            HEIGHT - 3,
 			            "...");
 			            "...");
+
+			next_cell_width -= FILE_DIALOG_BUTTON_WIDTH + BORDER_WIDTH / 2;
+
 		} else if (options[i].type == MG_CONFIG_TYPE_STRING_MULTILINE) {
 		} else if (options[i].type == MG_CONFIG_TYPE_STRING_MULTILINE) {
+
 			/* TODO: This is not really uer friendly */
 			/* TODO: This is not really uer friendly */
 			cl = 0x81;
 			cl = 0x81;
 			style |= WS_BORDER | ES_AUTOHSCROLL | ES_MULTILINE | ES_WANTRETURN
 			style |= WS_BORDER | ES_AUTOHSCROLL | ES_MULTILINE | ES_WANTRETURN
@@ -2227,6 +2258,8 @@ show_settings_dialog()
 			cl = 0x81;
 			cl = 0x81;
 			style |= WS_BORDER | ES_AUTOHSCROLL;
 			style |= WS_BORDER | ES_AUTOHSCROLL;
 		}
 		}
+
+		/* Add label (static text) */
 		add_control(&p,
 		add_control(&p,
 		            dia,
 		            dia,
 		            0x82,
 		            0x82,
@@ -2244,32 +2277,35 @@ show_settings_dialog()
 		            style,
 		            style,
 		            x + LABEL_WIDTH,
 		            x + LABEL_WIDTH,
 		            y,
 		            y,
-		            width,
-		            12,
+		            next_cell_width,
+		            HEIGHT - 3,
 		            "");
 		            "");
 		nelems++;
 		nelems++;
 
 
 		DEBUG_ASSERT(((intptr_t)p - (intptr_t)mem) < (intptr_t)sizeof(mem));
 		DEBUG_ASSERT(((intptr_t)p - (intptr_t)mem) < (intptr_t)sizeof(mem));
 	}
 	}
 
 
-	y = (((nelems + 1) / 2 + 1) * HEIGHT + 5);
+	/* "Settings" frame around all options */
+	y = ((nelems + NO_OF_COLUMNS - 1) / NO_OF_COLUMNS + 1) * HEIGHT;
 	add_control(&p,
 	add_control(&p,
 	            dia,
 	            dia,
 	            0x80,
 	            0x80,
 	            ID_GROUP,
 	            ID_GROUP,
 	            WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
 	            WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
-	            5,
-	            5,
-	            WIDTH - 10,
+	            BORDER_WIDTH / 2,
+	            BORDER_WIDTH / 2,
+	            DIALOG_WIDTH - BORDER_WIDTH,
 	            y,
 	            y,
 	            " Settings ");
 	            " Settings ");
-	y += 10;
+
+	/* Buttons below "Settings" frame */
+	y += HEIGHT;
 	add_control(&p,
 	add_control(&p,
 	            dia,
 	            dia,
 	            0x80,
 	            0x80,
 	            ID_SAVE,
 	            ID_SAVE,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 70,
+	            DIALOG_WIDTH - 70,
 	            y,
 	            y,
 	            65,
 	            65,
 	            12,
 	            12,
@@ -2279,7 +2315,7 @@ show_settings_dialog()
 	            0x80,
 	            0x80,
 	            ID_RESET_DEFAULTS,
 	            ID_RESET_DEFAULTS,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 140,
+	            DIALOG_WIDTH - 140,
 	            y,
 	            y,
 	            65,
 	            65,
 	            12,
 	            12,
@@ -2289,7 +2325,7 @@ show_settings_dialog()
 	            0x80,
 	            0x80,
 	            ID_RESET_FILE,
 	            ID_RESET_FILE,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 210,
+	            DIALOG_WIDTH - 210,
 	            y,
 	            y,
 	            65,
 	            65,
 	            12,
 	            12,
@@ -2299,7 +2335,7 @@ show_settings_dialog()
 	            0x80,
 	            0x80,
 	            ID_RESET_ACTIVE,
 	            ID_RESET_ACTIVE,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 280,
+	            DIALOG_WIDTH - 280,
 	            y,
 	            y,
 	            65,
 	            65,
 	            12,
 	            12,
@@ -2315,9 +2351,11 @@ show_settings_dialog()
 	            12,
 	            12,
 	            g_server_base_name);
 	            g_server_base_name);
 
 
+	/* Check befor every release */
 	DEBUG_ASSERT(((intptr_t)p - (intptr_t)mem) < (intptr_t)sizeof(mem));
 	DEBUG_ASSERT(((intptr_t)p - (intptr_t)mem) < (intptr_t)sizeof(mem));
 
 
-	dia->cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30;
+	/* Calculate total height of the dialog */
+	dia->cy = y + HEIGHT;
 
 
 	s_dlg_proc_param.fRetry = NULL;
 	s_dlg_proc_param.fRetry = NULL;
 
 
@@ -2326,20 +2364,18 @@ show_settings_dialog()
 
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
 	s_dlg_proc_param.guard = 0;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 }
 
 
 
 
 static void
 static void
 change_password_file()
 change_password_file()
 {
 {
-#define HEIGHT (15)
-#define WIDTH (320)
-#define LABEL_WIDTH (90)
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short WIDTH = 320;
+	short LABEL_WIDTH = 90;
 
 
+	/* Other variables */
 	OPENFILENAME of;
 	OPENFILENAME of;
 	char path[PATH_MAX] = PASSWORDS_FILE_NAME;
 	char path[PATH_MAX] = PASSWORDS_FILE_NAME;
 	char strbuf[256], u[256], d[256];
 	char strbuf[256], u[256], d[256];
@@ -2519,18 +2555,13 @@ change_password_file()
 		s_dlg_proc_param.name = path;
 		s_dlg_proc_param.name = path;
 		s_dlg_proc_param.fRetry = NULL;
 		s_dlg_proc_param.fRetry = NULL;
 
 
-	} while (
-	    (IDOK
-	     == DialogBoxIndirectParam(
-	            NULL, dia, NULL, PasswordDlgProc, (LPARAM)&s_dlg_proc_param))
-	    && (!g_exit_flag));
+	} while ((IDOK
+	          == DialogBoxIndirectParam(
+	              NULL, dia, NULL, PasswordDlgProc, (LPARAM)&s_dlg_proc_param))
+	         && (!g_exit_flag));
 
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
 	s_dlg_proc_param.guard = 0;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 }
 
 
 
 
@@ -2564,10 +2595,12 @@ sysinfo_reload(struct dlg_proc_param *prm)
 int
 int
 show_system_info()
 show_system_info()
 {
 {
-#define HEIGHT (15)
-#define WIDTH (320)
-#define LABEL_WIDTH (50)
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short WIDTH = 320;
+	short LABEL_WIDTH = 50;
 
 
+	/* Other parameters */
 	unsigned char mem[4096], *p;
 	unsigned char mem[4096], *p;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	int ok;
 	int ok;
@@ -2648,16 +2681,12 @@ show_system_info()
 
 
 	ok = (IDOK
 	ok = (IDOK
 	      == DialogBoxIndirectParam(
 	      == DialogBoxIndirectParam(
-	             NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
+	          NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
 
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
 	s_dlg_proc_param.guard = 0;
 
 
 	return ok;
 	return ok;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 }
 
 
 
 
@@ -2731,7 +2760,6 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 	char buf[200];
 	char buf[200];
 	POINT pt;
 	POINT pt;
 	HMENU hMenu;
 	HMENU hMenu;
-	static UINT s_uTaskbarRestart; /* for taskbar creation */
 
 
 	switch (msg) {
 	switch (msg) {
 
 
@@ -2754,7 +2782,6 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 			exit(EXIT_SUCCESS);
 			exit(EXIT_SUCCESS);
 		} else {
 		} else {
 			start_civetweb(__argc, __argv);
 			start_civetweb(__argc, __argv);
-			s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
 		}
 		}
 		break;
 		break;
 
 
@@ -2762,7 +2789,9 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 		switch (LOWORD(wParam)) {
 		switch (LOWORD(wParam)) {
 		case ID_QUIT:
 		case ID_QUIT:
 			stop_civetweb();
 			stop_civetweb();
-			Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+			if (TrayIcon.cbSize) {
+				Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+			}
 			g_exit_flag = 1;
 			g_exit_flag = 1;
 			PostQuitMessage(0);
 			PostQuitMessage(0);
 			return 0;
 			return 0;
@@ -2840,14 +2869,12 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
 
 	case WM_CLOSE:
 	case WM_CLOSE:
 		stop_civetweb();
 		stop_civetweb();
-		Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+		if (TrayIcon.cbSize) {
+			Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+		}
 		g_exit_flag = 1;
 		g_exit_flag = 1;
 		PostQuitMessage(0);
 		PostQuitMessage(0);
 		return 0; /* We've just sent our own quit message, with proper hwnd. */
 		return 0; /* We've just sent our own quit message, with proper hwnd. */
-
-	default:
-		if (msg == s_uTaskbarRestart)
-			Shell_NotifyIcon(NIM_ADD, &TrayIcon);
 	}
 	}
 
 
 	return DefWindowProc(hWnd, msg, wParam, lParam);
 	return DefWindowProc(hWnd, msg, wParam, lParam);
@@ -2933,6 +2960,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show)
 	                    NULL);
 	                    NULL);
 	ShowWindow(hWnd, SW_HIDE);
 	ShowWindow(hWnd, SW_HIDE);
 
 
+	/* Load icon for systray and other dialogs */
 	if (g_icon_name) {
 	if (g_icon_name) {
 		hIcon = (HICON)
 		hIcon = (HICON)
 		    LoadImage(NULL, g_icon_name, IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
 		    LoadImage(NULL, g_icon_name, IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
@@ -2945,15 +2973,21 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show)
 		                         0);
 		                         0);
 	}
 	}
 
 
-	TrayIcon.cbSize = sizeof(TrayIcon);
-	TrayIcon.uID = ID_ICON;
-	TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
-	TrayIcon.hIcon = hIcon;
-	TrayIcon.hWnd = hWnd;
-	snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", g_server_name);
-	TrayIcon.uCallbackMessage = WM_USER;
-	Shell_NotifyIcon(NIM_ADD, &TrayIcon);
+	/* add icon to systray; tray icon is entry point to the menu */
+	if (!g_hide_tray) {
+		TrayIcon.cbSize = sizeof(TrayIcon);
+		TrayIcon.uID = ID_ICON;
+		TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+		TrayIcon.hIcon = hIcon;
+		TrayIcon.hWnd = hWnd;
+		snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", g_server_name);
+		TrayIcon.uCallbackMessage = WM_USER;
+		Shell_NotifyIcon(NIM_ADD, &TrayIcon);
+	} else {
+		TrayIcon.cbSize = 0;
+	}
 
 
+	/* Message loop */
 	while (GetMessage(&msg, hWnd, 0, 0) > 0) {
 	while (GetMessage(&msg, hWnd, 0, 0) > 0) {
 		TranslateMessage(&msg);
 		TranslateMessage(&msg);
 		DispatchMessage(&msg);
 		DispatchMessage(&msg);
@@ -2985,13 +3019,15 @@ main(int argc, char *argv[])
 @end
 @end
 
 
 @implementation Civetweb
 @implementation Civetweb
-- (void)openBrowser {
+- (void)openBrowser
+{
 	[[NSWorkspace sharedWorkspace]
 	[[NSWorkspace sharedWorkspace]
 	    openURL:[NSURL URLWithString:[NSString stringWithUTF8String:
 	    openURL:[NSURL URLWithString:[NSString stringWithUTF8String:
 	                                               get_url_to_first_open_port(
 	                                               get_url_to_first_open_port(
 	                                                   g_ctx)]]];
 	                                                   g_ctx)]]];
 }
 }
-- (void)editConfig {
+- (void)editConfig
+{
 	create_config_file(g_ctx, g_config_file_name);
 	create_config_file(g_ctx, g_config_file_name);
 	NSString *path = [NSString stringWithUTF8String:g_config_file_name];
 	NSString *path = [NSString stringWithUTF8String:g_config_file_name];
 	if (![[NSWorkspace sharedWorkspace] openFile:path
 	if (![[NSWorkspace sharedWorkspace] openFile:path
@@ -3004,7 +3040,8 @@ main(int argc, char *argv[])
 		(void)[alert runModal];
 		(void)[alert runModal];
 	}
 	}
 }
 }
-- (void)shutDown {
+- (void)shutDown
+{
 	[NSApp terminate:nil];
 	[NSApp terminate:nil];
 }
 }
 @end
 @end

+ 68 - 0
src/mod_lua.inl

@@ -1588,6 +1588,67 @@ lsp_get_option(lua_State *L)
 	return luaL_error(L, "invalid get_option() call");
 	return luaL_error(L, "invalid get_option() call");
 }
 }
 
 
+static int s_lua_traceLevel = 1;
+static FILE *s_lua_traceFile = NULL;
+static pthread_mutex_t s_lua_traceMutex;
+
+
+/* mg.trace */
+static int
+lsp_trace(lua_State *L)
+{
+	int num_args = lua_gettop(L);
+	int arg_type[8];
+	int trace_level = 0;
+	int firstarg = 1;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		if (num_args >= (i + 1)) {
+			arg_type[i] = lua_type(L, (i + 1));
+		} else {
+			arg_type[i] = LUA_TNIL;
+		}
+	}
+
+	if (arg_type[0] == LUA_TNUMBER) {
+		trace_level = lua_tointeger(L, 1);
+		if (num_args == 1) {
+			/* Set a new trace level, return the current one. */
+			lua_pushinteger(L, s_lua_traceLevel);
+			s_lua_traceLevel = trace_level;
+			if (s_lua_traceFile) {
+				pthread_mutex_lock(&s_lua_traceMutex);
+				fflush(s_lua_traceFile);
+				pthread_mutex_unlock(&s_lua_traceMutex);
+			}
+			return 1;
+		}
+		firstarg = 2;
+	}
+
+	if (trace_level > s_lua_traceLevel) {
+		/* If this trace request has a higher trace level than the global trace
+		 * level, do not trace. */
+		lua_pushboolean(L, 0);
+		return 1;
+	}
+
+	/* Print to file */
+	if (s_lua_traceFile) {
+		pthread_mutex_lock(&s_lua_traceMutex);
+		for (i = firstarg; i <= num_args; i++) {
+			if (arg_type[i - 1] == LUA_TSTRING) {
+				const char *arg = lua_tostring(L, i);
+				fprintf(s_lua_traceFile, "%s\n", arg);
+			}
+		}
+		pthread_mutex_unlock(&s_lua_traceMutex);
+	}
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
 
 
 /* UUID library and function pointer */
 /* UUID library and function pointer */
 union {
 union {
@@ -2232,6 +2293,7 @@ prepare_lua_environment(struct mg_context *ctx,
 	reg_function(L, "get_response_code_text", lsp_get_response_code_text);
 	reg_function(L, "get_response_code_text", lsp_get_response_code_text);
 	reg_function(L, "random", lsp_random);
 	reg_function(L, "random", lsp_random);
 	reg_function(L, "get_info", lsp_get_info);
 	reg_function(L, "get_info", lsp_get_info);
+	reg_function(L, "trace", lsp_trace);
 
 
 	if (pf_uuid_generate.f) {
 	if (pf_uuid_generate.f) {
 		reg_function(L, "uuid", lsp_uuid);
 		reg_function(L, "uuid", lsp_uuid);
@@ -2875,6 +2937,9 @@ static void *lib_handle_uuid = NULL;
 static void
 static void
 lua_init_optional_libraries(void)
 lua_init_optional_libraries(void)
 {
 {
+	/* Create logging mutex */
+	pthread_mutex_init(&s_lua_traceMutex, &pthread_mutex_attr);
+
 	/* shared Lua state */
 	/* shared Lua state */
 	lua_shared_init();
 	lua_shared_init();
 
 
@@ -2903,6 +2968,9 @@ lua_exit_optional_libraries(void)
 
 
 	/* shared Lua state */
 	/* shared Lua state */
 	lua_shared_exit();
 	lua_shared_exit();
+
+	/* Delete logging mutex */
+	pthread_mutex_destroy(&s_lua_traceMutex);
 }
 }
 
 
 
 

+ 2 - 0
test/nonlatin/𞤀𞤣𞤤𞤢𞤥 𞤆𞤵𞤤𞤢𞤪.txt

@@ -0,0 +1,2 @@
+𞤀𞤣𞤤𞤢𞤥 𞤆𞤵𞤤𞤢𞤪
+https://en.wikipedia.org/wiki/Fula_language#Adlam_script

+ 70 - 37
unittest/private.c

@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018 the Civetweb developers
+/* Copyright (c) 2015-2019 the Civetweb developers
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * of this software and associated documentation files (the "Software"), to deal
@@ -19,23 +19,25 @@
  * THE SOFTWARE.
  * THE SOFTWARE.
  */
  */
 
 
-/**
- * We include the source file so that we have access to the internal private
- * static functions
+/**********************************************************************************/
+/*
+ * We include the civetweb.c source file so that we have access to the internal
+ * private static functions
  */
  */
+
 #ifdef _MSC_VER
 #ifdef _MSC_VER
-#ifndef _CRT_SECURE_NO_WARNINGS
-#define _CRT_SECURE_NO_WARNINGS
-#endif
+/* Since the C file is included, declare all API functions as static,
+ * to avoid linker errors in the test (seems to be required for MS
+ * toolchain only).
+ */
 #define CIVETWEB_API static
 #define CIVETWEB_API static
 #endif
 #endif
 
 
-#ifdef REPLACE_CHECK_FOR_LOCAL_DEBUGGING
-#undef MEMORY_DEBUGGING
-#endif
-
 #include "../src/civetweb.c"
 #include "../src/civetweb.c"
+/**********************************************************************************/
+
 
 
+/* Standard includes for the test code below */
 #include <stdlib.h>
 #include <stdlib.h>
 #include <time.h>
 #include <time.h>
 
 
@@ -93,7 +95,6 @@ START_TEST(test_parse_http_message)
 	char req12[] =
 	char req12[] =
 	    "POST /a/b/c.d?e=f&g HTTP/1.1\r\nKey1: val1\r\nKey2: val2\r\n\r\nBODY";
 	    "POST /a/b/c.d?e=f&g HTTP/1.1\r\nKey1: val1\r\nKey2: val2\r\n\r\nBODY";
 
 
-
 	int lenreq1 = (int)strlen(req1);
 	int lenreq1 = (int)strlen(req1);
 	int lenreq2 = (int)strlen(req2);
 	int lenreq2 = (int)strlen(req2);
 	int lenreq3 = (int)strlen(req3);
 	int lenreq3 = (int)strlen(req3);
@@ -323,33 +324,66 @@ START_TEST(test_match_prefix)
 END_TEST
 END_TEST
 
 
 
 
-START_TEST(test_remove_double_dots_and_double_slashes)
+START_TEST(test_remove_dot_segments)
 {
 {
-	/* Adapted from unit_test.c */
-	/* Copyright (c) 2013-2015 the Civetweb developers */
-	/* Copyright (c) 2004-2013 Sergey Lyubka */
+	int i;
+
 	struct {
 	struct {
-		char before[20], after[20];
-	} data[] = {
-	    {"////a", "/a"},
-	    {"/.....", "/."},
-	    {"/......", "/"},
-	    {"..", "."},
-	    {"...", "."},
-	    {"/...///", "/./"},
-	    {"/a...///", "/a.../"},
-	    {"/.x", "/.x"},
-	    {"/\\", "/"},
-	    {"/a\\", "/a\\"},
-	    {"/a\\\\...", "/a\\."},
-	};
-	size_t i;
+		const char *input;
+		const char *expected_output;
+	} tests[] = {{"/path/to/file.ext", "/path/to/file.ext"},
+	             {"/file.ext", "/file.ext"},
+	             {"/path/../file.ext", "/file.ext"},
+	             {"/../to/file.ext", "/to/file.ext"},
+	             {"/../../file.ext", "/file.ext"},
+	             {"/./../file.ext", "/file.ext"},
+	             {"/.././file.ext", "/file.ext"},
+	             {"/././file.ext", "/file.ext"},
+	             {"/././file.ext", "/file.ext"},
+	             {"/path/.to/..file.ext", "/path/.to/..file.ext"},
+	             {"/file", "/file"},
+	             {"/path/", "/path/"},
+
+	             {"file.ext", "file.ext"},
+	             {"./file.ext", "file.ext"},
+	             {"../file.ext", "file.ext"},
+	             {".file.ext", ".file.ext"},
+	             {"..file.ext", "..file.ext"},
+	             {"file", "file"},
+	             {"/x/../", "/"},
+	             {"/x/../../", "/"},
+	             {"/x/.././", "/"},
+	             {"/./x/.././", "/"},
+
+	             /* Windows specific */
+	             {"\\file.ext", "/file.ext"},
+	             {"\\..\\file.ext", "/file.ext"},
+	             {"/file.", "/file"},
+	             {"/path\\to.\\.\\file.", "/path/to/file"},
+
+	             /* Multiple dots and slashes */
+	             {"\\//\\\\x", "/x"},
+	             {"//", "/"},
+	             {"/./", "/"},
+	             {"/../", "/"},
+	             {"/.../", "/"},
+	             {"/..../", "/"},
+	             {"/...../", "/"},
+	             {"/...../", "/"},
+	             {"/...//", "/"},
+	             {"/..././", "/"},
+	             {"/.../../", "/"},
+	             {"/.../.../", "/"},
+
+	             {NULL, NULL}};
 
 
 	mark_point();
 	mark_point();
 
 
-	for (i = 0; i < ARRAY_SIZE(data); i++) {
-		remove_double_dots_and_double_slashes(data[i].before);
-		ck_assert_str_eq(data[i].before, data[i].after);
+	for (i = 0; (tests[i].input != NULL); i++) {
+		char inout[256];
+		strcpy(inout, tests[i].input);
+		remove_dot_segments(inout);
+		ck_assert_str_eq(inout, tests[i].expected_output);
 	}
 	}
 }
 }
 END_TEST
 END_TEST
@@ -1106,8 +1140,7 @@ make_private_suite(void)
 	tcase_set_timeout(tcase_url_parsing_1, civetweb_min_test_timeout);
 	tcase_set_timeout(tcase_url_parsing_1, civetweb_min_test_timeout);
 	suite_add_tcase(suite, tcase_url_parsing_1);
 	suite_add_tcase(suite, tcase_url_parsing_1);
 
 
-	tcase_add_test(tcase_url_parsing_2,
-	               test_remove_double_dots_and_double_slashes);
+	tcase_add_test(tcase_url_parsing_2, test_remove_dot_segments);
 	tcase_set_timeout(tcase_url_parsing_2, civetweb_min_test_timeout);
 	tcase_set_timeout(tcase_url_parsing_2, civetweb_min_test_timeout);
 	suite_add_tcase(suite, tcase_url_parsing_2);
 	suite_add_tcase(suite, tcase_url_parsing_2);
 
 
@@ -1178,7 +1211,7 @@ MAIN_PRIVATE(void)
 
 
 	test_alloc_vprintf(0);
 	test_alloc_vprintf(0);
 	test_mg_vsnprintf(0);
 	test_mg_vsnprintf(0);
-	test_remove_double_dots_and_double_slashes(0);
+	test_remove_dot_segments(0);
 	test_parse_date_string(0);
 	test_parse_date_string(0);
 	test_parse_port_string(0);
 	test_parse_port_string(0);
 	test_parse_http_message(0);
 	test_parse_http_message(0);

+ 17 - 9
unittest/public_server.c

@@ -151,8 +151,6 @@ START_TEST(test_the_test_environment)
 	char wd[300];
 	char wd[300];
 	char buf[500];
 	char buf[500];
 	FILE *f;
 	FILE *f;
-	struct stat st;
-	int ret;
 	const char *ssl_cert = locate_ssl_cert();
 	const char *ssl_cert = locate_ssl_cert();
 
 
 	memset(wd, 0, sizeof(wd));
 	memset(wd, 0, sizeof(wd));
@@ -832,16 +830,18 @@ request_test_handler(struct mg_connection *conn, void *cbdata)
 	char chunk_data[32];
 	char chunk_data[32];
 	const struct mg_request_info *ri;
 	const struct mg_request_info *ri;
 	struct mg_context *ctx;
 	struct mg_context *ctx;
-	void *ud, *cud;
+	void *ud, *cud, *ud2;
 	void *dummy = malloc(1);
 	void *dummy = malloc(1);
 
 
 	ctx = mg_get_context(conn);
 	ctx = mg_get_context(conn);
 	ud = mg_get_user_data(ctx);
 	ud = mg_get_user_data(ctx);
+	ud2 = mg_get_user_context_data(conn);
 	ri = mg_get_request_info(conn);
 	ri = mg_get_request_info(conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert(ctx == g_ctx);
 	ck_assert(ctx == g_ctx);
 	ck_assert(ud == &g_ctx);
 	ck_assert(ud == &g_ctx);
+	ck_assert(ud == ud2);
 
 
 	ck_assert(dummy != NULL);
 	ck_assert(dummy != NULL);
 
 
@@ -884,15 +884,17 @@ request_test_handler2(struct mg_connection *conn, void *cbdata)
 	const char *chunk_data = "123456789A123456789B123456789C";
 	const char *chunk_data = "123456789A123456789B123456789C";
 	const struct mg_request_info *ri;
 	const struct mg_request_info *ri;
 	struct mg_context *ctx;
 	struct mg_context *ctx;
-	void *ud;
+	void *ud, *ud2;
 
 
 	ctx = mg_get_context(conn);
 	ctx = mg_get_context(conn);
 	ud = mg_get_user_data(ctx);
 	ud = mg_get_user_data(ctx);
+	ud2 = mg_get_user_context_data(conn);
 	ri = mg_get_request_info(conn);
 	ri = mg_get_request_info(conn);
 
 
 	ck_assert(ri != NULL);
 	ck_assert(ri != NULL);
 	ck_assert(ctx == g_ctx);
 	ck_assert(ctx == g_ctx);
 	ck_assert(ud == &g_ctx);
 	ck_assert(ud == &g_ctx);
+	ck_assert(ud == ud2);
 
 
 	mg_send_http_ok(conn, "text/plain", -1);
 	mg_send_http_ok(conn, "text/plain", -1);
 
 
@@ -4652,7 +4654,7 @@ minimal_http_https_client_impl(const char *server,
 		             client_err_buf);
 		             client_err_buf);
 	}
 	}
 
 
-	mg_printf(client, "GET /%s HTTP/1.0\r\n\r\n", uri);
+	mg_printf(client, "GET %s HTTP/1.0\r\n\r\n", uri);
 
 
 	r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);
 	r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);
 
 
@@ -4669,8 +4671,14 @@ minimal_http_https_client_impl(const char *server,
 	ck_assert(client_ri != NULL);
 	ck_assert(client_ri != NULL);
 
 
 	/* Check for status code 200 OK or 30? moved */
 	/* Check for status code 200 OK or 30? moved */
-	if ((client_ri->status_code < 300) || (client_ri->status_code > 308)) {
-		ck_assert_int_eq(client_ri->status_code, 200);
+	if ((client_ri->status_code != 200)
+	    && (client_ri->status_code / 10 != 30)) {
+		ck_abort_msg("Request to %s://%s:%u/%s: Status %u",
+		             use_ssl ? "HTTPS" : "HTTP",
+		             server,
+		             port,
+		             uri,
+		             client_ri->status_code);
 	}
 	}
 
 
 	data_read = 0;
 	data_read = 0;
@@ -4721,7 +4729,7 @@ START_TEST(test_minimal_client)
 	/* Call a test client */
 	/* Call a test client */
 	minimal_http_client_impl("192.30.253.113" /* www.github.com */,
 	minimal_http_client_impl("192.30.253.113" /* www.github.com */,
 	                         80,
 	                         80,
-	                         "civetweb/civetweb/");
+	                         "/civetweb/civetweb/");
 
 
 	mark_point();
 	mark_point();
 
 
@@ -4750,7 +4758,7 @@ START_TEST(test_minimal_tls_client)
 	/* Call a test client */
 	/* Call a test client */
 	minimal_https_client_impl("192.30.253.113" /* www.github.com */,
 	minimal_https_client_impl("192.30.253.113" /* www.github.com */,
 	                          443,
 	                          443,
-	                          "civetweb/civetweb/");
+	                          "/civetweb/civetweb/");
 
 
 	mark_point();
 	mark_point();
 
 

Some files were not shown because too many files changed in this diff