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 */
