You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2018/07/03 20:45:56 UTC

[4/6] celix git commit: CELIX-451: Adds celix_bundle_dir for copying files. Also remove mongoose example and adds a new civetweb example.

http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/civetweb.h
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/civetweb.h b/examples/celix-examples/civetweb/civetweb/civetweb.h
new file mode 100644
index 0000000..5ecc75b
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/civetweb.h
@@ -0,0 +1,1498 @@
+/* Copyright (c) 2013-2017 the Civetweb developers
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef CIVETWEB_HEADER_INCLUDED
+#define CIVETWEB_HEADER_INCLUDED
+
+#define CIVETWEB_VERSION "1.11"
+#define CIVETWEB_VERSION_MAJOR (1)
+#define CIVETWEB_VERSION_MINOR (11)
+#define CIVETWEB_VERSION_PATCH (0)
+
+#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
+#elif __GNUC__ >= 4
+#define CIVETWEB_API __attribute__((visibility("default")))
+#else
+#define CIVETWEB_API
+#endif
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* Init Features */
+enum {
+    MG_FEATURES_DEFAULT = 0x0u,
+
+    /* Support files from local directories */
+    /* Will only work, if NO_FILES is not set. */
+            MG_FEATURES_FILES = 0x1u,
+
+    /* Support transport layer security (TLS). */
+    /* SSL is still often used synonymously for TLS. */
+    /* Will only work, if NO_SSL is not set. */
+            MG_FEATURES_TLS = 0x2u,
+    MG_FEATURES_SSL = 0x2u,
+
+    /* Support common gateway interface (CGI). */
+    /* Will only work, if NO_CGI is not set. */
+            MG_FEATURES_CGI = 0x4u,
+
+    /* Support IPv6. */
+    /* Will only work, if USE_IPV6 is set. */
+            MG_FEATURES_IPV6 = 0x8u,
+
+    /* Support WebSocket protocol. */
+    /* Will only work, if USE_WEBSOCKET is set. */
+            MG_FEATURES_WEBSOCKET = 0x10u,
+
+    /* Support server side Lua scripting. */
+    /* Will only work, if USE_LUA is set. */
+            MG_FEATURES_LUA = 0x20u,
+
+    /* Support server side JavaScript scripting. */
+    /* Will only work, if USE_DUKTAPE is set. */
+            MG_FEATURES_SSJS = 0x40u,
+
+    /* Provide data required for caching files. */
+    /* Will only work, if NO_CACHING is not set. */
+            MG_FEATURES_CACHE = 0x80u,
+
+    /* Collect server status information. */
+    /* Will only work, if USE_SERVER_STATS is set. */
+            MG_FEATURES_STATS = 0x100u,
+
+    /* Support on-the-fly compression. */
+    /* Will only work, if USE_ZLIB is set. */
+            MG_FEATURES_COMPRESSION = 0x200u,
+
+    /* Collect server status information. */
+    /* Will only work, if USE_SERVER_STATS is set. */
+            MG_FEATURES_ALL = 0xFFFFu
+};
+
+
+/* Initialize this library. This should be called once before any other
+ * function from this library. This function is not guaranteed to be
+ * thread safe.
+ * Parameters:
+ *   features: bit mask for features to be initialized.
+ *             Note: The TLS libraries (like OpenSSL) is initialized
+ *                   only if the MG_FEATURES_TLS bit is set.
+ *                   Currently the other bits do not influence
+ *                   initialization, but this may change in future
+ *                   versions.
+ * Return value:
+ *   initialized features
+ *   0: error
+ */
+CIVETWEB_API unsigned mg_init_library(unsigned features);
+
+
+/* Un-initialize this library.
+ * Return value:
+ *   0: error
+ */
+CIVETWEB_API unsigned mg_exit_library(void);
+
+
+struct mg_context;    /* Handle for the HTTP service itself */
+struct mg_connection; /* Handle for the individual connection */
+
+
+/* Maximum number of headers */
+#define MG_MAX_HEADERS (64)
+
+struct mg_header {
+    const char *name;  /* HTTP header name */
+    const char *value; /* HTTP header value */
+};
+
+
+/* This structure contains information about the HTTP request. */
+struct mg_request_info {
+    const char *request_method;  /* "GET", "POST", etc */
+    const char *request_uri;     /* URL-decoded URI (absolute or relative,
+	                              * as in the request) */
+    const char *local_uri;       /* URL-decoded URI (relative). Can be NULL
+	                              * if the request_uri does not address a
+	                              * resource at the server host. */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-02-04, deprecated 2014-09-14 */
+    const char *uri;             /* Deprecated: use local_uri instead */
+#endif
+    const char *http_version; /* E.g. "1.0", "1.1" */
+    const char *query_string; /* URL part after '?', not including '?', or
+	                             NULL */
+    const char *remote_user;  /* Authenticated user, or NULL if no auth
+	                             used */
+    char remote_addr[48];     /* Client's IP address as a string. */
+
+    long long content_length; /* Length (in bytes) of the request body,
+	                             can be -1 if no length was given. */
+    int remote_port;          /* Client's port */
+    int is_ssl;               /* 1 if SSL-ed, 0 if not */
+    void *user_data;          /* User data pointer passed to mg_start() */
+    void *conn_data;          /* Connection-specific user data */
+
+    int num_headers; /* Number of HTTP headers */
+    struct mg_header
+            http_headers[MG_MAX_HEADERS]; /* Allocate maximum headers */
+
+    struct mg_client_cert *client_cert; /* Client certificate information */
+
+    const char *acceptedWebSocketSubprotocol; /* websocket subprotocol,
+	                                           * accepted during handshake */
+};
+
+
+/* This structure contains information about the HTTP request. */
+/* This structure may be extended in future versions. */
+struct mg_response_info {
+    int status_code;          /* E.g. 200 */
+    const char *status_text;  /* E.g. "OK" */
+    const char *http_version; /* E.g. "1.0", "1.1" */
+
+    long long content_length; /* Length (in bytes) of the request body,
+	                             can be -1 if no length was given. */
+
+    int num_headers; /* Number of HTTP headers */
+    struct mg_header
+            http_headers[MG_MAX_HEADERS]; /* Allocate maximum headers */
+};
+
+
+/* Client certificate information (part of mg_request_info) */
+/* New nomenclature. */
+struct mg_client_cert {
+    void *peer_cert;
+    const char *subject;
+    const char *issuer;
+    const char *serial;
+    const char *finger;
+};
+
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+/* Old nomenclature. */
+struct client_cert {
+	const char *subject;
+	const char *issuer;
+	const char *serial;
+	const char *finger;
+};
+#endif
+
+
+/* This structure needs to be passed to mg_start(), to let civetweb know
+   which callbacks to invoke. For a detailed description, see
+   https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md */
+struct mg_callbacks {
+    /* Called when civetweb has received new HTTP request.
+       If the callback returns one, it must process the request
+       by sending valid HTTP headers and a body. Civetweb will not do
+       any further processing. Otherwise it must return zero.
+       Note that since V1.7 the "begin_request" function is called
+       before an authorization check. If an authorization check is
+       required, use a request_handler instead.
+       Return value:
+         0: civetweb will process the request itself. In this case,
+            the callback must not send any data to the client.
+         1-999: callback already processed the request. Civetweb will
+                not send any data after the callback returned. The
+                return code is stored as a HTTP status code for the
+                access log. */
+    int (*begin_request)(struct mg_connection *);
+
+    /* Called when civetweb has finished processing request. */
+    void (*end_request)(const struct mg_connection *, int reply_status_code);
+
+    /* Called when civetweb is about to log a message. If callback returns
+       non-zero, civetweb does not log anything. */
+    int (*log_message)(const struct mg_connection *, const char *message);
+
+    /* Called when civetweb is about to log access. If callback returns
+       non-zero, civetweb does not log anything. */
+    int (*log_access)(const struct mg_connection *, const char *message);
+
+    /* Called when civetweb initializes SSL library.
+       Parameters:
+         user_data: parameter user_data passed when starting the server.
+       Return value:
+         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)(void *ssl_context, 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);
+
+#if defined(MG_LEGACY_INTERFACE) /* 2015-08-19 */
+    /* Called when websocket request is received, before websocket handshake.
+	   Return value:
+	     0: civetweb proceeds with websocket handshake.
+	     1: connection is closed immediately.
+	   This callback is deprecated: Use mg_set_websocket_handler instead. */
+	int (*websocket_connect)(const struct mg_connection *);
+
+	/* Called when websocket handshake is successfully completed, and
+	   connection is ready for data exchange.
+	   This callback is deprecated: Use mg_set_websocket_handler instead. */
+	void (*websocket_ready)(struct mg_connection *);
+
+	/* Called when data frame has been received from the client.
+	   Parameters:
+	     bits: first byte of the websocket frame, see websocket RFC at
+	           http://tools.ietf.org/html/rfc6455, section 5.2
+	     data, data_len: payload, with mask (if any) already applied.
+	   Return value:
+	     1: keep this websocket connection open.
+	     0: close this websocket connection.
+	   This callback is deprecated: Use mg_set_websocket_handler instead. */
+	int (*websocket_data)(struct mg_connection *,
+	                      int bits,
+	                      char *data,
+	                      size_t data_len);
+#endif /* MG_LEGACY_INTERFACE */
+
+    /* Called when civetweb is closing a connection.  The per-context mutex is
+       locked when this is invoked.
+
+       Websockets:
+       Before mg_set_websocket_handler has been added, it was primarily useful
+       for noting when a websocket is closing, and used to remove it from any
+       application-maintained list of clients.
+       Using this callback for websocket connections is deprecated: Use
+       mg_set_websocket_handler instead.
+
+       Connection specific data:
+       If memory has been allocated for the connection specific user data
+       (mg_request_info->conn_data, mg_get_user_connection_data),
+       this is the last chance to free it.
+    */
+    void (*connection_close)(const struct mg_connection *);
+
+    /* Called when civetweb is about to serve Lua server page, if
+       Lua support is enabled.
+       Parameters:
+         conn: current connection.
+         lua_context: "lua_State *" pointer. */
+    void (*init_lua)(const struct mg_connection *conn, void *lua_context);
+
+#if defined(MG_LEGACY_INTERFACE) /* 2016-05-14 */
+    /* Called when civetweb has uploaded a file to a temporary directory as a
+	   result of mg_upload() call.
+	   Note that mg_upload is deprecated. Use mg_handle_form_request instead.
+	   Parameters:
+	     file_name: full path name to the uploaded file. */
+	void (*upload)(struct mg_connection *, const char *file_name);
+#endif
+
+    /* Called when civetweb is about to send HTTP error to the client.
+       Implementing this callback allows to create custom error pages.
+       Parameters:
+         conn: current connection.
+         status: HTTP error status code.
+         errmsg: error message text.
+       Return value:
+         1: run civetweb error handler.
+         0: callback already handled the error. */
+    int (*http_error)(struct mg_connection *conn,
+                      int status,
+                      const char *errmsg);
+
+    /* Called after civetweb context has been created, before requests
+       are processed.
+       Parameters:
+         ctx: context handle */
+    void (*init_context)(const struct mg_context *ctx);
+
+    /* Called when a new worker thread is initialized.
+       Parameters:
+         ctx: context handle
+         thread_type:
+           0 indicates the master thread
+           1 indicates a worker thread handling client connections
+           2 indicates an internal helper thread (timer thread)
+           */
+    void (*init_thread)(const struct mg_context *ctx, int thread_type);
+
+    /* Called when civetweb context is deleted.
+       Parameters:
+         ctx: context handle */
+    void (*exit_context)(const struct mg_context *ctx);
+
+    /* Called when initializing a new connection object.
+     * Can be used to initialize the connection specific user data
+     * (mg_request_info->conn_data, mg_get_user_connection_data).
+     * When the callback is called, it is not yet known if a
+     * valid HTTP(S) request will be made.
+     * Parameters:
+     *   conn: not yet fully initialized connection object
+     *   conn_data: output parameter, set to initialize the
+     *              connection specific user data
+     * Return value:
+     *   must be 0
+     *   Otherwise, the result is undefined
+     */
+    int (*init_connection)(const struct mg_connection *conn, void **conn_data);
+};
+
+
+/* Start web server.
+
+   Parameters:
+     callbacks: mg_callbacks structure with user-defined callbacks.
+     options: NULL terminated list of option_name, option_value pairs that
+              specify Civetweb configuration parameters.
+
+   Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
+      processing is required for these, signal handlers must be set up
+      after calling mg_start().
+
+
+   Example:
+     const char *options[] = {
+       "document_root", "/var/www",
+       "listening_ports", "80,443s",
+       NULL
+     };
+     struct mg_context *ctx = mg_start(&my_func, NULL, options);
+
+   Refer to https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md
+   for the list of valid option and their possible values.
+
+   Return:
+     web server context, or NULL on error. */
+CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks,
+                                         void *user_data,
+                                         const char **configuration_options);
+
+
+/* Stop the web server.
+
+   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. */
+CIVETWEB_API void mg_stop(struct mg_context *);
+
+
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+/* Add an additional domain to an already running web server.
+ *
+ * Parameters:
+ *   ctx: Context handle of a server started by mg_start.
+ *   options: NULL terminated list of option_name, option_value pairs that
+ *            specify CivetWeb configuration parameters.
+ *
+ * Return:
+ *   < 0 in case of an error
+ *    -1 for a parameter error
+ *    -2 invalid options
+ *    -3 initializing SSL failed
+ *    -4 mandatory domain option missing
+ *    -5 duplicate domain
+ *    -6 out of memory
+ *   > 0 index / handle of a new domain
+ */
+CIVETWEB_API int mg_start_domain(struct mg_context *ctx,
+                                 const char **configuration_options);
+#endif
+
+
+/* mg_request_handler
+
+   Called when a new request comes in.  This callback is URI based
+   and configured with mg_set_request_handler().
+
+   Parameters:
+      conn: current connection information.
+      cbdata: the callback data configured with mg_set_request_handler().
+   Returns:
+      0: the handler could not handle the request, so fall through.
+      1 - 999: the handler processed the request. The return code is
+               stored as a HTTP status code for the access log. */
+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.
+   This function uses mg_lock_context internally.
+
+   URI's are ordered and prefixed URI's are supported. For example,
+   consider two URIs: /a/b and /a
+           /a   matches /a
+           /a/b matches /a/b
+           /a/c matches /a
+
+   Parameters:
+      ctx: server context
+      uri: the URI (exact or pattern) for the handler
+      handler: the callback handler to use when the URI is requested.
+               If NULL, an already registered handler for this URI will
+               be removed.
+               The URI used to remove a handler must match exactly the
+               one used to register it (not only a pattern match).
+      cbdata: the callback data to give to the handler when it is called. */
+CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx,
+                                         const char *uri,
+                                         mg_request_handler handler,
+                                         void *cbdata);
+
+
+/* Callback types for websocket handlers in C/C++.
+
+   mg_websocket_connect_handler
+       Is called when the client intends to establish a websocket connection,
+       before websocket handshake.
+       Return value:
+         0: civetweb proceeds with websocket handshake.
+         1: connection is closed immediately.
+
+   mg_websocket_ready_handler
+       Is called when websocket handshake is successfully completed, and
+       connection is ready for data exchange.
+
+   mg_websocket_data_handler
+       Is called when a data frame has been received from the client.
+       Parameters:
+         bits: first byte of the websocket frame, see websocket RFC at
+               http://tools.ietf.org/html/rfc6455, section 5.2
+         data, data_len: payload, with mask (if any) already applied.
+       Return value:
+         1: keep this websocket connection open.
+         0: close this websocket connection.
+
+   mg_connection_close_handler
+       Is called, when the connection is closed.*/
+typedef int (*mg_websocket_connect_handler)(const struct mg_connection *,
+                                            void *);
+typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *);
+typedef int (*mg_websocket_data_handler)(struct mg_connection *,
+                                         int,
+                                         char *,
+                                         size_t,
+                                         void *);
+typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
+                                           void *);
+
+/* struct mg_websocket_subprotocols
+ *
+ * List of accepted subprotocols
+ */
+struct mg_websocket_subprotocols {
+    int nb_subprotocols;
+    char **subprotocols;
+};
+
+/* mg_set_websocket_handler
+
+   Set or remove handler functions for websocket connections.
+   This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void
+mg_set_websocket_handler(struct mg_context *ctx,
+                         const char *uri,
+                         mg_websocket_connect_handler connect_handler,
+                         mg_websocket_ready_handler ready_handler,
+                         mg_websocket_data_handler data_handler,
+                         mg_websocket_close_handler close_handler,
+                         void *cbdata);
+
+/* mg_set_websocket_handler
+
+   Set or remove handler functions for websocket connections.
+   This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void mg_set_websocket_handler_with_subprotocols(
+        struct mg_context *ctx,
+        const char *uri,
+        struct mg_websocket_subprotocols *subprotocols,
+        mg_websocket_connect_handler connect_handler,
+        mg_websocket_ready_handler ready_handler,
+        mg_websocket_data_handler data_handler,
+        mg_websocket_close_handler close_handler,
+        void *cbdata);
+
+
+/* mg_authorization_handler
+
+   Callback function definition for mg_set_auth_handler
+
+   Parameters:
+      conn: current connection information.
+      cbdata: the callback data configured with mg_set_request_handler().
+   Returns:
+      0: access denied
+      1: access granted
+ */
+typedef int (*mg_authorization_handler)(struct mg_connection *conn,
+                                        void *cbdata);
+
+
+/* mg_set_auth_handler
+
+   Sets or removes a URI mapping for an authorization handler.
+   This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx,
+                                      const char *uri,
+                                      mg_authorization_handler handler,
+                                      void *cbdata);
+
+
+/* Get the value of particular configuration parameter.
+   The value returned is read-only. Civetweb does not allow changing
+   configuration at run time.
+   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. */
+CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx,
+                                       const char *name);
+
+
+/* Get context from connection. */
+CIVETWEB_API struct mg_context *
+mg_get_context(const struct mg_connection *conn);
+
+
+/* Get user data passed to mg_start from context. */
+CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
+
+
+/* Set user data for the current connection. */
+/* Note: This function is deprecated. Use the init_connection callback
+   instead to initialize the user connection data pointer. It is
+   reccomended to supply a pointer to some user defined data structure
+   as conn_data initializer in init_connection. In case it is required
+   to change some data after the init_connection call, store another
+   data pointer in the user defined data structure and modify that
+   pointer. In either case, after the init_connection callback, only
+   calls to mg_get_user_connection_data should be required. */
+CIVETWEB_API void mg_set_user_connection_data(struct mg_connection *conn,
+                                              void *data);
+
+
+/* Get user data set for the current connection. */
+CIVETWEB_API void *
+mg_get_user_connection_data(const struct mg_connection *conn);
+
+
+/* Get a formatted link corresponding to the current request
+
+   Parameters:
+      conn: current connection information.
+      buf: string buffer (out)
+      buflen: length of the string buffer
+   Returns:
+      <0: error
+      >=0: ok */
+CIVETWEB_API int
+mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2014-02-21 */
+/* 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 {
+    const char *name;
+    int type;
+    const char *default_value;
+};
+
+/* Old nomenclature */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+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,
+	CONFIG_TYPE_STRING_LIST = 0x7,
+	CONFIG_TYPE_STRING_MULTILINE = 0x8
+};
+#endif
+
+/* New nomenclature */
+enum {
+    MG_CONFIG_TYPE_UNKNOWN = 0x0,
+    MG_CONFIG_TYPE_NUMBER = 0x1,
+    MG_CONFIG_TYPE_STRING = 0x2,
+    MG_CONFIG_TYPE_FILE = 0x3,
+    MG_CONFIG_TYPE_DIRECTORY = 0x4,
+    MG_CONFIG_TYPE_BOOLEAN = 0x5,
+    MG_CONFIG_TYPE_EXT_PATTERN = 0x6,
+    MG_CONFIG_TYPE_STRING_LIST = 0x7,
+    MG_CONFIG_TYPE_STRING_MULTILINE = 0x8,
+    MG_CONFIG_TYPE_YES_NO_OPTIONAL = 0x9
+};
+
+/* 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);
+
+
+struct mg_server_ports {
+    int protocol;    /* 1 = IPv4, 2 = IPv6, 3 = both */
+    int port;        /* port number */
+    int is_ssl;      /* https port: 0 = no, 1 = yes */
+    int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
+    int _reserved1;
+    int _reserved2;
+    int _reserved3;
+    int _reserved4;
+};
+
+
+/* Get the list of ports that civetweb is listening on.
+   The parameter size is the size of the ports array in elements.
+   The caller is responsibility to allocate the required memory.
+   This function returns the number of struct mg_server_ports elements
+   filled in, or <0 in case of an error. */
+CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx,
+                                     int size,
+                                     struct mg_server_ports *ports);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2017-04-02 */
+/* Deprecated: Use mg_get_server_ports instead. */
+CIVETWEB_API size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl);
+#endif
+
+
+/* Add, edit or delete the entry in the passwords file.
+ *
+ * This function allows an application to manipulate .htpasswd files on the
+ * fly by adding, deleting and changing user records. This is one of the
+ * several ways of implementing authentication on the server side. For another,
+ * cookie-based way please refer to the examples/chat in the source tree.
+ *
+ * Parameter:
+ *   passwords_file_name: Path and name of a file storing multiple passwords
+ *   realm: HTTP authentication realm (authentication domain) name
+ *   user: User name
+ *   password:
+ *     If password is not NULL, entry modified or added.
+ *     If password is NULL, entry is deleted.
+ *
+ *  Return:
+ *    1 on success, 0 on error.
+ */
+CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name,
+                                          const char *realm,
+                                          const char *user,
+                                          const char *password);
+
+
+/* Return information associated with the request.
+ * Use this function to implement a server and get data about a request
+ * from a HTTP/HTTPS client.
+ * Note: Before CivetWeb 1.10, this function could be used to read
+ * a response from a server, when implementing a client, although the
+ * values were never returned in appropriate mg_request_info elements.
+ * It is strongly advised to use mg_get_response_info for clients.
+ */
+CIVETWEB_API const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *);
+
+
+/* Return information associated with a HTTP/HTTPS response.
+ * Use this function in a client, to check the response from
+ * the server. */
+CIVETWEB_API const struct mg_response_info *
+mg_get_response_info(const struct mg_connection *);
+
+
+/* Send data to the client.
+   Return:
+    0   when the connection has been closed
+    -1  on error
+    >0  number of bytes written on success */
+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_connection to ensure that the transmission is not interrupted,
+   i.e., when the application is proactively communicating and responding to
+   a request simultaneously.
+
+   Send data to a websocket client wrapped in a websocket frame.
+   This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+   Return:
+    0   when the connection has been closed
+    -1  on error
+    >0  number of bytes written on success */
+CIVETWEB_API int mg_websocket_write(struct mg_connection *conn,
+                                    int opcode,
+                                    const char *data,
+                                    size_t data_len);
+
+
+/* Send data to a websocket server wrapped in a masked websocket frame.  Uses
+   mg_lock_connection to ensure that the transmission is not interrupted,
+   i.e., when the application is proactively communicating and responding to
+   a request simultaneously.
+
+   Send data to a websocket server wrapped in a masked websocket frame.
+   This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+   Return:
+    0   when the connection has been closed
+    -1  on error
+    >0  number of bytes written on success */
+CIVETWEB_API int mg_websocket_client_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. */
+CIVETWEB_API void mg_lock_connection(struct mg_connection *conn);
+CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2014-06-21 */
+#define mg_lock mg_lock_connection
+#define mg_unlock mg_unlock_connection
+#endif
+
+
+/* Lock server context.  This lock may be used to protect resources
+   that are shared between different connection/worker threads. */
+CIVETWEB_API void mg_lock_context(struct mg_context *ctx);
+CIVETWEB_API void mg_unlock_context(struct mg_context *ctx);
+
+
+/* Opcodes, from http://tools.ietf.org/html/rfc6455 */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+enum {
+	WEBSOCKET_OPCODE_CONTINUATION = 0x0,
+	WEBSOCKET_OPCODE_TEXT = 0x1,
+	WEBSOCKET_OPCODE_BINARY = 0x2,
+	WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
+	WEBSOCKET_OPCODE_PING = 0x9,
+	WEBSOCKET_OPCODE_PONG = 0xa
+};
+#endif
+
+/* New nomenclature */
+enum {
+    MG_WEBSOCKET_OPCODE_CONTINUATION = 0x0,
+    MG_WEBSOCKET_OPCODE_TEXT = 0x1,
+    MG_WEBSOCKET_OPCODE_BINARY = 0x2,
+    MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
+    MG_WEBSOCKET_OPCODE_PING = 0x9,
+    MG_WEBSOCKET_OPCODE_PONG = 0xa
+};
+
+/* Macros for enabling compiler-specific checks for printf-like arguments. */
+#undef PRINTF_FORMAT_STRING
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#include <sal.h>
+#if defined(_MSC_VER) && _MSC_VER > 1400
+#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
+#else
+#define PRINTF_FORMAT_STRING(s) __format_string s
+#endif
+#else
+#define PRINTF_FORMAT_STRING(s) s
+#endif
+
+#ifdef __GNUC__
+#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
+#else
+#define PRINTF_ARGS(x, y)
+#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);
+
+
+/* Send a part of the message body, if chunked transfer encoding is set.
+ * Only use this function after sending a complete HTTP request or response
+ * header with "Transfer-Encoding: chunked" set. */
+CIVETWEB_API int mg_send_chunk(struct mg_connection *conn,
+                               const char *chunk,
+                               unsigned int chunk_len);
+
+
+/* Send contents of the entire file together with HTTP headers.
+ * Parameters:
+ *   conn: Current connection information.
+ *   path: Full path to the file to send.
+ * This function has been superseded by mg_send_mime_file
+ */
+CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path);
+
+
+/* Send contents of the file without HTTP headers.
+ * The code must send a valid HTTP response header before using this function.
+ *
+ * Parameters:
+ *   conn: Current connection information.
+ *   path: Full path to the file to send.
+ *
+ * Return:
+ *   < 0   Error
+*/
+CIVETWEB_API int mg_send_file_body(struct mg_connection *conn,
+                                   const char *path);
+
+
+/* Send HTTP error reply. */
+CIVETWEB_API int mg_send_http_error(struct mg_connection *conn,
+                                    int status_code,
+                                    PRINTF_FORMAT_STRING(const char *fmt),
+                                    ...) PRINTF_ARGS(3, 4);
+
+
+/* Send "HTTP 200 OK" response header.
+ * After calling this function, use mg_write or mg_send_chunk to send the
+ * response body.
+ * Parameters:
+ *   conn: Current connection handle.
+ *   mime_type: Set Content-Type for the following content.
+ *   content_length: Size of the following content, if content_length >= 0.
+ *                   Will set transfer-encoding to chunked, if set to -1.
+ * Return:
+ *   < 0   Error
+ */
+CIVETWEB_API int mg_send_http_ok(struct mg_connection *conn,
+                                 const char *mime_type,
+                                 long long content_length);
+
+
+/* Send "HTTP 30x" redirect response.
+ * The response has content-size zero: do not send any body data after calling
+ * this function.
+ * Parameters:
+ *   conn: Current connection handle.
+ *   target_url: New location.
+ *   redirect_code: HTTP redirect type. Could be 301, 302, 303, 307, 308.
+ * Return:
+ *   < 0   Error (-1 send error, -2 parameter error)
+ */
+CIVETWEB_API int mg_send_http_redirect(struct mg_connection *conn,
+                                       const char *target_url,
+                                       int redirect_code);
+
+
+/* Send HTTP digest access authentication request.
+ * Browsers will send a user name and password in their next request, showing
+ * an authentication dialog if the password is not stored.
+ * Parameters:
+ *   conn: Current connection handle.
+ *   realm: Authentication realm. If NULL is supplied, the sever domain
+ *          set in the authentication_domain configuration is used.
+ * Return:
+ *   < 0   Error
+ */
+CIVETWEB_API int
+mg_send_digest_access_authentication_request(struct mg_connection *conn,
+                                             const char *realm);
+
+
+/* Check if the current request has a valid authentication token set.
+ * A file is used to provide a list of valid user names, realms and
+ * password hashes. The file can be created and modified using the
+ * mg_modify_passwords_file API function.
+ * Parameters:
+ *   conn: Current connection handle.
+ *   realm: Authentication realm. If NULL is supplied, the sever domain
+ *          set in the authentication_domain configuration is used.
+ *   filename: Path and name of a file storing multiple password hashes.
+ * Return:
+ *   > 0   Valid authentication
+ *   0     Invalid authentication
+ *   < 0   Error (all values < 0 should be considered as invalid
+ *         authentication, future error codes will have negative
+ *         numbers)
+ *   -1    Parameter error
+ *   -2    File not found
+ */
+CIVETWEB_API int
+mg_check_digest_access_authentication(struct mg_connection *conn,
+                                      const char *realm,
+                                      const char *filename);
+
+
+/* Send contents of the entire file together with HTTP headers.
+ * Parameters:
+ *   conn: Current connection handle.
+ *   path: Full path to the file to send.
+ *   mime_type: Content-Type for file.  NULL will cause the type to be
+ *              looked up by the file extension.
+ */
+CIVETWEB_API void mg_send_mime_file(struct mg_connection *conn,
+                                    const char *path,
+                                    const char *mime_type);
+
+
+/* Send contents of the entire file together with HTTP headers.
+   Parameters:
+     conn: Current connection information.
+     path: Full path to the file to send.
+     mime_type: Content-Type for file.  NULL will cause the type to be
+                looked up by the file extension.
+     additional_headers: Additional custom header fields appended to the header.
+                         Each header should start with an X-, to ensure it is
+                         not included twice.
+                         NULL does not append anything.
+*/
+CIVETWEB_API void mg_send_mime_file2(struct mg_connection *conn,
+                                     const char *path,
+                                     const char *mime_type,
+                                     const char *additional_headers);
+
+
+/* Store body data into a file. */
+CIVETWEB_API long long mg_store_body(struct mg_connection *conn,
+                                     const char *path);
+/* Read entire request body and store it in a file "path".
+   Return:
+     < 0   Error
+     >= 0  Number of bytes stored in file "path".
+*/
+
+
+/* Read data from the remote end, return number of bytes read.
+   Return:
+     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. */
+CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len);
+
+
+/* Get the value of particular HTTP header.
+
+   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. */
+CIVETWEB_API const char *mg_get_header(const struct mg_connection *,
+                                       const char *name);
+
+
+/* Get a value of particular form variable.
+
+   Parameters:
+     data: pointer to form-uri-encoded buffer. This could be either POST data,
+           or request_info.query_string.
+     data_len: length of the encoded data.
+     var_name: variable name to decode from the buffer
+     dst: destination buffer for the decoded variable
+     dst_len: length of the destination buffer
+
+   Return:
+     On success, length of the decoded variable.
+     On error:
+        -1 (variable not found).
+        -2 (destination buffer is NULL, zero length or too small to hold the
+            decoded variable).
+
+   Destination buffer is guaranteed to be '\0' - terminated if it is not
+   NULL or zero length. */
+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:
+     data: pointer to form-uri-encoded buffer. This could be either POST data,
+           or request_info.query_string.
+     data_len: length of the encoded data.
+     var_name: variable name to decode from the buffer
+     dst: destination buffer for the decoded variable
+     dst_len: length of the destination buffer
+     occurrence: which occurrence of the variable, 0 is the first, 1 the
+                 second...
+                this makes it possible to parse a query like
+                b=x&a=y&a=z which will have occurrence values b:0, a:0 and a:1
+
+   Return:
+     On success, length of the decoded variable.
+     On error:
+        -1 (variable not found).
+        -2 (destination buffer is NULL, zero length or too small to hold the
+            decoded variable).
+
+   Destination buffer is guaranteed to be '\0' - terminated if it is not
+   NULL or zero length. */
+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
+   failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
+   parameter. This function returns only first occurrence.
+
+   Return:
+     On success, value length.
+     On error:
+        -1 (either "Cookie:" header is not present at all or the requested
+            parameter is not found).
+        -2 (destination buffer is NULL, zero length or too small to hold the
+            value). */
+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.
+     host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
+     port: port number, e.g. 80.
+     use_ssl: whether to use SSL connection.
+     error_buffer, error_buffer_size: error message placeholder.
+     request_fmt,...: HTTP request.
+   Return:
+     On success, valid pointer to the new connection, suitable for mg_read().
+     On error, NULL. error_buffer contains error message.
+   Example:
+     char ebuf[100];
+     struct mg_connection *conn;
+     conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
+                        "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
+ */
+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(). */
+CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2016-05-14 */
+/* File upload functionality. Each uploaded file gets saved into a temporary
+   file and MG_UPLOAD event is sent.
+   Return number of uploaded files.
+   Deprecated: Use mg_handle_form_request instead. */
+CIVETWEB_API int mg_upload(struct mg_connection *conn,
+                           const char *destination_dir);
+#endif
+
+
+/* This structure contains callback functions for handling form fields.
+   It is used as an argument to mg_handle_form_request. */
+struct mg_form_data_handler {
+    /* This callback function is called, if a new field has been found.
+     * The return value of this callback is used to define how the field
+     * should be processed.
+     *
+     * Parameters:
+     *   key: Name of the field ("name" property of the HTML input field).
+     *   filename: Name of a file to upload, at the client computer.
+     *             Only set for input fields of type "file", otherwise NULL.
+     *   path: Output parameter: File name (incl. path) to store the file
+     *         at the server computer. Only used if FORM_FIELD_STORAGE_STORE
+     *         is returned by this callback. Existing files will be
+     *         overwritten.
+     *   pathlen: Length of the buffer for path.
+     *   user_data: Value of the member user_data of mg_form_data_handler
+     *
+     * Return value:
+     *   The callback must return the intended storage for this field
+     *   (See FORM_FIELD_STORAGE_*).
+     */
+    int (*field_found)(const char *key,
+                       const char *filename,
+                       char *path,
+                       size_t pathlen,
+                       void *user_data);
+
+    /* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
+     * this callback will receive the field data.
+     *
+     * Parameters:
+     *   key: Name of the field ("name" property of the HTML input field).
+     *   value: Value of the input field.
+     *   user_data: Value of the member user_data of mg_form_data_handler
+     *
+     * Return value:
+     *   TODO: Needs to be defined.
+     */
+    int (*field_get)(const char *key,
+                     const char *value,
+                     size_t valuelen,
+                     void *user_data);
+
+    /* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
+     * the data will be stored into a file. If the file has been written
+     * successfully, this callback will be called. This callback will
+     * not be called for only partially uploaded files. The
+     * mg_handle_form_request function will either store the file completely
+     * and call this callback, or it will remove any partial content and
+     * not call this callback function.
+     *
+     * Parameters:
+     *   path: Path of the file stored at the server.
+     *   file_size: Size of the stored file in bytes.
+     *   user_data: Value of the member user_data of mg_form_data_handler
+     *
+     * Return value:
+     *   TODO: Needs to be defined.
+     */
+    int (*field_store)(const char *path, long long file_size, void *user_data);
+
+    /* User supplied argument, passed to all callback functions. */
+    void *user_data;
+};
+
+
+/* Return values definition for the "field_found" callback in
+ * mg_form_data_handler. */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+enum {
+	/* Skip this field (neither get nor store it). Continue with the
+     * next field. */
+	FORM_FIELD_STORAGE_SKIP = 0x0,
+	/* Get the field value. */
+	FORM_FIELD_STORAGE_GET = 0x1,
+	/* Store the field value into a file. */
+	FORM_FIELD_STORAGE_STORE = 0x2,
+	/* Stop parsing this request. Skip the remaining fields. */
+	FORM_FIELD_STORAGE_ABORT = 0x10
+};
+#endif
+/* New nomenclature */
+enum {
+    /* Skip this field (neither get nor store it). Continue with the
+     * next field. */
+            MG_FORM_FIELD_STORAGE_SKIP = 0x0,
+    /* Get the field value. */
+            MG_FORM_FIELD_STORAGE_GET = 0x1,
+    /* Store the field value into a file. */
+            MG_FORM_FIELD_STORAGE_STORE = 0x2,
+    /* Stop parsing this request. Skip the remaining fields. */
+            MG_FORM_FIELD_STORAGE_ABORT = 0x10
+};
+
+/* Process form data.
+ * Returns the number of fields handled, or < 0 in case of an error.
+ * Note: It is possible that several fields are already handled successfully
+ * (e.g., stored into files), before the request handling is stopped with an
+ * error. In this case a number < 0 is returned as well.
+ * In any case, it is the duty of the caller to remove files once they are
+ * no longer required. */
+CIVETWEB_API int mg_handle_form_request(struct mg_connection *conn,
+                                        struct mg_form_data_handler *fdh);
+
+
+/* Convenience function -- create detached thread.
+   Return: 0 on success, non-0 on error. */
+typedef void *(*mg_thread_func_t)(void *);
+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. */
+CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name);
+
+
+/* Get text representation of HTTP status code. */
+CIVETWEB_API const char *
+mg_get_response_code_text(const struct mg_connection *conn, int response_code);
+
+
+/* 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
+   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. */
+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
+   MD5 hash. Example:
+     char buf[33];
+     mg_md5(buf, "aa", "bb", NULL); */
+CIVETWEB_API char *mg_md5(char buf[33], ...);
+
+
+/* Print error message to the opened error log stream.
+   This utilizes the provided logging configuration.
+     conn: connection (not used for sending data, but to get perameters)
+     fmt: format string without the line return
+     ...: variable argument list
+   Example:
+     mg_cry(conn,"i like %s", "logging"); */
+CIVETWEB_API void mg_cry(const struct mg_connection *conn,
+                         PRINTF_FORMAT_STRING(const char *fmt),
+                         ...) PRINTF_ARGS(2, 3);
+
+
+/* utility methods to compare two buffers, case insensitive. */
+CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2);
+CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
+
+
+/* Connect to a websocket as a client
+   Parameters:
+     host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or
+   "localhost"
+     port: server port
+     use_ssl: make a secure connection to server
+     error_buffer, error_buffer_size: buffer for an error message
+     path: server path you are trying to connect to, i.e. if connection to
+   localhost/app, path should be "/app"
+     origin: value of the Origin HTTP header
+     data_func: callback that should be used when data is received from the
+   server
+     user_data: user supplied argument
+
+   Return:
+     On success, valid mg_connection object.
+     On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *
+mg_connect_websocket_client(const char *host,
+                            int port,
+                            int use_ssl,
+                            char *error_buffer,
+                            size_t error_buffer_size,
+                            const char *path,
+                            const char *origin,
+                            mg_websocket_data_handler data_func,
+                            mg_websocket_close_handler close_func,
+                            void *user_data);
+
+
+/* Connect to a TCP server as a client (can be used to connect to a HTTP server)
+   Parameters:
+     host: host to connect to, i.e. "www.wikipedia.org" or "192.168.1.1" or
+   "localhost"
+     port: server port
+     use_ssl: make a secure connection to server
+     error_buffer, error_buffer_size: buffer for an error message
+
+   Return:
+     On success, valid mg_connection object.
+     On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *mg_connect_client(const char *host,
+                                                     int port,
+                                                     int use_ssl,
+                                                     char *error_buffer,
+                                                     size_t error_buffer_size);
+
+
+struct mg_client_options {
+    const char *host;
+    int port;
+    const char *client_cert;
+    const char *server_cert;
+    /* TODO: add more data */
+};
+
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client_secure(const struct mg_client_options *client_options,
+                         char *error_buffer,
+                         size_t error_buffer_size);
+
+
+enum { TIMEOUT_INFINITE = -1 };
+enum { MG_TIMEOUT_INFINITE = -1 };
+
+/* Wait for a response from the server
+   Parameters:
+     conn: connection
+     ebuf, ebuf_len: error message placeholder.
+     timeout: time to wait for a response in milliseconds (if < 0 then wait
+   forever)
+
+   Return:
+     On success, >= 0
+     On error/timeout, < 0
+*/
+CIVETWEB_API int mg_get_response(struct mg_connection *conn,
+                                 char *ebuf,
+                                 size_t ebuf_len,
+                                 int timeout);
+
+
+/* Check which features where set when the civetweb library has been compiled.
+   The function explicitly addresses compile time defines used when building
+   the library - it does not mean, the feature has been initialized using a
+   mg_init_library call.
+   mg_check_feature can be called anytime, even before mg_init_library has
+   been called.
+
+   Parameters:
+     feature: specifies which feature should be checked
+       The value is a bit mask. The individual bits are defined as:
+         1  serve files (NO_FILES not set)
+         2  support HTTPS (NO_SSL not set)
+         4  support CGI (NO_CGI not set)
+         8  support IPv6 (USE_IPV6 set)
+        16  support WebSocket (USE_WEBSOCKET set)
+        32  support Lua scripts and Lua server pages (USE_LUA is set)
+        64  support server side JavaScript (USE_DUKTAPE is set)
+       128  support caching (NO_CACHING not set)
+       256  support server statistics (USE_SERVER_STATS is set)
+       The result is undefined, if bits are set that do not represent a
+       defined feature (currently: feature >= 512).
+       The result is undefined, if no bit is set (feature == 0).
+
+   Return:
+     If feature is available, the corresponding bit is set
+     If feature is not available, the bit is 0
+*/
+CIVETWEB_API unsigned mg_check_feature(unsigned feature);
+
+
+/* Get information on the system. Useful for support requests.
+   Parameters:
+     buffer: Store system information as string here.
+     buflen: Length of buffer (including a byte required for a terminating 0).
+   Return:
+     Available size of system information, exluding a terminating 0.
+     The information is complete, if the return value is smaller than buflen.
+     The result is a JSON formatted string, the exact content may vary.
+   Note:
+     It is possible to determine the required buflen, by first calling this
+     function with buffer = NULL and buflen = NULL. The required buflen is
+     one byte more than the returned value.
+*/
+CIVETWEB_API int mg_get_system_info(char *buffer, int buflen);
+
+
+/* Get context information. Useful for server diagnosis.
+   Parameters:
+     ctx: Context handle
+     buffer: Store context information here.
+     buflen: Length of buffer (including a byte required for a terminating 0).
+   Return:
+     Available size of system information, exluding a terminating 0.
+     The information is complete, if the return value is smaller than buflen.
+     The result is a JSON formatted string, the exact content may vary.
+     Note:
+     It is possible to determine the required buflen, by first calling this
+     function with buffer = NULL and buflen = NULL. The required buflen is
+     one byte more than the returned value. However, since the available
+     context information changes, you should allocate a few bytes more.
+*/
+CIVETWEB_API int
+mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen);
+
+
+#ifdef MG_EXPERIMENTAL_INTERFACES
+/* Get connection information. Useful for server diagnosis.
+   Parameters:
+     ctx: Context handle
+     idx: Connection index
+     buffer: Store context information here.
+     buflen: Length of buffer (including a byte required for a terminating 0).
+   Return:
+     Available size of system information, exluding a terminating 0.
+     The information is complete, if the return value is smaller than buflen.
+     The result is a JSON formatted string, the exact content may vary.
+   Note:
+     It is possible to determine the required buflen, by first calling this
+     function with buffer = NULL and buflen = NULL. The required buflen is
+     one byte more than the returned value. However, since the available
+     context information changes, you should allocate a few bytes more.
+*/
+CIVETWEB_API int mg_get_connection_info(const struct mg_context *ctx,
+                                        int idx,
+                                        char *buffer,
+                                        int buflen);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CIVETWEB_HEADER_INCLUDED */
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/handle_form.inl
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/handle_form.inl b/examples/celix-examples/civetweb/civetweb/handle_form.inl
new file mode 100644
index 0000000..e5511d5
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/handle_form.inl
@@ -0,0 +1,981 @@
+/* Copyright (c) 2016-2018 the Civetweb developers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+static int
+url_encoded_field_found(const struct mg_connection *conn,
+                        const char *key,
+                        size_t key_len,
+                        const char *filename,
+                        size_t filename_len,
+                        char *path,
+                        size_t path_len,
+                        struct mg_form_data_handler *fdh)
+{
+	char key_dec[1024];
+	char filename_dec[1024];
+	int key_dec_len;
+	int filename_dec_len;
+	int ret;
+
+	key_dec_len =
+	    mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+	if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
+		return MG_FORM_FIELD_STORAGE_SKIP;
+	}
+
+	if (filename) {
+		filename_dec_len = mg_url_decode(filename,
+		                                 (int)filename_len,
+		                                 filename_dec,
+		                                 (int)sizeof(filename_dec),
+		                                 1);
+
+		if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
+		    || (filename_dec_len < 0)) {
+			/* Log error message and skip this field. */
+			mg_cry_internal(conn, "%s: Cannot decode filename", __func__);
+			return MG_FORM_FIELD_STORAGE_SKIP;
+		}
+	} else {
+		filename_dec[0] = 0;
+	}
+
+	ret =
+	    fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
+
+	if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_GET) {
+		if (fdh->field_get == NULL) {
+			mg_cry_internal(conn,
+			                "%s: Function \"Get\" not available",
+			                __func__);
+			return MG_FORM_FIELD_STORAGE_SKIP;
+		}
+	}
+	if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_STORE) {
+		if (fdh->field_store == NULL) {
+			mg_cry_internal(conn,
+			                "%s: Function \"Store\" not available",
+			                __func__);
+			return MG_FORM_FIELD_STORAGE_SKIP;
+		}
+	}
+
+	return ret;
+}
+
+
+static int
+url_encoded_field_get(const struct mg_connection *conn,
+                      const char *key,
+                      size_t key_len,
+                      const char *value,
+                      size_t value_len,
+                      struct mg_form_data_handler *fdh)
+{
+	char key_dec[1024];
+
+	char *value_dec = (char *)mg_malloc_ctx(value_len + 1, conn->phys_ctx);
+	int value_dec_len, ret;
+
+	if (!value_dec) {
+		/* Log error message and stop parsing the form data. */
+		mg_cry_internal(conn,
+		                "%s: Not enough memory (required: %lu)",
+		                __func__,
+		                (unsigned long)(value_len + 1));
+		return MG_FORM_FIELD_STORAGE_ABORT;
+	}
+
+	mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+	value_dec_len =
+	    mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
+
+	ret = fdh->field_get(key_dec,
+	                     value_dec,
+	                     (size_t)value_dec_len,
+	                     fdh->user_data);
+
+	mg_free(value_dec);
+
+	return ret;
+}
+
+
+static int
+unencoded_field_get(const struct mg_connection *conn,
+                    const char *key,
+                    size_t key_len,
+                    const char *value,
+                    size_t value_len,
+                    struct mg_form_data_handler *fdh)
+{
+	char key_dec[1024];
+	(void)conn;
+
+	mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+	return fdh->field_get(key_dec, value, value_len, fdh->user_data);
+}
+
+
+static int
+field_stored(const struct mg_connection *conn,
+             const char *path,
+             long long file_size,
+             struct mg_form_data_handler *fdh)
+{
+	/* Equivalent to "upload" callback of "mg_upload". */
+
+	(void)conn; /* we do not need mg_cry here, so conn is currently unused */
+
+	return fdh->field_store(path, file_size, fdh->user_data);
+}
+
+
+static const char *
+search_boundary(const char *buf,
+                size_t buf_len,
+                const char *boundary,
+                size_t boundary_len)
+{
+	/* We must do a binary search here, not a string search, since the buffer
+	 * may contain '\x00' bytes, if binary data is transferred. */
+	int clen = (int)buf_len - (int)boundary_len - 4;
+	int i;
+
+	for (i = 0; i <= clen; i++) {
+		if (!memcmp(buf + i, "\r\n--", 4)) {
+			if (!memcmp(buf + i + 4, boundary, boundary_len)) {
+				return buf + i;
+			}
+		}
+	}
+	return NULL;
+}
+
+
+int
+mg_handle_form_request(struct mg_connection *conn,
+                       struct mg_form_data_handler *fdh)
+{
+	const char *content_type;
+	char path[512];
+	char buf[1024]; /* Must not be smaller than ~900 - see sanity check */
+	int field_storage;
+	int buf_fill = 0;
+	int r;
+	int field_count = 0;
+	struct mg_file fstore = STRUCT_FILE_INITIALIZER;
+	int64_t file_size = 0; /* init here, to a avoid a false positive
+	                         "uninitialized variable used" warning */
+
+	int has_body_data =
+	    (conn->request_info.content_length > 0) || (conn->is_chunked);
+
+	/* There are three ways to encode data from a HTML form:
+	 * 1) method: GET (default)
+	 *    The form data is in the HTTP query string.
+	 * 2) method: POST, enctype: "application/x-www-form-urlencoded"
+	 *    The form data is in the request body.
+	 *    The body is url encoded (the default encoding for POST).
+	 * 3) method: POST, enctype: "multipart/form-data".
+	 *    The form data is in the request body of a multipart message.
+	 *    This is the typical way to handle file upload from a form.
+	 */
+
+	if (!has_body_data) {
+		const char *data;
+
+		if (0 != strcmp(conn->request_info.request_method, "GET")) {
+			/* No body data, but not a GET request.
+			 * This is not a valid form request. */
+			return -1;
+		}
+
+		/* GET request: form data is in the query string. */
+		/* The entire data has already been loaded, so there is no nead to
+		 * call mg_read. We just need to split the query string into key-value
+		 * pairs. */
+		data = conn->request_info.query_string;
+		if (!data) {
+			/* No query string. */
+			return -1;
+		}
+
+		/* Split data in a=1&b=xy&c=3&c=4 ... */
+		while (*data) {
+			const char *val = strchr(data, '=');
+			const char *next;
+			ptrdiff_t keylen, vallen;
+
+			if (!val) {
+				break;
+			}
+			keylen = val - data;
+
+			/* In every "field_found" callback we ask what to do with the
+			 * data ("field_storage"). This could be:
+			 * MG_FORM_FIELD_STORAGE_SKIP (0):
+			 *   ignore the value of this field
+			 * MG_FORM_FIELD_STORAGE_GET (1):
+			 *   read the data and call the get callback function
+			 * MG_FORM_FIELD_STORAGE_STORE (2):
+			 *   store the data in a file
+			 * MG_FORM_FIELD_STORAGE_READ (3):
+			 *   let the user read the data (for parsing long data on the fly)
+			 * MG_FORM_FIELD_STORAGE_ABORT (flag):
+			 *   stop parsing
+			 */
+			memset(path, 0, sizeof(path));
+			field_count++;
+			field_storage = url_encoded_field_found(conn,
+			                                        data,
+			                                        (size_t)keylen,
+			                                        NULL,
+			                                        0,
+			                                        path,
+			                                        sizeof(path) - 1,
+			                                        fdh);
+
+			val++;
+			next = strchr(val, '&');
+			if (next) {
+				vallen = next - val;
+				next++;
+			} else {
+				vallen = (ptrdiff_t)strlen(val);
+				next = val + vallen;
+			}
+
+			if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+				/* Call callback */
+				url_encoded_field_get(
+				    conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
+			}
+			if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+				/* Store the content to a file */
+				if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
+					fstore.access.fp = NULL;
+				}
+				file_size = 0;
+				if (fstore.access.fp != NULL) {
+					size_t n = (size_t)
+					    fwrite(val, 1, (size_t)vallen, fstore.access.fp);
+					if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
+						mg_cry_internal(conn,
+						                "%s: Cannot write file %s",
+						                __func__,
+						                path);
+						(void)mg_fclose(&fstore.access);
+						remove_bad_file(conn, path);
+					}
+					file_size += (int64_t)n;
+
+					if (fstore.access.fp) {
+						r = mg_fclose(&fstore.access);
+						if (r == 0) {
+							/* stored successfully */
+							field_stored(conn, path, file_size, fdh);
+						} else {
+							mg_cry_internal(conn,
+							                "%s: Error saving file %s",
+							                __func__,
+							                path);
+							remove_bad_file(conn, path);
+						}
+						fstore.access.fp = NULL;
+					}
+
+				} else {
+					mg_cry_internal(conn,
+					                "%s: Cannot create file %s",
+					                __func__,
+					                path);
+				}
+			}
+
+			/* if (field_storage == MG_FORM_FIELD_STORAGE_READ) { */
+			/* The idea of "field_storage=read" is to let the API user read
+			 * data chunk by chunk and to some data processing on the fly.
+			 * This should avoid the need to store data in the server:
+			 * It should neither be stored in memory, like
+			 * "field_storage=get" does, nor in a file like
+			 * "field_storage=store".
+			 * However, for a "GET" request this does not make any much
+			 * sense, since the data is already stored in memory, as it is
+			 * part of the query string.
+			 */
+			/* } */
+
+			if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
+			    == MG_FORM_FIELD_STORAGE_ABORT) {
+				/* Stop parsing the request */
+				break;
+			}
+
+			/* Proceed to next entry */
+			data = next;
+		}
+
+		return field_count;
+	}
+
+	content_type = mg_get_header(conn, "Content-Type");
+
+	if (!content_type
+	    || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
+	    || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
+		/* The form data is in the request body data, encoded in key/value
+		 * pairs. */
+		int all_data_read = 0;
+
+		/* Read body data and split it in keys and values.
+		 * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
+		 * Here we use "POST", and read the data from the request body.
+		 * The data read on the fly, so it is not required to buffer the
+		 * entire request in memory before processing it. */
+		for (;;) {
+			const char *val;
+			const char *next;
+			ptrdiff_t keylen, vallen;
+			ptrdiff_t used;
+			int end_of_key_value_pair_found = 0;
+			int get_block;
+
+			if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+				size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+				r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+				if (r < 0) {
+					/* read error */
+					return -1;
+				}
+				if (r != (int)to_read) {
+					/* TODO: Create a function to get "all_data_read" from
+					 * the conn object. All data is read if the Content-Length
+					 * has been reached, or if chunked encoding is used and
+					 * the end marker has been read, or if the connection has
+					 * been closed. */
+					all_data_read = 1;
+				}
+				buf_fill += r;
+				buf[buf_fill] = 0;
+				if (buf_fill < 1) {
+					break;
+				}
+			}
+
+			val = strchr(buf, '=');
+
+			if (!val) {
+				break;
+			}
+			keylen = val - buf;
+			val++;
+
+			/* Call callback */
+			memset(path, 0, sizeof(path));
+			field_count++;
+			field_storage = url_encoded_field_found(conn,
+			                                        buf,
+			                                        (size_t)keylen,
+			                                        NULL,
+			                                        0,
+			                                        path,
+			                                        sizeof(path) - 1,
+			                                        fdh);
+
+			if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
+			    == MG_FORM_FIELD_STORAGE_ABORT) {
+				/* Stop parsing the request */
+				break;
+			}
+
+			if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+				if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
+					fstore.access.fp = NULL;
+				}
+				file_size = 0;
+				if (!fstore.access.fp) {
+					mg_cry_internal(conn,
+					                "%s: Cannot create file %s",
+					                __func__,
+					                path);
+				}
+			}
+
+			get_block = 0;
+			/* Loop to read values larger than sizeof(buf)-keylen-2 */
+			do {
+				next = strchr(val, '&');
+				if (next) {
+					vallen = next - val;
+					next++;
+					end_of_key_value_pair_found = 1;
+				} else {
+					vallen = (ptrdiff_t)strlen(val);
+					next = val + vallen;
+					end_of_key_value_pair_found = all_data_read;
+				}
+
+				if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+#if 0
+					if (!end_of_key_value_pair_found && !all_data_read) {
+						/* This callback will deliver partial contents */
+					}
+#endif
+
+					/* Call callback */
+					url_encoded_field_get(conn,
+					                      ((get_block > 0) ? NULL : buf),
+					                      ((get_block > 0) ? 0
+					                                       : (size_t)keylen),
+					                      val,
+					                      (size_t)vallen,
+					                      fdh);
+					get_block++;
+				}
+				if (fstore.access.fp) {
+					size_t n = (size_t)
+					    fwrite(val, 1, (size_t)vallen, fstore.access.fp);
+					if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
+						mg_cry_internal(conn,
+						                "%s: Cannot write file %s",
+						                __func__,
+						                path);
+						mg_fclose(&fstore.access);
+						remove_bad_file(conn, path);
+					}
+					file_size += (int64_t)n;
+				}
+
+				if (!end_of_key_value_pair_found) {
+					used = next - buf;
+					memmove(buf,
+					        buf + (size_t)used,
+					        sizeof(buf) - (size_t)used);
+					next = buf;
+					buf_fill -= (int)used;
+					if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+						size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+						r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+						if (r < 0) {
+							/* read error */
+							if (fstore.access.fp) {
+								mg_fclose(&fstore.access);
+								remove_bad_file(conn, path);
+							}
+							return -1;
+						}
+						if (r != (int)to_read) {
+							/* TODO: Create a function to get "all_data_read"
+							 * from the conn object. All data is read if the
+							 * Content-Length has been reached, or if chunked
+							 * encoding is used and the end marker has been
+							 * read, or if the connection has been closed. */
+							all_data_read = 1;
+						}
+						buf_fill += r;
+						buf[buf_fill] = 0;
+						if (buf_fill < 1) {
+							break;
+						}
+						val = buf;
+					}
+				}
+
+			} while (!end_of_key_value_pair_found);
+
+			if (fstore.access.fp) {
+				r = mg_fclose(&fstore.access);
+				if (r == 0) {
+					/* stored successfully */
+					field_stored(conn, path, file_size, fdh);
+				} else {
+					mg_cry_internal(conn,
+					                "%s: Error saving file %s",
+					                __func__,
+					                path);
+					remove_bad_file(conn, path);
+				}
+				fstore.access.fp = NULL;
+			}
+
+			if (all_data_read && (buf_fill == 0)) {
+				/* nothing more to process */
+				break;
+			}
+
+			/* Proceed to next entry */
+			used = next - buf;
+			memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+			buf_fill -= (int)used;
+		}
+
+		return field_count;
+	}
+
+	if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
+		/* The form data is in the request body data, encoded as multipart
+		 * content (see https://www.ietf.org/rfc/rfc1867.txt,
+		 * https://www.ietf.org/rfc/rfc2388.txt). */
+		char *boundary;
+		size_t bl;
+		ptrdiff_t used;
+		struct mg_request_info part_header;
+		char *hbuf;
+		const char *content_disp, *hend, *fbeg, *fend, *nbeg, *nend;
+		const char *next;
+		unsigned part_no;
+
+		memset(&part_header, 0, sizeof(part_header));
+
+		/* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
+		bl = 20;
+		while (content_type[bl] == ' ') {
+			bl++;
+		}
+
+		/* There has to be a BOUNDARY definition in the Content-Type header */
+		if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
+			/* Malformed request */
+			return -1;
+		}
+
+		/* Copy boundary string to variable "boundary" */
+		fbeg = content_type + bl + 9;
+		bl = strlen(fbeg);
+		boundary = (char *)mg_malloc(bl + 1);
+		if (!boundary) {
+			/* Out of memory */
+			mg_cry_internal(conn,
+			                "%s: Cannot allocate memory for boundary [%lu]",
+			                __func__,
+			                (unsigned long)bl);
+			return -1;
+		}
+		memcpy(boundary, fbeg, bl);
+		boundary[bl] = 0;
+
+		/* RFC 2046 permits the boundary string to be quoted. */
+		/* If the boundary is quoted, trim the quotes */
+		if (boundary[0] == '"') {
+			hbuf = strchr(boundary + 1, '"');
+			if ((!hbuf) || (*hbuf != '"')) {
+				/* Malformed request */
+				mg_free(boundary);
+				return -1;
+			}
+			*hbuf = 0;
+			memmove(boundary, boundary + 1, bl);
+			bl = strlen(boundary);
+		}
+
+		/* Do some sanity checks for boundary lengths */
+		if (bl > 70) {
+			/* From RFC 2046:
+			 * Boundary delimiters must not appear within the
+			 * encapsulated material, and must be no longer
+			 * than 70 characters, not counting the two
+			 * leading hyphens.
+			 */
+
+			/* The initial sanity check
+			 * (bl + 800 > sizeof(buf))
+			 * is no longer required, since sizeof(buf) == 1024
+			 *
+			 * Original comment:
+			 */
+			/* Sanity check:  The algorithm can not work if bl >= sizeof(buf),
+			 * and it will not work effectively, if the buf is only a few byte
+			 * larger than bl, or if buf can not hold the multipart header
+			 * plus the boundary.
+			 * Check some reasonable number here, that should be fulfilled by
+			 * any reasonable request from every browser. If it is not
+			 * fulfilled, it might be a hand-made request, intended to
+			 * interfere with the algorithm. */
+			mg_free(boundary);
+			return -1;
+		}
+		if (bl < 4) {
+			/* Sanity check:  A boundary string of less than 4 bytes makes
+			 * no sense either. */
+			mg_free(boundary);
+			return -1;
+		}
+
+		for (part_no = 0;; part_no++) {
+			size_t towrite, fnlen, n;
+			int get_block;
+
+			r = mg_read(conn,
+			            buf + (size_t)buf_fill,
+			            sizeof(buf) - 1 - (size_t)buf_fill);
+			if (r < 0) {
+				/* read error */
+				mg_free(boundary);
+				return -1;
+			}
+			buf_fill += r;
+			buf[buf_fill] = 0;
+			if (buf_fill < 1) {
+				/* No data */
+				mg_free(boundary);
+				return -1;
+			}
+
+			if (part_no == 0) {
+				int d = 0;
+				while ((buf[d] != '-') && (d < buf_fill)) {
+					d++;
+				}
+				if ((d > 0) && (buf[d] == '-')) {
+					memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d);
+					buf_fill -= d;
+					buf[buf_fill] = 0;
+				}
+			}
+
+			if (buf[0] != '-' || buf[1] != '-') {
+				/* Malformed request */
+				mg_free(boundary);
+				return -1;
+			}
+			if (0 != strncmp(buf + 2, boundary, bl)) {
+				/* Malformed request */
+				mg_free(boundary);
+				return -1;
+			}
+			if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
+				/* Every part must end with \r\n, if there is another part.
+				 * The end of the request has an extra -- */
+				if (((size_t)buf_fill != (size_t)(bl + 6))
+				    || (strncmp(buf + bl + 2, "--\r\n", 4))) {
+					/* Malformed request */
+					mg_free(boundary);
+					return -1;
+				}
+				/* End of the request */
+				break;
+			}
+
+			/* Next, we need to get the part header: Read until \r\n\r\n */
+			hbuf = buf + bl + 4;
+			hend = strstr(hbuf, "\r\n\r\n");
+			if (!hend) {
+				/* Malformed request */
+				mg_free(boundary);
+				return -1;
+			}
+
+			part_header.num_headers =
+			    parse_http_headers(&hbuf, part_header.http_headers);
+			if ((hend + 2) != hbuf) {
+				/* Malformed request */
+				mg_free(boundary);
+				return -1;
+			}
+
+			/* Skip \r\n\r\n */
+			hend += 4;
+
+			/* According to the RFC, every part has to have a header field like:
+			 * Content-Disposition: form-data; name="..." */
+			content_disp = get_header(part_header.http_headers,
+			                          part_header.num_headers,
+			                          "Content-Disposition");
+			if (!content_disp) {
+				/* Malformed request */
+				mg_free(boundary);
+				return -1;
+			}
+
+			/* Get the mandatory name="..." part of the Content-Disposition
+			 * header. */
+			nbeg = strstr(content_disp, "name=\"");
+			while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
+				/* It could be somethingname= instead of name= */
+				nbeg = strstr(nbeg + 1, "name=\"");
+			}
+
+			/* This line is not required, but otherwise some compilers
+			 * generate spurious warnings. */
+			nend = nbeg;
+			/* And others complain, the result is unused. */
+			(void)nend;
+
+			/* If name=" is found, search for the closing " */
+			if (nbeg) {
+				nbeg += 6;
+				nend = strchr(nbeg, '\"');
+				if (!nend) {
+					/* Malformed request */
+					mg_free(boundary);
+					return -1;
+				}
+			} else {
+				/* name= without quotes is also allowed */
+				nbeg = strstr(content_disp, "name=");
+				while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
+					/* It could be somethingname= instead of name= */
+					nbeg = strstr(nbeg + 1, "name=");
+				}
+				if (!nbeg) {
+					/* Malformed request */
+					mg_free(boundary);
+					return -1;
+				}
+				nbeg += 5;
+
+				/* RFC 2616 Sec. 2.2 defines a list of allowed
+				 * separators, but many of them make no sense
+				 * here, e.g. various brackets or slashes.
+				 * If they are used, probably someone is
+				 * trying to attack with curious hand made
+				 * requests. Only ; , space and tab seem to be
+				 * reasonable here. Ignore everything else. */
+				nend = nbeg + strcspn(nbeg, ",; \t");
+			}
+
+			/* Get the optional filename="..." part of the Content-Disposition
+			 * header. */
+			fbeg = strstr(content_disp, "filename=\"");
+			while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
+				/* It could be somethingfilename= instead of filename= */
+				fbeg = strstr(fbeg + 1, "filename=\"");
+			}
+
+			/* This line is not required, but otherwise some compilers
+			 * generate spurious warnings. */
+			fend = fbeg;
+
+			/* If filename=" is found, search for the closing " */
+			if (fbeg) {
+				fbeg += 10;
+				fend = strchr(fbeg, '\"');
+
+				if (!fend) {
+					/* Malformed request (the filename field is optional, but if
+					 * it exists, it needs to be terminated correctly). */
+					mg_free(boundary);
+					return -1;
+				}
+
+				/* TODO: check Content-Type */
+				/* Content-Type: application/octet-stream */
+			}
+			if (!fbeg) {
+				/* Try the same without quotes */
+				fbeg = strstr(content_disp, "filename=");
+				while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
+					/* It could be somethingfilename= instead of filename= */
+					fbeg = strstr(fbeg + 1, "filename=");
+				}
+				if (fbeg) {
+					fbeg += 9;
+					fend = fbeg + strcspn(fbeg, ",; \t");
+				}
+			}
+
+			if (!fbeg || !fend) {
+				fbeg = NULL;
+				fend = NULL;
+				fnlen = 0;
+			} else {
+				fnlen = (size_t)(fend - fbeg);
+			}
+
+			/* In theory, it could be possible that someone crafts
+			 * a request like name=filename=xyz. Check if name and
+			 * filename do not overlap. */
+			if (!(((ptrdiff_t)fbeg > (ptrdiff_t)nend)
+			      || ((ptrdiff_t)nbeg > (ptrdiff_t)fend))) {
+				mg_free(boundary);
+				return -1;
+			}
+
+			/* Call callback for new field */
+			memset(path, 0, sizeof(path));
+			field_count++;
+			field_storage = url_encoded_field_found(conn,
+			                                        nbeg,
+			                                        (size_t)(nend - nbeg),
+			                                        ((fnlen > 0) ? fbeg : NULL),
+			                                        fnlen,
+			                                        path,
+			                                        sizeof(path) - 1,
+			                                        fdh);
+
+			/* If the boundary is already in the buffer, get the address,
+			 * otherwise next will be NULL. */
+			next = search_boundary(hbuf,
+			                       (size_t)((buf - hbuf) + buf_fill),
+			                       boundary,
+			                       bl);
+
+			if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+				/* Store the content to a file */
+				if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
+					fstore.access.fp = NULL;
+				}
+				file_size = 0;
+
+				if (!fstore.access.fp) {
+					mg_cry_internal(conn,
+					                "%s: Cannot create file %s",
+					                __func__,
+					                path);
+				}
+			}
+
+			get_block = 0;
+			while (!next) {
+				/* Set "towrite" to the number of bytes available
+				 * in the buffer */
+				towrite = (size_t)(buf - hend + buf_fill);
+				/* Subtract the boundary length, to deal with
+				 * cases the boundary is only partially stored
+				 * in the buffer. */
+				towrite -= bl + 4;
+
+				if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+					unencoded_field_get(conn,
+					                    ((get_block > 0) ? NULL : nbeg),
+					                    ((get_block > 0)
+					                         ? 0
+					                         : (size_t)(nend - nbeg)),
+					                    hend,
+					                    towrite,
+					                    fdh);
+					get_block++;
+				}
+
+				if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+					if (fstore.access.fp) {
+
+						/* Store the content of the buffer. */
+						n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
+						if ((n != towrite) || (ferror(fstore.access.fp))) {
+							mg_cry_internal(conn,
+							                "%s: Cannot write file %s",
+							                __func__,
+							                path);
+							mg_fclose(&fstore.access);
+							remove_bad_file(conn, path);
+						}
+						file_size += (int64_t)n;
+					}
+				}
+
+				memmove(buf, hend + towrite, bl + 4);
+				buf_fill = (int)(bl + 4);
+				hend = buf;
+
+				/* Read new data */
+				r = mg_read(conn,
+				            buf + (size_t)buf_fill,
+				            sizeof(buf) - 1 - (size_t)buf_fill);
+				if (r < 0) {
+					/* read error */
+					if (fstore.access.fp) {
+						mg_fclose(&fstore.access);
+						remove_bad_file(conn, path);
+					}
+					mg_free(boundary);
+					return -1;
+				}
+				buf_fill += r;
+				buf[buf_fill] = 0;
+				/* buf_fill is at least 8 here */
+
+				/* Find boundary */
+				next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
+			}
+
+			towrite = (size_t)(next - hend);
+
+			if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+				/* Call callback */
+				unencoded_field_get(conn,
+				                    ((get_block > 0) ? NULL : nbeg),
+				                    ((get_block > 0) ? 0
+				                                     : (size_t)(nend - nbeg)),
+				                    hend,
+				                    towrite,
+				                    fdh);
+			}
+
+			if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+
+				if (fstore.access.fp) {
+					n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
+					if ((n != towrite) || (ferror(fstore.access.fp))) {
+						mg_cry_internal(conn,
+						                "%s: Cannot write file %s",
+						                __func__,
+						                path);
+						mg_fclose(&fstore.access);
+						remove_bad_file(conn, path);
+					} else {
+						file_size += (int64_t)n;
+						r = mg_fclose(&fstore.access);
+						if (r == 0) {
+							/* stored successfully */
+							field_stored(conn, path, file_size, fdh);
+						} else {
+							mg_cry_internal(conn,
+							                "%s: Error saving file %s",
+							                __func__,
+							                path);
+							remove_bad_file(conn, path);
+						}
+					}
+					fstore.access.fp = NULL;
+				}
+			}
+
+			if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
+			    == MG_FORM_FIELD_STORAGE_ABORT) {
+				/* Stop parsing the request */
+				break;
+			}
+
+			/* Remove from the buffer */
+			used = next - buf + 2;
+			memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+			buf_fill -= (int)used;
+		}
+
+		/* All parts handled */
+		mg_free(boundary);
+		return field_count;
+	}
+
+	/* Unknown Content-Type */
+	return -1;
+}
+
+
+/* End of handle_form.inl */