From d67a2e6ea547ef392bed53734d357ef10080e241 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Oct 2013 09:11:49 -0700 Subject: [PATCH] update to latest civetweb from source --- externals/civetweb/include/civetweb.h | 622 ++++----- externals/civetweb/src/civetweb.c | 1759 +++++++++++++++---------- 2 files changed, 1348 insertions(+), 1033 deletions(-) diff --git a/externals/civetweb/include/civetweb.h b/externals/civetweb/include/civetweb.h index 1c1b0a9294..22caa2a030 100755 --- a/externals/civetweb/include/civetweb.h +++ b/externals/civetweb/include/civetweb.h @@ -1,272 +1,284 @@ -// Copyright (c) 2004-2012 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. +/* 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 +#ifndef CIVETWEB_VERSION +#define CIVETWEB_VERSION "1.6" +#endif + #include #include #ifdef __cplusplus extern "C" { -#endif // __cplusplus +#endif /* __cplusplus */ -struct mg_context; // Handle for the HTTP service itself -struct mg_connection; // Handle for the individual connection +struct mg_context; /* Handle for the HTTP service itself */ +struct mg_connection; /* Handle for the individual connection */ -// This structure contains information about the HTTP request. +/* This structure contains information about the HTTP request. */ struct mg_request_info { - const char *request_method; // "GET", "POST", etc - const char *uri; // URL-decoded URI - 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 - long remote_ip; // Client's IP address - 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 + const char *request_method; /* "GET", "POST", etc */ + const char *uri; /* URL-decoded URI */ + 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 */ + long remote_ip; /* Client's IP address */ + 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 + int num_headers; /* Number of HTTP headers */ struct mg_header { - const char *name; // HTTP header name - const char *value; // HTTP header value - } http_headers[64]; // Maximum 64 headers + const char *name; /* HTTP header name */ + const char *value; /* HTTP header value */ + } http_headers[64]; /* Maximum 64 headers */ }; -// This structure needs to be passed to mg_start(), to let civetweb know -// which callbacks to invoke. For detailed description, see -// https://github.com/sunsetbrew/civetweb/blob/master/docs/UserManual.md +/* This structure needs to be passed to mg_start(), to let civetweb know + which callbacks to invoke. For detailed description, see + https://github.com/sunsetbrew/civetweb/blob/master/docs/UserManual.md */ struct mg_callbacks { - // Called when civetweb has received new HTTP request. - // If callback returns non-zero, - // callback must process the request by sending valid HTTP headers and body, - // and civetweb will not do any further processing. - // If callback returns 0, civetweb processes the request itself. In this case, - // callback must not send any data to the client. + /* Called when civetweb has received new HTTP request. + If callback returns non-zero, + callback must process the request by sending valid HTTP headers and + body, and civetweb will not do any further processing. + If callback returns 0, civetweb processes the request itself. In this + case, callback must not send any data to the client. */ int (*begin_request)(struct mg_connection *); - // Called when civetweb has finished processing request. + /* 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. + /* 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 initializes SSL library. + /* Called when civetweb initializes SSL library. */ int (*init_ssl)(void *ssl_context, void *user_data); - // Called when websocket request is received, before websocket handshake. - // If callback returns 0, civetweb proceeds with handshake, otherwise - // cinnection is closed immediately. + /* Called when websocket request is received, before websocket handshake. + If callback returns 0, civetweb proceeds with handshake, otherwise + cinnection is closed immediately. */ int (*websocket_connect)(const struct mg_connection *); - // Called when websocket handshake is successfully completed, and - // connection is ready for data exchange. + /* Called when websocket handshake is successfully completed, and + connection is ready for data exchange. */ 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: - // non-0: keep this websocket connection opened. - // 0: close this websocket 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: + non-0: keep this websocket connection opened. + 0: close this websocket connection. */ int (*websocket_data)(struct mg_connection *, int bits, char *data, size_t data_len); - // Called when civetweb is closing a connection. The per-context mutex is locked when this - // is invoked. This is primarily useful for noting when a websocket is closing and removing it - // from any application-maintained list of clients. + /* Called when civetweb is closing a connection. The per-context mutex is + locked when this is invoked. This is primarily useful for noting when + a websocket is closing and removing it from any application-maintained + list of clients. */ void (*connection_close)(struct mg_connection *); - // Called when civetweb tries to open a file. Used to intercept file open - // calls, and serve file data from memory instead. - // Parameters: - // path: Full path to the file to open. - // data_len: Placeholder for the file size, if file is served from memory. - // Return value: - // NULL: do not serve file from memory, proceed with normal file open. - // non-NULL: pointer to the file contents in memory. data_len must be - // initilized with the size of the memory block. + /* Called when civetweb tries to open a file. Used to intercept file open + calls, and serve file data from memory instead. + Parameters: + path: Full path to the file to open. + data_len: Placeholder for the file size, if file is served from + memory. + Return value: + NULL: do not serve file from memory, proceed with normal file open. + non-NULL: pointer to the file contents in memory. data_len must be + initilized with the size of the memory block. */ const char * (*open_file)(const struct mg_connection *, const char *path, size_t *data_len); - // Called when civetweb is about to serve Lua server page (.lp file), if - // Lua support is enabled. - // Parameters: - // lua_context: "lua_State *" pointer. + /* Called when civetweb is about to serve Lua server page (.lp file), if + Lua support is enabled. + Parameters: + lua_context: "lua_State *" pointer. */ void (*init_lua)(struct mg_connection *, void *lua_context); - // Called when civetweb has uploaded a file to a temporary directory as a - // result of mg_upload() call. - // Parameters: - // file_file: full path name to the uploaded file. + /* Called when civetweb has uploaded a file to a temporary directory as a + result of mg_upload() call. + Parameters: + file_file: full path name to the uploaded file. */ void (*upload)(struct mg_connection *, const char *file_name); - // Called when civetweb is about to send HTTP error to the client. - // Implementing this callback allows to create custom error pages. - // Parameters: - // status: HTTP error status code. + /* Called when civetweb is about to send HTTP error to the client. + Implementing this callback allows to create custom error pages. + Parameters: + status: HTTP error status code. */ int (*http_error)(struct mg_connection *, int status); }; -// 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/sunsetbrew/civetweb/blob/master/docs/UserManual.md -// for the list of valid option and their possible values. -// -// Return: -// web server context, or NULL on error. +/* 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/sunsetbrew/civetweb/blob/master/docs/UserManual.md + for the list of valid option and their possible values. + + Return: + web server context, or NULL on error. */ 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. +/* 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. */ void mg_stop(struct mg_context *); -// 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: the handler processed the request. +/* 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: the handler processed the request. */ typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata); -// mg_set_request_handler -// -// Sets or removes a URI mapping for a request handler. -// -// 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 to configure -// handler: the callback handler to use when the URI is requested. -// If NULL, the URI will be removed. -// cbdata: the callback data to give to the handler when it s requested. +/* mg_set_request_handler + + Sets or removes a URI mapping for a request handler. + + 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 to configure + handler: the callback handler to use when the URI is requested. + If NULL, the URI will be removed. + cbdata: the callback data to give to the handler when it s requested. */ void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata); -// 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. +/* 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. */ const char *mg_get_option(const struct mg_context *ctx, const char *name); -// Return array of strings that represent valid configuration options. -// For each option, option name and default value is returned, i.e. the -// number of entries in the array equals to number_of_options x 2. -// Array is NULL terminated. +/* Return array of strings that represent valid configuration options. + For each option, option name and default value is returned, i.e. the + number of entries in the array equals to number_of_options x 2. + Array is NULL terminated. */ const char **mg_get_valid_option_names(void); -// 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. -// -// If password is not NULL, entry is added (or modified if already exists). -// If password is NULL, entry is deleted. -// -// Return: -// 1 on success, 0 on error. +/* 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. + + If password is not NULL, entry is added (or modified if already exists). + If password is NULL, entry is deleted. + + Return: + 1 on success, 0 on error. */ int mg_modify_passwords_file(const char *passwords_file_name, const char *domain, const char *user, const char *password); -// Return information associated with the request. +/* Return information associated with the request. */ struct mg_request_info *mg_get_request_info(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 +/* Send data to the client. + Return: + 0 when the connection has been closed + -1 on error + >0 number of bytes written on success */ int mg_write(struct mg_connection *, const void *buf, size_t len); -// Send data to a websocket client wrapped in a websocket frame. Uses mg_lock 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 +/* Send data to a websocket client wrapped in a websocket frame. Uses mg_lock + 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 */ int mg_websocket_write(struct mg_connection* conn, int opcode, const char *data, size_t data_len); -// Blocks until unique access is obtained to this connection. Intended for use with websockets only. -// Invoke this before mg_write or mg_printf when communicating with a websocket if your code has -// server-initiated communication as well as communication in direct response to a message. +/* Blocks until unique access is obtained to this connection. Intended for use + with websockets only. + Invoke this before mg_write or mg_printf when communicating with a + websocket if your code has server-initiated communication as well as + communication in direct response to a message. */ void mg_lock(struct mg_connection* conn); void mg_unlock(struct mg_connection* conn); -// Opcodes, from http://tools.ietf.org/html/rfc6455 +/* Opcodes, from http://tools.ietf.org/html/rfc6455 */ enum { WEBSOCKET_OPCODE_CONTINUATION = 0x0, WEBSOCKET_OPCODE_TEXT = 0x1, @@ -277,7 +289,7 @@ enum { }; -// Macros for enabling compiler-specific checks for printf-like arguments. +/* Macros for enabling compiler-specific checks for printf-like arguments. */ #undef PRINTF_FORMAT_STRING #if defined(_MSC_VER) && _MSC_VER >= 1400 #include @@ -296,179 +308,181 @@ enum { #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. +/* Send data to the client using printf() semantics. + + Works exactly like mg_write(), but allows to do message formatting. */ int mg_printf(struct mg_connection *, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); -// Send contents of the entire file together with HTTP headers. +/* Send contents of the entire file together with HTTP headers. */ void mg_send_file(struct mg_connection *conn, const char *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. +/* 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. */ 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. +/* 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. */ 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. +/* 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. */ 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. +/* 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. */ 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). +/* 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). */ 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: wether 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"); +/* 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: wether 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"); + */ 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(). +/* Close the connection opened by mg_download(). */ void mg_close_connection(struct mg_connection *conn); -// File upload functionality. Each uploaded file gets saved into a temporary -// file and MG_UPLOAD event is sent. -// Return number of uploaded files. +/* File upload functionality. Each uploaded file gets saved into a temporary + file and MG_UPLOAD event is sent. + Return number of uploaded files. */ int mg_upload(struct mg_connection *conn, const char *destination_dir); -// Convenience function -- create detached thread. -// Return: 0 on success, non-0 on error. +/* Convenience function -- create detached thread. + Return: 0 on success, non-0 on error. */ typedef void * (*mg_thread_func_t)(void *); int mg_start_thread(mg_thread_func_t f, void *p); -// Return builtin mime type for the given file name. -// For unrecognized extensions, "text/plain" is returned. +/* Return builtin mime type for the given file name. + For unrecognized extensions, "text/plain" is returned. */ const char *mg_get_builtin_mime_type(const char *file_name); -// Return Civetweb version. +/* Return Civetweb version. */ 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. +/* 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. */ 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. +/* URL-encode input buffer into destination buffer. + returns the length of the resulting buffer or -1 + is the buffer is too small. */ int mg_url_encode(const char *src, char *dst, size_t dst_len); -// 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); +/* 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); */ char *mg_md5(char buf[33], ...); -// Print error message to the opened error log stream. -// This utilizes the provided logging configuration. -// conn: connection -// fmt: format string without the line return -// ...: variable argument list -// Example: -// mg_cry(conn,"i like %s", "logging"); +/* Print error message to the opened error log stream. + This utilizes the provided logging configuration. + conn: connection + fmt: format string without the line return + ...: variable argument list + Example: + mg_cry(conn,"i like %s", "logging"); */ void mg_cry(struct mg_connection *conn, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); -// utility method to compare two buffers, case incensitive. +/* utility method to compare two buffers, case incensitive. */ int mg_strncasecmp(const char *s1, const char *s2, size_t len); #ifdef __cplusplus } -#endif // __cplusplus +#endif /* __cplusplus */ -#endif // CIVETWEB_HEADER_INCLUDED +#endif /* CIVETWEB_HEADER_INCLUDED */ diff --git a/externals/civetweb/src/civetweb.c b/externals/civetweb/src/civetweb.c index c83bd96fb6..6c5a08561c 100755 --- a/externals/civetweb/src/civetweb.c +++ b/externals/civetweb/src/civetweb.c @@ -1,66 +1,73 @@ -// 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. +/* 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. + */ #if defined(_WIN32) #if !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005 +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ #endif #else #ifdef __linux__ -#define _XOPEN_SOURCE 600 // For flockfile() on Linux +#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ #endif -#define _LARGEFILE_SOURCE // Enable 64-bit file offsets -#define __STDC_FORMAT_MACROS // wants this for C++ -#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX +#define _LARGEFILE_SOURCE /* Enable 64-bit file offsets */ +#define __STDC_FORMAT_MACROS /* wants this for C++ */ +#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ #endif #if defined (_MSC_VER) -// conditional expression is constant: introduced by FD_SET(..) +/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */ +#pragma warning (disable : 4306 ) +/* conditional expression is constant: introduced by FD_SET(..) */ #pragma warning (disable : 4127) -// non-constant aggregate initializer: issued due to missing C99 support +/* non-constant aggregate initializer: issued due to missing C99 support */ #pragma warning (disable : 4204) #endif -// Disable WIN32_LEAN_AND_MEAN. -// This makes windows.h always include winsock2.h -#ifdef WIN32_LEAN_AND_MEAN +/* Disable WIN32_LEAN_AND_MEAN. + This makes windows.h always include winsock2.h */ +#if defined(WIN32_LEAN_AND_MEAN) && (_MSC_VER <= 1400) #undef WIN32_LEAN_AND_MEAN #endif +#if defined USE_IPV6 && defined(_WIN32) +#include +#endif + #if defined(__SYMBIAN32__) -#define NO_SSL // SSL is not supported -#define NO_CGI // CGI is not supported +#define NO_SSL /* SSL is not supported */ +#define NO_CGI /* CGI is not supported */ #define PATH_MAX FILENAME_MAX -#endif // __SYMBIAN32__ +#endif /* __SYMBIAN32__ */ #ifndef IGNORE_UNUSED_RESULT #define IGNORE_UNUSED_RESULT(a) (void)((a) && 1) #endif -#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE +#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */ #include #include #include #include #include -#endif // !_WIN32_WCE +#endif /* !_WIN32_WCE */ #include #include @@ -72,9 +79,13 @@ #include #include -#if defined(_WIN32) && !defined(__SYMBIAN32__) // Windows specific +#define MAX_WORKER_THREADS 1024 + +#if defined(_WIN32) && !defined(__SYMBIAN32__) /* Windows specific */ +#if defined(_MSC_VER) && _MSC_VER <= 1400 #undef _WIN32_WINNT -#define _WIN32_WINNT 0x0400 // To make it link in VS2005 +#define _WIN32_WINNT 0x0400 /* To make it link in VS2005 */ +#endif #include #ifndef PATH_MAX @@ -85,25 +96,25 @@ #include #include #include -#else // _WIN32_WCE -#define NO_CGI // WinCE has no pipes +#else /* _WIN32_WCE */ +#define NO_CGI /* WinCE has no pipes */ typedef long off_t; #define errno GetLastError() #define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10) -#endif // _WIN32_WCE +#endif /* _WIN32_WCE */ #define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \ ((uint64_t)((uint32_t)(hi))) << 32)) -#define RATE_DIFF 10000000 // 100 nsecs +#define RATE_DIFF 10000000 /* 100 nsecs */ #define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de) #define SYS2UNIX_TIME(lo, hi) \ (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF) -// Visual Studio 6 does not know __func__ or __FUNCTION__ -// The rest of MS compilers use __FUNCTION__, not C99 __func__ -// Also use _strtoui64 on modern M$ compilers +/* Visual Studio 6 does not know __func__ or __FUNCTION__ + The rest of MS compilers use __FUNCTION__, not C99 __func__ + Also use _strtoui64 on modern M$ compilers */ #if defined(_MSC_VER) && _MSC_VER < 1300 #define STRX(x) #x #define STR(x) STRX(x) @@ -114,7 +125,7 @@ typedef long off_t; #define __func__ __FUNCTION__ #define strtoull(x, y, z) _strtoui64(x, y, z) #define strtoll(x, y, z) _strtoi64(x, y, z) -#endif // _MSC_VER +#endif /* _MSC_VER */ #define ERRNO GetLastError() #define NO_SOCKLEN_T @@ -123,7 +134,7 @@ typedef long off_t; #define O_NONBLOCK 0 #if !defined(EWOULDBLOCK) #define EWOULDBLOCK WSAEWOULDBLOCK -#endif // !EWOULDBLOCK +#endif /* !EWOULDBLOCK */ #define _POSIX_ #define INT64_FMT "I64d" @@ -154,18 +165,31 @@ typedef long off_t; #if !defined(va_copy) #define va_copy(x, y) x = y -#endif // !va_copy MINGW #defines va_copy +#endif /* !va_copy MINGW #defines va_copy */ #if !defined(fileno) #define fileno(x) _fileno(x) -#endif // !fileno MINGW #defines fileno +#endif /* !fileno MINGW #defines fileno */ typedef HANDLE pthread_mutex_t; -typedef struct { - HANDLE signal, broadcast; -} pthread_cond_t; +typedef DWORD pthread_key_t; typedef HANDLE pthread_t; -#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here. +typedef struct { + CRITICAL_SECTION threadIdSec; + int waitingthreadcount; /* The number of threads queued. */ + pthread_t *waitingthreadhdls; /* The thread handles. */ +} pthread_cond_t; + +typedef DWORD clockid_t; +#define CLOCK_MONOTONIC (1) +#define CLOCK_REALTIME (2) + +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; + +#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ static int pthread_mutex_lock(pthread_mutex_t *); static int pthread_mutex_unlock(pthread_mutex_t *); @@ -181,9 +205,9 @@ typedef unsigned short uint16_t; typedef unsigned __int64 uint64_t; typedef __int64 int64_t; #define INT64_MAX 9223372036854775807 -#endif // HAVE_STDINT +#endif /* HAVE_STDINT */ -// POSIX dirent interface +/* POSIX dirent interface */ struct dirent { char d_name[PATH_MAX]; }; @@ -194,6 +218,7 @@ typedef struct DIR { struct dirent result; } DIR; +#if !defined(USE_IPV6) && defined(_WIN32) #ifndef HAVE_POLL struct pollfd { SOCKET fd; @@ -202,14 +227,14 @@ struct pollfd { }; #define POLLIN 1 #endif +#endif - -// Mark required libraries +/* Mark required libraries */ #ifdef _MSC_VER #pragma comment(lib, "Ws2_32.lib") #endif -#else // UNIX specific +#else /* UNIX specific */ #include #include #include @@ -240,7 +265,7 @@ struct pollfd { #endif #ifndef O_BINARY #define O_BINARY 0 -#endif // O_BINARY +#endif /* O_BINARY */ #define closesocket(a) close(a) #define mg_mkdir(x, y) mkdir(x, y) #define mg_remove(x) remove(x) @@ -251,11 +276,10 @@ struct pollfd { typedef int SOCKET; #define WINCDECL -#endif // End of Windows and UNIX specific includes +#endif /* End of Windows and UNIX specific includes */ #include "civetweb.h" -#define CIVETWEB_VERSION "1.5" #define PASSWORDS_FILE_NAME ".htpasswd" #define CGI_ENVIRONMENT_SIZE 4096 #define MAX_CGI_ENVIR_VARS 64 @@ -271,7 +295,32 @@ static DWORD pthread_self(void) { return GetCurrentThreadId(); } -#endif // _WIN32 + +int pthread_key_create(pthread_key_t *key, void (*_must_be_zero)(void*) /* destructor function not supported for windows */) +{ + assert(_must_be_zero == NULL); + if ((key!=0) && (_must_be_zero == NULL)) { + *key = TlsAlloc(); + return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1; + } + return -2; +} + +int pthread_key_delete(pthread_key_t key) +{ + return TlsFree(key) ? 0 : 1; +} + +int pthread_setspecific(pthread_key_t key, void * value) +{ + return TlsSetValue(key, value) ? 0 : 1; +} + +void *pthread_getspecific(pthread_key_t key) +{ + return TlsGetValue(key); +} +#endif /* _WIN32 */ #define MD5_STATIC static #include "md5.inl" @@ -293,16 +342,16 @@ static DWORD pthread_self(void) } while (0) #else #define DEBUG_TRACE(x) -#endif // DEBUG -#endif // DEBUG_TRACE +#endif /* DEBUG */ +#endif /* DEBUG_TRACE */ -// Darwin prior to 7.0 and Win32 do not have socklen_t +/* Darwin prior to 7.0 and Win32 do not have socklen_t */ #ifdef NO_SOCKLEN_T typedef int socklen_t; -#endif // NO_SOCKLEN_T +#endif /* NO_SOCKLEN_T */ #define _DARWIN_UNLIMITED_SELECT -#define IP_ADDR_STR_LEN 50 // IPv6 hex string is 46 chars +#define IP_ADDR_STR_LEN 50 /* IPv6 hex string is 46 chars */ #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 @@ -316,7 +365,7 @@ typedef int socklen_t; #define PATH_MAX 4096 #endif -// Size of the accepted socket queue +/* Size of the accepted socket queue */ #if !defined(MGSQLEN) #define MGSQLEN 20 #endif @@ -327,15 +376,17 @@ static const char *http_500_error = "Internal Server Error"; #include #include #else -// SSL loaded dynamically from DLL. -// I put the prototypes here to be independent from OpenSSL source installation. +/* SSL loaded dynamically from DLL. + I put the prototypes here to be independent from OpenSSL source + installation. */ + typedef struct ssl_st SSL; typedef struct ssl_method_st SSL_METHOD; typedef struct ssl_ctx_st SSL_CTX; struct ssl_func { - const char *name; // SSL function name - void (*ptr)(void); // Function pointer + const char *name; /* SSL function name */ + void (*ptr)(void); /* Function pointer */ }; #define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr) @@ -372,10 +423,10 @@ struct ssl_func { #define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr) #define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr) -// set_ssl_option() function updates this array. -// It loads SSL library dynamically and changes NULLs to the actual addresses -// of respective functions. The macros above (like SSL_connect()) are really -// just calling these functions indirectly via the pointer. +/* set_ssl_option() function updates this array. + It loads SSL library dynamically and changes NULLs to the actual addresses + of respective functions. The macros above (like SSL_connect()) are really + just calling these functions indirectly via the pointer. */ static struct ssl_func ssl_sw[] = { {"SSL_free", NULL}, {"SSL_accept", NULL}, @@ -401,7 +452,8 @@ static struct ssl_func ssl_sw[] = { {NULL, NULL} }; -// Similar array as ssl_sw. These functions could be located in different lib. +/* Similar array as ssl_sw. These functions could be located in different + lib. */ #if !defined(NO_SSL) static struct ssl_func crypto_sw[] = { {"CRYPTO_num_locks", NULL}, @@ -411,16 +463,16 @@ static struct ssl_func crypto_sw[] = { {"ERR_error_string", NULL}, {NULL, NULL} }; -#endif // NO_SSL -#endif // NO_SSL_DL +#endif /* NO_SSL */ +#endif /* NO_SSL_DL */ static const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -// Unified socket address. For IPv6 support, add IPv6 address structure -// in the union u. +/* Unified socket address. For IPv6 support, add IPv6 address structure + in the union u. */ union usa { struct sockaddr sa; struct sockaddr_in sin; @@ -429,7 +481,7 @@ union usa { #endif }; -// Describes a string (chunk of memory). +/* Describes a string (chunk of memory). */ struct vec { const char *ptr; size_t len; @@ -440,24 +492,25 @@ struct file { time_t modification_time; int64_t size; FILE *fp; - const char *membuf; // Non-NULL if file data is in memory - // set to 1 if the content is gzipped - // in which case we need a content-encoding: gzip header + const char *membuf; /* Non-NULL if file data is in memory */ + /* set to 1 if the content is gzipped + in which case we need a content-encoding: gzip header */ int gzipped; }; #define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL, 0} -// Describes listening socket, or socket which was accept()-ed by the master -// thread and queued for future handling by the worker thread. +/* Describes listening socket, or socket which was accept()-ed by the master + thread and queued for future handling by the worker thread. */ struct socket { - SOCKET sock; // Listening socket - union usa lsa; // Local socket address - union usa rsa; // Remote socket address - unsigned is_ssl:1; // Is port SSL-ed - unsigned ssl_redir:1; // Is port supposed to redirect everything to SSL port + SOCKET sock; /* Listening socket */ + union usa lsa; /* Local socket address */ + union usa rsa; /* Remote socket address */ + unsigned is_ssl:1; /* Is port SSL-ed */ + unsigned ssl_redir:1; /* Is port supposed to redirect everything to SSL + port */ }; -// NOTE(lsm): this enum shoulds be in sync with the config_options below. +/* NOTE(lsm): this enum shoulds be in sync with the config_options below. */ enum { CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER, PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, THROTTLE, @@ -506,58 +559,70 @@ struct mg_request_handler_info { }; struct mg_context { - volatile int stop_flag; // Should we stop event loop - void *ssllib_dll_handle; // Store the ssl library handle. - void *cryptolib_dll_handle; // Store the crypto library handle. - SSL_CTX *ssl_ctx; // SSL context - char *config[NUM_OPTIONS]; // Civetweb configuration parameters - struct mg_callbacks callbacks; // User-defined callback function - void *user_data; // User-defined data + volatile int stop_flag; /* Should we stop event loop */ + void *ssllib_dll_handle; /* Store the ssl library handle. */ + void *cryptolib_dll_handle; /* Store the crypto library handle. */ + SSL_CTX *ssl_ctx; /* SSL context */ + char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */ + struct mg_callbacks callbacks; /* User-defined callback function */ + void *user_data; /* User-defined data */ struct socket *listening_sockets; int num_listening_sockets; - volatile int num_threads; // Number of threads - pthread_mutex_t mutex; // Protects (max|num)_threads - pthread_cond_t cond; // Condvar for tracking workers terminations + volatile int num_threads; /* Number of threads */ + pthread_mutex_t mutex; /* Protects (max|num)_threads */ + pthread_cond_t cond; /* Condvar for tracking workers terminations */ - struct socket queue[MGSQLEN]; // Accepted sockets - volatile int sq_head; // Head of the socket queue - volatile int sq_tail; // Tail of the socket queue - pthread_cond_t sq_full; // Signaled when socket is produced - pthread_cond_t sq_empty; // Signaled when socket is consumed - pthread_t masterthreadid; // The master thread ID. - int workerthreadcount; // The amount of worker threads. - pthread_t *workerthreadids;// The worker thread IDs. + struct socket queue[MGSQLEN]; /* Accepted sockets */ + volatile int sq_head; /* Head of the socket queue */ + volatile int sq_tail; /* Tail of the socket queue */ + pthread_cond_t sq_full; /* Signaled when socket is produced */ + pthread_cond_t sq_empty; /* Signaled when socket is consumed */ + pthread_t masterthreadid; /* The master thread ID. */ + int workerthreadcount; /* The amount of worker threads. */ + pthread_t *workerthreadids;/* The worker thread IDs. */ - // linked list of uri handlers + /* linked list of uri handlers */ struct mg_request_handler_info *request_handlers; }; struct mg_connection { struct mg_request_info request_info; struct mg_context *ctx; - SSL *ssl; // SSL descriptor - SSL_CTX *client_ssl_ctx; // SSL context for client connections - struct socket client; // Connected client - time_t birth_time; // Time when request was received - int64_t num_bytes_sent; // Total bytes sent to client - int64_t content_len; // Content-Length header value - int64_t consumed_content; // How many bytes of content have been read - char *buf; // Buffer for received data - char *path_info; // PATH_INFO part of the URL - int must_close; // 1 if connection must be closed - int buf_size; // Buffer size - int request_len; // Size of the request + headers in a buffer - int data_len; // Total size of data in a buffer - int status_code; // HTTP reply status code, e.g. 200 - int throttle; // Throttling, bytes/sec. <= 0 means no throttle - time_t last_throttle_time; // Last time throttled data was sent - int64_t last_throttle_bytes;// Bytes sent this second - pthread_mutex_t mutex; // Used by mg_lock/mg_unlock to ensure atomic transmissions for websockets + SSL *ssl; /* SSL descriptor */ + SSL_CTX *client_ssl_ctx; /* SSL context for client connections */ + struct socket client; /* Connected client */ + time_t birth_time; /* Time when request was received */ + int64_t num_bytes_sent; /* Total bytes sent to client */ + int64_t content_len; /* Content-Length header value */ + int64_t consumed_content; /* How many bytes of content have been read */ + char *buf; /* Buffer for received data */ + char *path_info; /* PATH_INFO part of the URL */ + int must_close; /* 1 if connection must be closed */ + int buf_size; /* Buffer size */ + int request_len; /* Size of the request + headers in a buffer */ + int data_len; /* Total size of data in a buffer */ + int status_code; /* HTTP reply status code, e.g. 200 */ + int throttle; /* Throttling, bytes/sec. <= 0 means no + throttle */ + time_t last_throttle_time; /* Last time throttled data was sent */ + int64_t last_throttle_bytes;/* Bytes sent this second */ + pthread_mutex_t mutex; /* Used by mg_lock/mg_unlock to ensure atomic + transmissions for websockets */ }; -// Directory entry +static pthread_key_t sTlsKey; /* Thread local storage index */ +static int sTlsInit = 0; + +struct mg_workerTLS { + int is_master; +#if defined(_WIN32) && !defined(__SYMBIAN32__) + HANDLE pthread_cond_helper_mutex; +#endif +}; + +/* Directory entry */ struct de { struct mg_connection *conn; char *file_name; @@ -575,8 +640,8 @@ static int is_file_in_memory(struct mg_connection *conn, const char *path, size_t size = 0; if ((filep->membuf = conn->ctx->callbacks.open_file == NULL ? NULL : conn->ctx->callbacks.open_file(conn, path, &size)) != NULL) { - // NOTE: override filep->size only on success. Otherwise, it might break - // constructs like if (!mg_stat() || !mg_fopen()) ... + /* NOTE: override filep->size only on success. Otherwise, it might + break constructs like if (!mg_stat() || !mg_fopen()) ... */ filep->size = size; } return filep->membuf != NULL; @@ -644,14 +709,14 @@ static void sockaddr_to_string(char *buf, size_t len, (void *) &usa->sin.sin_addr : (void *) &usa->sin6.sin6_addr, buf, len); #elif defined(_WIN32) - // Only Windoze Vista (and newer) have inet_ntop() + /* Only Windoze Vista (and newer) have inet_ntop() */ strncpy(buf, inet_ntoa(usa->sin.sin_addr), len); #else inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len); #endif } -// Print error message to the opened error log stream. +/* Print error message to the opened error log stream. */ void mg_cry(struct mg_connection *conn, const char *fmt, ...) { char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; @@ -663,9 +728,9 @@ void mg_cry(struct mg_connection *conn, const char *fmt, ...) IGNORE_UNUSED_RESULT(vsnprintf(buf, sizeof(buf), fmt, ap)); va_end(ap); - // Do not lock when getting the callback value, here and below. - // I suppose this is fine, since function cannot disappear in the - // same way string option can. + /* Do not lock when getting the callback value, here and below. + I suppose this is fine, since function cannot disappear in the + same way string option can. */ if (conn->ctx->callbacks.log_message == NULL || conn->ctx->callbacks.log_message(conn, buf) == 0) { fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : @@ -692,8 +757,8 @@ void mg_cry(struct mg_connection *conn, const char *fmt, ...) } } -// Return fake connection structure. Used for logging, if connection -// is not applicable at the moment of logging. +/* Return fake connection structure. Used for logging, if connection + is not applicable at the moment of logging. */ static struct mg_connection *fc(struct mg_context *ctx) { static struct mg_connection fake_connection; @@ -776,10 +841,10 @@ static const char *mg_strcasestr(const char *big_str, const char *small_str) return NULL; } -// Like snprintf(), but never returns negative value, or a value -// that is larger than a supplied buffer. -// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability -// in his audit report. +/* Like snprintf(), but never returns negative value, or a value + that is larger than a supplied buffer. + Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability + in his audit report. */ static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen, const char *fmt, va_list ap) { @@ -820,10 +885,10 @@ static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, return n; } -// Skip the characters until one of the delimiters characters found. -// 0-terminate resulting word. Skip the delimiter and following whitespaces. -// Advance pointer to buffer to the next word. Return found 0-terminated word. -// Delimiters can be quoted with quotechar. +/* Skip the characters until one of the delimiters characters found. + 0-terminate resulting word. Skip the delimiter and following whitespaces. + Advance pointer to buffer to the next word. Return found 0-terminated word. + Delimiters can be quoted with quotechar. */ static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) { @@ -832,18 +897,18 @@ static char *skip_quoted(char **buf, const char *delimiters, begin_word = *buf; end_word = begin_word + strcspn(begin_word, delimiters); - // Check for quotechar + /* Check for quotechar */ if (end_word > begin_word) { p = end_word - 1; while (*p == quotechar) { - // If there is anything beyond end_word, copy it + /* If there is anything beyond end_word, copy it */ if (*end_word == '\0') { *p = '\0'; break; } else { size_t end_off = strcspn(end_word + 1, delimiters); memmove (p, end_word, end_off + 1); - p += end_off; // p must correspond to end_word - 1 + p += end_off; /* p must correspond to end_word - 1 */ end_word += end_off + 1; } } @@ -867,15 +932,15 @@ static char *skip_quoted(char **buf, const char *delimiters, return begin_word; } -// Simplified version of skip_quoted without quote char -// and whitespace == delimiters +/* Simplified version of skip_quoted without quote char + and whitespace == delimiters */ static char *skip(char **buf, const char *delimiters) { return skip_quoted(buf, delimiters, delimiters, 0); } -// Return HTTP header value, or NULL if not found. +/* Return HTTP header value, or NULL if not found. */ static const char *get_header(const struct mg_request_info *ri, const char *name) { @@ -893,37 +958,37 @@ const char *mg_get_header(const struct mg_connection *conn, const char *name) return get_header(&conn->request_info, name); } -// A helper function for traversing a comma separated list of values. -// It returns a list pointer shifted to the next value, or NULL if the end -// of the list found. -// Value is stored in val vector. If value has form "x=y", then eq_val -// vector is initialized to point to the "y" part, and val vector length -// is adjusted to point only to "x". +/* A helper function for traversing a comma separated list of values. + It returns a list pointer shifted to the next value, or NULL if the end + of the list found. + Value is stored in val vector. If value has form "x=y", then eq_val + vector is initialized to point to the "y" part, and val vector length + is adjusted to point only to "x". */ static const char *next_option(const char *list, struct vec *val, struct vec *eq_val) { if (list == NULL || *list == '\0') { - // End of the list + /* End of the list */ list = NULL; } else { val->ptr = list; if ((list = strchr(val->ptr, ',')) != NULL) { - // Comma found. Store length and shift the list ptr + /* Comma found. Store length and shift the list ptr */ val->len = list - val->ptr; list++; } else { - // This value is the last one + /* This value is the last one */ list = val->ptr + strlen(val->ptr); val->len = list - val->ptr; } if (eq_val != NULL) { - // Value has form "x=y", adjust pointers and lengths - // so that val points to "x", and eq_val points to "y". + /* Value has form "x=y", adjust pointers and lengths + so that val points to "x", and eq_val points to "y". */ eq_val->len = 0; eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); if (eq_val->ptr != NULL) { - eq_val->ptr++; // Skip over '=' character + eq_val->ptr++; /* Skip over '=' character */ eq_val->len = val->ptr + val->len - eq_val->ptr; val->len = (eq_val->ptr - val->ptr) - 1; } @@ -933,7 +998,7 @@ static const char *next_option(const char *list, struct vec *val, return list; } -// Perform case-insensitive match of string against pattern +/* Perform case-insensitive match of string against pattern */ static int match_prefix(const char *pattern, int pattern_len, const char *str) { const char *or_str; @@ -974,9 +1039,9 @@ static int match_prefix(const char *pattern, int pattern_len, const char *str) return j; } -// HTTP 1.1 assumes keep alive if "Connection:" header is not set -// This function must tolerate situations when connection info is not -// set up, for example if request parsing failed. +/* HTTP 1.1 assumes keep alive if "Connection:" header is not set + This function must tolerate situations when connection info is not + set up, for example if request parsing failed. */ static int should_keep_alive(const struct mg_connection *conn) { const char *http_version = conn->request_info.http_version; @@ -1013,7 +1078,7 @@ static void send_http_error(struct mg_connection *conn, int status, conn->ctx->callbacks.http_error(conn, status)) { buf[0] = '\0'; - // Errors 1xx, 204 and 304 MUST NOT send a body + /* Errors 1xx, 204 and 304 MUST NOT send a body */ if (status > 199 && status != 204 && status != 304) { len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason); buf[len++] = '\n'; @@ -1055,40 +1120,135 @@ static int pthread_mutex_unlock(pthread_mutex_t *mutex) return ReleaseMutex(*mutex) == 0 ? -1 : 0; } +static int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + FILETIME ft; + ULARGE_INTEGER li; + BOOL ok = FALSE; + double d; + static double perfcnt_per_sec = 0.0; + + if (tp) { + if (clk_id == CLOCK_REALTIME) { + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */ + tp->tv_sec = (time_t)(li.QuadPart / 10000000); + tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; + ok = TRUE; + } else if (clk_id == CLOCK_MONOTONIC) { + if (perfcnt_per_sec==0) { + QueryPerformanceFrequency((LARGE_INTEGER *) &li); + perfcnt_per_sec = 1.0 / li.QuadPart; + } + if (perfcnt_per_sec!=0) { + QueryPerformanceCounter((LARGE_INTEGER *) &li); + d = li.QuadPart * perfcnt_per_sec; + tp->tv_sec = (time_t)d; + d -= tp->tv_sec; + tp->tv_nsec = (long)(d*1.0E9); + ok = TRUE; + } + } + } + + return ok ? 0 : -1; +} + static int pthread_cond_init(pthread_cond_t *cv, const void *unused) { (void) unused; - cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL); - cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL); - return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1; + InitializeCriticalSection(&cv->threadIdSec); + cv->waitingthreadcount = 0; + cv->waitingthreadhdls = calloc(MAX_WORKER_THREADS, sizeof(pthread_t)); + return (cv->waitingthreadhdls!=NULL) ? 0 : -1; +} + +static int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mutex, const struct timespec * abstime) +{ + struct mg_workerTLS * tls = (struct mg_workerTLS *)TlsGetValue(sTlsKey); + int ok; + struct timespec tsnow; + int64_t nsnow, nswaitabs, nswaitrel; + DWORD mswaitrel; + + EnterCriticalSection(&cv->threadIdSec); + assert(cv->waitingthreadcount < MAX_WORKER_THREADS); + cv->waitingthreadhdls[cv->waitingthreadcount] = tls->pthread_cond_helper_mutex; + cv->waitingthreadcount++; + LeaveCriticalSection(&cv->threadIdSec); + + if (abstime) { + clock_gettime(CLOCK_REALTIME, &tsnow); + nsnow = (((uint64_t)tsnow.tv_sec)<<32) + tsnow.tv_nsec; + nswaitabs = (((uint64_t)abstime->tv_sec)<<32) + abstime->tv_nsec; + nswaitrel = nswaitabs - nsnow; + if (nswaitrel<0) nswaitrel=0; + mswaitrel = (DWORD)(nswaitrel / 1000000); + } else { + mswaitrel = INFINITE; + } + + pthread_mutex_unlock(mutex); + ok = (WAIT_OBJECT_0 == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel)); + pthread_mutex_lock(mutex); + + return ok ? 0 : -1; } static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) { - HANDLE handles[] = {cv->signal, cv->broadcast}; - ReleaseMutex(*mutex); - WaitForMultipleObjects(2, handles, FALSE, INFINITE); - return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1; + return pthread_cond_timedwait(cv, mutex, NULL); } static int pthread_cond_signal(pthread_cond_t *cv) { - return SetEvent(cv->signal) == 0 ? -1 : 0; + int i; + HANDLE wkup = NULL; + BOOL ok = FALSE; + + EnterCriticalSection(&cv->threadIdSec); + if (cv->waitingthreadcount) { + wkup = cv->waitingthreadhdls[0]; + ok = SetEvent(wkup); + + for (i=1; iwaitingthreadcount; i++) { + cv->waitingthreadhdls[i-1] = cv->waitingthreadhdls[i]; + } + cv->waitingthreadcount--; + + assert(ok); + } + LeaveCriticalSection(&cv->threadIdSec); + + return ok ? 0 : 1; } static int pthread_cond_broadcast(pthread_cond_t *cv) { - // Implementation with PulseEvent() has race condition, see - // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html - return PulseEvent(cv->broadcast) == 0 ? -1 : 0; + EnterCriticalSection(&cv->threadIdSec); + while (cv->waitingthreadcount) { + pthread_cond_signal(cv); + } + LeaveCriticalSection(&cv->threadIdSec); + + return 0; } static int pthread_cond_destroy(pthread_cond_t *cv) { - return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1; + EnterCriticalSection(&cv->threadIdSec); + assert(cv->waitingthreadcount==0); + cv->waitingthreadhdls = 0; + free(cv->waitingthreadhdls); + LeaveCriticalSection(&cv->threadIdSec); + DeleteCriticalSection(&cv->threadIdSec); + + return 0; } -// For Windows, change all slashes to backslashes in path names. +/* For Windows, change all slashes to backslashes in path names. */ static void change_slashes_to_backslashes(char *path) { int i; @@ -1096,7 +1256,7 @@ static void change_slashes_to_backslashes(char *path) for (i = 0; path[i] != '\0'; i++) { if (path[i] == '/') path[i] = '\\'; - // i > 0 check is to preserve UNC paths, like \\server\file.txt + /* i > 0 check is to preserve UNC paths, like \\server\file.txt */ if (path[i] == '\\' && i > 0) while (path[i + 1] == '\\' || path[i + 1] == '/') (void) memmove(path + i + 1, @@ -1104,8 +1264,8 @@ static void change_slashes_to_backslashes(char *path) } } -// Encode 'path' which is assumed UTF-8 string, into UNICODE string. -// wbuf and wbuf_len is a target buffer and its length. +/* Encode 'path' which is assumed UTF-8 string, into UNICODE string. + wbuf and wbuf_len is a target buffer and its length. */ static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) { char buf[PATH_MAX], buf2[PATH_MAX]; @@ -1113,8 +1273,8 @@ static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) mg_strlcpy(buf, path, sizeof(buf)); change_slashes_to_backslashes(buf); - // Convert to Unicode and back. If doubly-converted string does not - // match the original, something is fishy, reject. + /* Convert to Unicode and back. If doubly-converted string does not + match the original, something is fishy, reject. */ memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), @@ -1163,7 +1323,7 @@ static struct tm *localtime(const time_t *ptime, struct tm *ptm) ptm->tm_hour = st.wHour; ptm->tm_min = st.wMinute; ptm->tm_sec = st.wSecond; - ptm->tm_yday = 0; // hope nobody uses this + ptm->tm_yday = 0; /* hope nobody uses this */ ptm->tm_isdst = GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0; @@ -1172,7 +1332,7 @@ static struct tm *localtime(const time_t *ptime, struct tm *ptm) static struct tm *gmtime(const time_t *ptime, struct tm *ptm) { - // FIXME(lsm): fix this. + /* FIXME(lsm): fix this. */ return localtime(ptime, ptm); } @@ -1184,10 +1344,10 @@ static size_t strftime(char *dst, size_t dst_size, const char *fmt, } #endif -// Windows happily opens files with some garbage at the end of file name. -// For example, fopen("a.cgi ", "r") on Windows successfully opens -// "a.cgi", despite one would expect an error back. -// This function returns non-0 if path ends with some garbage. +/* Windows happily opens files with some garbage at the end of file name. + For example, fopen("a.cgi ", "r") on Windows successfully opens + "a.cgi", despite one would expect an error back. + This function returns non-0 if path ends with some garbage. */ static int path_cannot_disclose_cgi(const char *path) { static const char *allowed_last_characters = "_-"; @@ -1209,9 +1369,10 @@ static int mg_stat(struct mg_connection *conn, const char *path, info.ftLastWriteTime.dwLowDateTime, info.ftLastWriteTime.dwHighDateTime); filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - // If file name is fishy, reset the file structure and return error. - // Note it is important to reset, not just return the error, cause - // functions like is_file_opened() check the struct. + /* If file name is fishy, reset the file structure and return + error. + Note it is important to reset, not just return the error, cause + functions like is_file_opened() check the struct. */ if (!filep->is_directory && !path_cannot_disclose_cgi(path)) { memset(filep, 0, sizeof(*filep)); } @@ -1242,7 +1403,7 @@ static int mg_mkdir(const char *path, int mode) return CreateDirectoryW(wbuf, NULL) ? 0 : -1; } -// Implementation of POSIX opendir/closedir/readdir for Windows. +/* Implementation of POSIX opendir/closedir/readdir for Windows. */ static DIR * opendir(const char *name) { DIR *dir = NULL; @@ -1344,7 +1505,7 @@ static int poll(struct pollfd *pfd, int n, int milliseconds) return result; } -#endif // HAVE_POLL +#endif /* HAVE_POLL */ static void set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be null */) { @@ -1354,7 +1515,12 @@ static void set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be int mg_start_thread(mg_thread_func_t f, void *p) { +#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) + /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 */ + return (long)_beginthread((void (__cdecl *)(void *)) f, USE_STACK_SIZE, p) == -1L ? -1 : 0; +#else return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0; +#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ } /* Start a thread storing the thread context. */ @@ -1455,7 +1621,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, memset(&si, 0, sizeof(si)); si.cb = sizeof(si); - // TODO(lsm): redirect CGI errors to the error log file + /* TODO(lsm): redirect CGI errors to the error log file */ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; @@ -1465,12 +1631,12 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); - // If CGI file is a script, try to read the interpreter line + /* If CGI file is a script, try to read the interpreter line */ interp = conn->ctx->config[CGI_INTERPRETER]; if (interp == NULL) { buf[0] = buf[1] = '\0'; - // Read the first line of the script into the buffer + /* Read the first line of the script into the buffer */ snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog); if (mg_fopen(conn, cmdline, "r", &file)) { p = (char *) file.membuf; @@ -1511,7 +1677,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, return (pid_t) pi.hProcess; } -#endif // !NO_CGI +#endif /* !NO_CGI */ static int set_non_blocking_mode(SOCKET sock) { @@ -1554,10 +1720,11 @@ int mg_start_thread(mg_thread_func_t func, void *param) (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); -#if defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 - // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 +#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) + /* Compile-time option to control stack size, + e.g. -DUSE_STACK_SIZE=16384 */ (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE); -#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */ +#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ result = pthread_create(&thread_id, &attr, func, param); pthread_attr_destroy(&attr); @@ -1576,8 +1743,9 @@ static int mg_start_thread_with_id(mg_thread_func_t func, void *param, (void) pthread_attr_init(&attr); -#if defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 - // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 +#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) + /* Compile-time option to control stack size, + e.g. -DUSE_STACK_SIZE=16384 */ (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE); #endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */ @@ -1610,10 +1778,10 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, (void) envblk; if ((pid = fork()) == -1) { - // Parent + /* Parent */ send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO)); } else if (pid == 0) { - // Child + /* Child */ if (chdir(dir) != 0) { mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); } else if (dup2(fdin, 0) == -1) { @@ -1621,15 +1789,16 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, } else if (dup2(fdout, 1) == -1) { mg_cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO)); } else { - // Not redirecting stderr to stdout, to avoid output being littered - // with the error messages. + /* Not redirecting stderr to stdout, to avoid output being littered + with the error messages. */ (void) close(fdin); (void) close(fdout); - // After exec, all signal handlers are restored to their default values, - // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's - // implementation, SIGCHLD's handler will leave unchanged after exec - // if it was set to be ignored. Restore it to default action. + /* After exec, all signal handlers are restored to their default + values, with one exception of SIGCHLD. According to + POSIX.1-2001 and Linux's implementation, SIGCHLD's handler will + leave unchanged after exec if it was set to be ignored. Restore + it to default action. */ signal(SIGCHLD, SIG_DFL); interp = conn->ctx->config[CGI_INTERPRETER]; @@ -1647,7 +1816,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, return pid; } -#endif // !NO_CGI +#endif /* !NO_CGI */ static int set_non_blocking_mode(SOCKET sock) { @@ -1658,21 +1827,21 @@ static int set_non_blocking_mode(SOCKET sock) return 0; } -#endif // _WIN32 +#endif /* _WIN32 */ -// Write data to the IO channel - opened file descriptor, socket or SSL -// descriptor. Return number of bytes written. +/* Write data to the IO channel - opened file descriptor, socket or SSL + descriptor. Return number of bytes written. */ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len) { int64_t sent; int n, k; - (void) ssl; // Get rid of warning + (void) ssl; /* Get rid of warning */ sent = 0; while (sent < len) { - // How many bytes we send in this iteration + /* How many bytes we send in this iteration */ k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent); #ifndef NO_SSL @@ -1697,16 +1866,17 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, return sent; } -// Read from IO channel - opened file descriptor, socket, or SSL descriptor. -// Return negative value on error, or number of bytes read on success. +/* Read from IO channel - opened file descriptor, socket, or SSL descriptor. + Return negative value on error, or number of bytes read on success. */ static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) { int nread; if (fp != NULL) { - // Use read() instead of fread(), because if we're reading from the CGI - // pipe, fread() may block until IO buffer is filled up. We cannot afford - // to block and must pass all read bytes immediately to the client. + /* Use read() instead of fread(), because if we're reading from the + CGI pipe, fread() may block until IO buffer is filled up. We cannot + afford to block and must pass all read bytes immediately to the + client. */ nread = read(fileno(fp), buf, (size_t) len); #ifndef NO_SSL } else if (conn->ssl != NULL) { @@ -1726,10 +1896,10 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) while (len > 0 && conn->ctx->stop_flag == 0) { n = pull(fp, conn, buf + nread, len); if (n < 0) { - nread = n; // Propagate the error + nread = n; /* Propagate the error */ break; } else if (n == 0) { - break; // No more data to read + break; /* No more data to read */ } else { conn->consumed_content += n; nread += n; @@ -1745,7 +1915,7 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) int n, buffered_len, nread; const char *body; - // If Content-Length is not set, read until socket is closed + /* If Content-Length is not set, read until socket is closed */ if (conn->consumed_content == 0 && conn->content_len == 0) { conn->content_len = INT64_MAX; conn->must_close = 1; @@ -1753,13 +1923,13 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) nread = 0; if (conn->consumed_content < conn->content_len) { - // Adjust number of bytes to read. + /* Adjust number of bytes to read. */ int64_t to_read = conn->content_len - conn->consumed_content; if (to_read < (int64_t) len) { len = (size_t) to_read; } - // Return buffered data + /* Return buffered data */ body = conn->buf + conn->request_len + conn->consumed_content; buffered_len = (int)(&conn->buf[conn->data_len] - body); if (buffered_len > 0) { @@ -1773,7 +1943,8 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) buf = (char *) buf + buffered_len; } - // We have returned all buffered data. Read new data from the remote socket. + /* We have returned all buffered data. Read new data from the remote + socket. */ n = pull_all(NULL, conn, (char *) buf, (int) len); nread = n >= 0 ? nread + n : n; } @@ -1819,7 +1990,7 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len) return (int) total; } -// Alternative alloc_vprintf() for non-compliant C runtimes +/* Alternative alloc_vprintf() for non-compliant C runtimes */ static int alloc_vprintf2(char **buf, const char *fmt, va_list ap) { va_list ap_copy; @@ -1839,33 +2010,35 @@ static int alloc_vprintf2(char **buf, const char *fmt, va_list ap) return len; } -// Print message to buffer. If buffer is large enough to hold the message, -// return buffer. If buffer is to small, allocate large enough buffer on heap, -// and return allocated buffer. +/* Print message to buffer. If buffer is large enough to hold the message, + return buffer. If buffer is to small, allocate large enough buffer on heap, + and return allocated buffer. */ static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { va_list ap_copy; int len; - // Windows is not standard-compliant, and vsnprintf() returns -1 if - // buffer is too small. Also, older versions of msvcrt.dll do not have - // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. - // Therefore, we make two passes: on first pass, get required message length. - // On second pass, actually print the message. + /* Windows is not standard-compliant, and vsnprintf() returns -1 if + buffer is too small. Also, older versions of msvcrt.dll do not have + _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. + Therefore, we make two passes: on first pass, get required message + length. + On second pass, actually print the message. */ va_copy(ap_copy, ap); len = vsnprintf(NULL, 0, fmt, ap_copy); va_end(ap_copy); if (len < 0) { - // C runtime is not standard compliant, vsnprintf() returned -1. - // Switch to alternative code path that uses incremental allocations. + /* C runtime is not standard compliant, vsnprintf() returned -1. + Switch to alternative code path that uses incremental allocations. + */ va_copy(ap_copy, ap); len = alloc_vprintf2(buf, fmt, ap); va_end(ap_copy); } else if (len > (int) size && (size = len + 1) > 0 && (*buf = (char *) malloc(size)) == NULL) { - len = -1; // Allocation failed, mark failure + len = -1; /* Allocation failed, mark failure */ } else { va_copy(ap_copy, ap); IGNORE_UNUSED_RESULT(vsnprintf(*buf, size, fmt, ap_copy)); @@ -1925,7 +2098,7 @@ int mg_url_decode(const char *src, int src_len, char *dst, } } - dst[j] = '\0'; // Null-terminate the destination + dst[j] = '\0'; /* Null-terminate the destination */ return i >= src_len ? j : -1; } @@ -1954,25 +2127,26 @@ int mg_get_var2(const char *data, size_t data_len, const char *name, len = -1; dst[0] = '\0'; - // data is "var1=val1&var2=val2...". Find variable first + /* data is "var1=val1&var2=val2...". Find variable first */ for (p = data; p + name_len < e; p++) { if ((p == data || p[-1] == '&') && p[name_len] == '=' && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) { - // Point p to variable value + /* Point p to variable value */ p += name_len + 1; - // Point s to the end of the value + /* Point s to the end of the value */ s = (const char *) memchr(p, '&', (size_t)(e - p)); if (s == NULL) { s = e; } assert(s >= p); - // Decode variable into destination buffer + /* Decode variable into destination buffer */ len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1); - // Redirect error code from -1 to -2 (destination buffer too small). + /* Redirect error code from -1 to -2 (destination buffer too + small). */ if (len == -1) { len = -2; } @@ -2035,9 +2209,9 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, char gz_path[PATH_MAX]; char const* accept_encoding; - // Using buf_len - 1 because memmove() for PATH_INFO may shift part - // of the path one byte on the right. - // If document_root is NULL, leave the file empty. + /* Using buf_len - 1 because memmove() for PATH_INFO may shift part + of the path one byte on the right. + If document_root is NULL, leave the file empty. */ mg_snprintf(conn, buf, buf_len - 1, "%s%s", root == NULL ? "" : root, root == NULL ? "" : uri); @@ -2053,12 +2227,12 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, if (mg_stat(conn, buf, filep)) return; - // if we can't find the actual file, look for the file - // with the same name but a .gz extension. If we find it, - // use that and set the gzipped flag in the file struct - // to indicate that the response need to have the content- - // encoding: gzip header - // we can only do this if the browser declares support + /* if we can't find the actual file, look for the file + with the same name but a .gz extension. If we find it, + use that and set the gzipped flag in the file struct + to indicate that the response need to have the content- + encoding: gzip header + we can only do this if the browser declares support */ if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { if (strstr(accept_encoding,"gzip") != NULL) { snprintf(gz_path, sizeof(gz_path), "%s.gz", buf); @@ -2069,20 +2243,21 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, } } - // Support PATH_INFO for CGI scripts. + /* Support PATH_INFO for CGI scripts. */ for (p = buf + strlen(buf); p > buf + 1; p--) { if (*p == '/') { *p = '\0'; if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 && mg_stat(conn, buf, filep)) { - // Shift PATH_INFO block one character right, e.g. - // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" - // conn->path_info is pointing to the local variable "path" declared - // in handle_request(), so PATH_INFO is not valid after - // handle_request returns. + /* Shift PATH_INFO block one character right, e.g. + "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" + conn->path_info is pointing to the local variable "path" + declared in handle_request(), so PATH_INFO is not valid + after handle_request returns. */ conn->path_info = p + 1; - memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0 + memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for + trailing \0 */ p[1] = '/'; break; } else { @@ -2092,22 +2267,23 @@ static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, } } -// Check whether full request is buffered. Return: -// -1 if request is malformed -// 0 if request is not yet fully buffered -// >0 actual request length, including last \r\n\r\n +/* Check whether full request is buffered. Return: + -1 if request is malformed + 0 if request is not yet fully buffered + >0 actual request length, including last \r\n\r\n */ static int get_request_len(const char *buf, int buflen) { const char *s, *e; int len = 0; for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) - // Control characters are not allowed but >=128 is. + /* Control characters are not allowed but >=128 is. */ if (!isprint(* (const unsigned char *) s) && *s != '\r' && *s != '\n' && * (const unsigned char *) s < 128) { len = -1; - break; // [i_a] abort scan as soon as one malformed character is found; - // don't let subsequent \r\n\r\n win us over anyhow + break; /* [i_a] abort scan as soon as one malformed character is + found; */ + /* don't let subsequent \r\n\r\n win us over anyhow */ } else if (s[0] == '\n' && s[1] == '\n') { len = (int) (s - buf) + 2; } else if (s[0] == '\n' && &s[1] < e && @@ -2118,7 +2294,7 @@ static int get_request_len(const char *buf, int buflen) return len; } -// Convert month to the month number. Return -1 on error, or month number +/* Convert month to the month number. Return -1 on error, or month number */ static int get_month_index(const char *s) { size_t i; @@ -2135,7 +2311,7 @@ static int num_leap_years(int year) return year / 4 - year / 100 + year / 400; } -// Parse UTC date-time string, and return the corresponding time_t value. +/* Parse UTC date-time string, and return the corresponding time_t value. */ static time_t parse_date_string(const char *datetime) { static const unsigned short days_before_month[] = { @@ -2165,8 +2341,8 @@ static time_t parse_date_string(const char *datetime) return result; } -// Protect against directory disclosure attack by removing '..', -// excessive '/' and '\' characters +/* Protect against directory disclosure attack by removing '..', + excessive '/' and '\' characters */ static void remove_double_dots_and_double_slashes(char *s) { char *p = s; @@ -2174,7 +2350,7 @@ static void remove_double_dots_and_double_slashes(char *s) while (*s != '\0') { *p++ = *s++; if (s[-1] == '/' || s[-1] == '\\') { - // Skip all following slashes, backslashes and double-dots + /* Skip all following slashes, backslashes and double-dots */ while (s[0] != '\0') { if (s[0] == '/' || s[0] == '\\') { s++; @@ -2194,54 +2370,89 @@ static const struct { size_t ext_len; const char *mime_type; } builtin_mime_types[] = { - {".html", 5, "text/html"}, - {".htm", 4, "text/html"}, - {".shtm", 5, "text/html"}, - {".shtml", 6, "text/html"}, - {".css", 4, "text/css"}, - {".js", 3, "application/x-javascript"}, - {".ico", 4, "image/x-icon"}, - {".gif", 4, "image/gif"}, - {".jpg", 4, "image/jpeg"}, - {".jpeg", 5, "image/jpeg"}, - {".png", 4, "image/png"}, - {".svg", 4, "image/svg+xml"}, - {".txt", 4, "text/plain"}, + /* IANA registered MIME types (http://www.iana.org/assignments/media-types) + application types */ + {".doc", 4, "application/msword"}, + {".eps", 4, "application/postscript"}, + {".exe", 4, "application/octet-stream"}, + {".js", 3, "application/javascript"}, + {".json", 5, "application/json"}, + {".pdf", 4, "application/pdf"}, + {".ps", 3, "application/postscript"}, + {".rtf", 4, "application/rtf"}, + {".xhtml", 6, "application/xhtml+xml"}, + {".xsl", 4, "application/xml"}, + {".xslt", 5, "application/xml"}, + + /* audio */ + {".mp3", 4, "audio/mpeg"}, + {".oga", 4, "audio/ogg"}, + {".ogg", 4, "audio/ogg"}, + + /* image */ + {".gif", 4, "image/gif"}, + {".ief", 4, "image/ief"}, + {".jpeg", 5, "image/jpeg"}, + {".jpg", 4, "image/jpeg"}, + {".jpm", 4, "image/jpm"}, + {".jpx", 4, "image/jpx"}, + {".png", 4, "image/png"}, + {".svg", 4, "image/svg+xml"}, + {".tif", 4, "image/tiff"}, + {".tiff", 5, "image/tiff"}, + + /* model */ + {".wrl", 4, "model/vrml"}, + + /* text */ + {".css", 4, "text/css"}, + {".csv", 4, "text/csv"}, + {".htm", 4, "text/html"}, + {".html", 5, "text/html"}, + {".sgm", 4, "text/sgml"}, + {".shtm", 5, "text/html"}, + {".shtml", 6, "text/html"}, + {".txt", 4, "text/plain"}, + {".xml", 4, "text/xml"}, + + /* video */ + {".mov", 4, "video/quicktime"}, + {".mp4", 4, "video/mp4"}, + {".mpeg", 5, "video/mpeg"}, + {".mpg", 4, "video/mpeg"}, + {".ogv", 4, "video/ogg"}, + {".qt", 3, "video/quicktime"}, + + /* not registered types + (http://reference.sitepoint.com/html/mime-types-full, + http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */ + {".arj", 4, "application/x-arj-compressed"}, + {".gz", 3, "application/x-gunzip"}, + {".rar", 4, "application/x-arj-compressed"}, + {".swf", 4, "application/x-shockwave-flash"}, + {".tar", 4, "application/x-tar"}, + {".tgz", 4, "application/x-tar-gz"}, {".torrent", 8, "application/x-bittorrent"}, - {".wav", 4, "audio/x-wav"}, - {".mp3", 4, "audio/x-mp3"}, - {".mid", 4, "audio/mid"}, - {".m3u", 4, "audio/x-mpegurl"}, - {".ogg", 4, "audio/ogg"}, - {".ram", 4, "audio/x-pn-realaudio"}, - {".xml", 4, "text/xml"}, - {".json", 5, "text/json"}, - {".xslt", 5, "application/xml"}, - {".xsl", 4, "application/xml"}, - {".ra", 3, "audio/x-pn-realaudio"}, - {".doc", 4, "application/msword"}, - {".exe", 4, "application/octet-stream"}, - {".zip", 4, "application/x-zip-compressed"}, - {".xls", 4, "application/excel"}, - {".tgz", 4, "application/x-tar-gz"}, - {".tar", 4, "application/x-tar"}, - {".gz", 3, "application/x-gunzip"}, - {".arj", 4, "application/x-arj-compressed"}, - {".rar", 4, "application/x-arj-compressed"}, - {".rtf", 4, "application/rtf"}, - {".pdf", 4, "application/pdf"}, - {".swf", 4, "application/x-shockwave-flash"}, - {".mpg", 4, "video/mpeg"}, - {".webm", 5, "video/webm"}, - {".mpeg", 5, "video/mpeg"}, - {".mov", 4, "video/quicktime"}, - {".mp4", 4, "video/mp4"}, - {".m4v", 4, "video/x-m4v"}, - {".asf", 4, "video/x-ms-asf"}, - {".avi", 4, "video/x-msvideo"}, - {".bmp", 4, "image/bmp"}, - {".ttf", 4, "application/x-font-ttf"}, - {NULL, 0, NULL} + {".ppt", 4, "application/x-mspowerpoint"}, + {".xls", 4, "application/x-msexcel"}, + {".zip", 4, "application/x-zip-compressed"}, + {".aac", 4, "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */ + {".aif", 4, "audio/x-aif"}, + {".m3u", 4, "audio/x-mpegurl"}, + {".mid", 4, "audio/x-midi"}, + {".ra", 3, "audio/x-pn-realaudio"}, + {".ram", 4, "audio/x-pn-realaudio"}, + {".wav", 4, "audio/x-wav"}, + {".bmp", 4, "image/bmp"}, + {".ico", 4, "image/x-icon"}, + {".pct", 4, "image/x-pct"}, + {".pict", 5, "image/pict"}, + {".rgb", 4, "image/x-rgb"}, + {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */ + {".asf", 4, "video/x-ms-asf"}, + {".avi", 4, "video/x-msvideo"}, + {".m4v", 4, "video/x-m4v"}, + {NULL, 0, NULL} }; const char *mg_get_builtin_mime_type(const char *path) @@ -2262,8 +2473,8 @@ const char *mg_get_builtin_mime_type(const char *path) return "text/plain"; } -// Look at the "path" extension and figure what mime type it has. -// Store mime type in the vector. +/* Look at the "path" extension and figure what mime type it has. + Store mime type in the vector. */ static void get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec) { @@ -2273,11 +2484,11 @@ static void get_mime_type(struct mg_context *ctx, const char *path, path_len = strlen(path); - // Scan user-defined mime types first, in case user wants to - // override default mime types. + /* Scan user-defined mime types first, in case user wants to + override default mime types. */ list = ctx->config[EXTRA_MIME_TYPES]; while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { - // ext now points to the path suffix + /* ext now points to the path suffix */ ext = path + path_len - ext_vec.len; if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { *vec = mime_vec; @@ -2289,8 +2500,8 @@ static void get_mime_type(struct mg_context *ctx, const char *path, vec->len = strlen(vec->ptr); } -// Stringify binary data. Output buffer must be twice as big as input, -// because each byte takes 2 bytes in string representation +/* Stringify binary data. Output buffer must be twice as big as input, + because each byte takes 2 bytes in string representation */ static void bin2str(char *to, const unsigned char *p, size_t len) { static const char *hex = "0123456789abcdef"; @@ -2302,7 +2513,7 @@ static void bin2str(char *to, const unsigned char *p, size_t len) *to = '\0'; } -// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. +/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */ char *mg_md5(char buf[33], ...) { md5_byte_t hash[16]; @@ -2323,24 +2534,24 @@ char *mg_md5(char buf[33], ...) return buf; } -// Check the user's password, return 1 if OK +/* Check the user's password, return 1 if OK */ static int check_password(const char *method, const char *ha1, const char *uri, const char *nonce, const char *nc, const char *cnonce, const char *qop, const char *response) { char ha2[32 + 1], expected_response[32 + 1]; - // Some of the parameters may be NULL + /* Some of the parameters may be NULL */ if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL || qop == NULL || response == NULL) { return 0; } - // NOTE(lsm): due to a bug in MSIE, we do not compare the URI - // TODO(lsm): check for authentication timeout - if (// strcmp(dig->uri, c->ouri) != 0 || + /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */ + /* TODO(lsm): check for authentication timeout */ + if (/* strcmp(dig->uri, c->ouri) != 0 || */ strlen(response) != 32 - // || now - strtoul(dig->nonce, NULL, 10) > 3600 + /* || now - strtoul(dig->nonce, NULL, 10) > 3600 */ ) { return 0; } @@ -2352,8 +2563,8 @@ static int check_password(const char *method, const char *ha1, const char *uri, return mg_strcasecmp(response, expected_response) == 0; } -// Use the global passwords file, if specified by auth_gpass option, -// or search for .htpasswd in the requested directory. +/* Use the global passwords file, if specified by auth_gpass option, + or search for .htpasswd in the requested directory. */ static void open_auth_file(struct mg_connection *conn, const char *path, struct file *filep) { @@ -2362,14 +2573,16 @@ static void open_auth_file(struct mg_connection *conn, const char *path, struct file file = STRUCT_FILE_INITIALIZER; if (gpass != NULL) { - // Use global passwords file + /* Use global passwords file */ if (!mg_fopen(conn, gpass, "r", filep)) { #ifdef DEBUG mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO)); #endif } - // Important: using local struct file to test path for is_directory flag. - // If filep is used, mg_stat() makes it appear as if auth file was opened. + /* Important: using local struct file to test path for is_directory + flag. + If filep is used, mg_stat() makes it appear as if auth file was + opened. */ } else if (mg_stat(conn, path, &file) && file.is_directory) { mg_snprintf(conn, name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME); @@ -2379,7 +2592,7 @@ static void open_auth_file(struct mg_connection *conn, const char *path, #endif } } else { - // Try to find .htpasswd in requested directory. + /* Try to find .htpasswd in requested directory. */ for (p = path, e = p + strlen(p) - 1; e > p; e--) if (e[0] == '/') break; @@ -2393,12 +2606,12 @@ static void open_auth_file(struct mg_connection *conn, const char *path, } } -// Parsed Authorization header +/* Parsed Authorization header */ struct ah { char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; }; -// Return 1 on success. Always initializes the ah structure. +/* Return 1 on success. Always initializes the ah structure. */ static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah) { @@ -2411,18 +2624,18 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, return 0; } - // Make modifiable copy of the auth header + /* Make modifiable copy of the auth header */ (void) mg_strlcpy(buf, auth_header + 7, buf_size); s = buf; - // Parse authorization header + /* Parse authorization header */ for (;;) { - // Gobble initial spaces + /* Gobble initial spaces */ while (isspace(* (unsigned char *) s)) { s++; } name = skip_quoted(&s, "=", " ", 0); - // Value is either quote-delimited, or ends at first comma or space. + /* Value is either quote-delimited, or ends at first comma or space. */ if (s[0] == '\"') { s++; value = skip_quoted(&s, "\"", " ", '\\'); @@ -2430,7 +2643,8 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, s++; } } else { - value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces + value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses + spaces */ } if (*name == '\0') { break; @@ -2453,7 +2667,7 @@ static int parse_auth_header(struct mg_connection *conn, char *buf, } } - // CGI needs it as REMOTE_USER + /* CGI needs it as REMOTE_USER */ if (ah->user != NULL) { conn->request_info.remote_user = mg_strdup(ah->user); } else { @@ -2471,11 +2685,13 @@ static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p) if (filep->membuf != NULL && *p != NULL) { memend = (char *) &filep->membuf[filep->size]; - eof = (char *) memchr(*p, '\n', memend - *p); // Search for \n from p till the end of stream + eof = (char *) memchr(*p, '\n', memend - *p); /* Search for \n from p + till the end of + stream */ if (eof != NULL) { - eof += 1; // Include \n + eof += 1; /* Include \n */ } else { - eof = memend; // Copy remaining data + eof = memend; /* Copy remaining data */ } len = (size_t) (eof - *p) > size - 1 ? size - 1 : (size_t) (eof - *p); memcpy(buf, *p, len); @@ -2489,7 +2705,7 @@ static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p) } } -// Authorize against the opened passwords file. Return 1 if authorized. +/* Authorize against the opened passwords file. Return 1 if authorized. */ static int authorize(struct mg_connection *conn, struct file *filep) { struct ah ah; @@ -2499,7 +2715,7 @@ static int authorize(struct mg_connection *conn, struct file *filep) return 0; } - // Loop over passwords file + /* Loop over passwords file */ p = (char *) filep->membuf; while (mg_fgets(line, sizeof(line), filep, &p) != NULL) { if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) { @@ -2515,7 +2731,7 @@ static int authorize(struct mg_connection *conn, struct file *filep) return 0; } -// Return 1 if request is authorised, 0 otherwise. +/* Return 1 if request is authorised, 0 otherwise. */ static int check_authorization(struct mg_connection *conn, const char *path) { char fname[PATH_MAX]; @@ -2584,7 +2800,7 @@ int mg_modify_passwords_file(const char *fname, const char *domain, found = 0; fp = fp2 = NULL; - // Regard empty password as no password - remove user record. + /* Regard empty password as no password - remove user record. */ if (pass != NULL && pass[0] == '\0') { pass = NULL; } @@ -2592,12 +2808,12 @@ int mg_modify_passwords_file(const char *fname, const char *domain, (void) snprintf(tmp, sizeof(tmp) - 1, "%s.tmp", fname); tmp[sizeof(tmp) - 1] = 0; - // Create the file if does not exist + /* Create the file if does not exist */ if ((fp = fopen(fname, "a+")) != NULL) { (void) fclose(fp); } - // Open the given file and temporary file + /* Open the given file and temporary file */ if ((fp = fopen(fname, "r")) == NULL) { return 0; } else if ((fp2 = fopen(tmp, "w+")) == NULL) { @@ -2605,7 +2821,7 @@ int mg_modify_passwords_file(const char *fname, const char *domain, return 0; } - // Copy the stuff to temporary file + /* Copy the stuff to temporary file */ while (fgets(line, sizeof(line), fp) != NULL) { if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) { continue; @@ -2622,17 +2838,17 @@ int mg_modify_passwords_file(const char *fname, const char *domain, } } - // If new user, just add it + /* If new user, just add it */ if (!found && pass != NULL) { mg_md5(ha1, user, ":", domain, ":", pass, NULL); fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); } - // Close files + /* Close files */ fclose(fp); fclose(fp2); - // Put the temp file in place of real file + /* Put the temp file in place of real file */ IGNORE_UNUSED_RESULT(remove(fname)); IGNORE_UNUSED_RESULT(rename(tmp, fname)); @@ -2650,7 +2866,7 @@ static SOCKET conn2(struct mg_context *ctx /* may be null */, const char *host, snprintf(ebuf, ebuf_len, "%s", "NULL host"); } else if (use_ssl && SSLv23_client_method == NULL) { snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized"); - // TODO(lsm): use something threadsafe instead of gethostbyname() + /* TODO(lsm): use something threadsafe instead of gethostbyname() */ } else if ((he = gethostbyname(host)) == NULL) { snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO)); } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { @@ -2704,8 +2920,8 @@ static void print_dir_entry(struct de *de) if (de->file.is_directory) { mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]"); } else { - // We use (signed) cast below because MSVC 6 compiler cannot - // convert unsigned __int64 to double. Sigh. + /* We use (signed) cast below because MSVC 6 compiler cannot + convert unsigned __int64 to double. Sigh. */ if (de->file.size < 1024) { mg_snprintf(de->conn, size, sizeof(size), "%d", (int) de->file.size); } else if (de->file.size < 0x100000) { @@ -2734,10 +2950,10 @@ static void print_dir_entry(struct de *de) de->file_name, de->file.is_directory ? "/" : "", mod, size); } -// This function is called from send_directory() and used for -// sorting directory entries by size, or name, or modification time. -// On windows, __cdecl specification is needed in case if project is built -// with __stdcall convention. qsort always requires __cdels callback. +/* This function is called from send_directory() and used for + sorting directory entries by size, or name, or modification time. + On windows, __cdecl specification is needed in case if project is built + with __stdcall convention. qsort always requires __cdels callback. */ static int WINCDECL compare_dir_entries(const void *p1, const void *p2) { const struct de *a = (const struct de *) p1, *b = (const struct de *) p2; @@ -2749,9 +2965,9 @@ static int WINCDECL compare_dir_entries(const void *p1, const void *p2) } if (a->file.is_directory && !b->file.is_directory) { - return -1; // Always put directories on top + return -1; /* Always put directories on top */ } else if (!a->file.is_directory && b->file.is_directory) { - return 1; // Always put directories on top + return 1; /* Always put directories on top */ } else if (*query_string == 'n') { cmp_result = strcmp(a->file_name, b->file_name); } else if (*query_string == 's') { @@ -2787,7 +3003,7 @@ static int scan_directory(struct mg_connection *conn, const char *dir, de.conn = conn; while ((dp = readdir(dirp)) != NULL) { - // Do not show current dir and hidden files + /* Do not show current dir and hidden files */ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") || must_hide_file(conn, dp->d_name)) { @@ -2796,11 +3012,11 @@ static int scan_directory(struct mg_connection *conn, const char *dir, mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); - // If we don't memset stat structure to zero, mtime will have - // garbage and strftime() will segfault later on in - // print_dir_entry(). memset is required only if mg_stat() - // fails. For more details, see - // http://code.google.com/p/mongoose/issues/detail?id=79 + /* If we don't memset stat structure to zero, mtime will have + garbage and strftime() will segfault later on in + print_dir_entry(). memset is required only if mg_stat() + fails. For more details, see + http://code.google.com/p/mongoose/issues/detail?id=79 */ memset(&de.file, 0, sizeof(de.file)); if (!mg_stat(conn, path, &de.file)) { mg_cry(conn, "%s: mg_stat(%s) failed: %s", @@ -2828,7 +3044,8 @@ static int remove_directory(struct mg_connection *conn, const char *dir) de.conn = conn; while ((dp = readdir(dirp)) != NULL) { - // Do not show current dir (but show hidden files as they will also be removed) + /* Do not show current dir (but show hidden files as they will + also be removed) */ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { continue; @@ -2836,11 +3053,11 @@ static int remove_directory(struct mg_connection *conn, const char *dir) mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); - // If we don't memset stat structure to zero, mtime will have - // garbage and strftime() will segfault later on in - // print_dir_entry(). memset is required only if mg_stat() - // fails. For more details, see - // http://code.google.com/p/mongoose/issues/detail?id=79 + /* If we don't memset stat structure to zero, mtime will have + garbage and strftime() will segfault later on in + print_dir_entry(). memset is required only if mg_stat() + fails. For more details, see + http://code.google.com/p/mongoose/issues/detail?id=79 */ memset(&de.file, 0, sizeof(de.file)); if (!mg_stat(conn, path, &de.file)) { mg_cry(conn, "%s: mg_stat(%s) failed: %s", @@ -2869,7 +3086,7 @@ struct dir_scan_data { int arr_size; }; -// Behaves like realloc(), but frees original pointer on failure +/* Behaves like realloc(), but frees original pointer on failure */ static void *realloc2(void *ptr, size_t size) { void *new_ptr = realloc(ptr, size); @@ -2889,7 +3106,7 @@ static void dir_scan_callback(struct de *de, void *data) sizeof(dsd->entries[0])); } if (dsd->entries == NULL) { - // TODO(lsm): propagate an error to the caller + /* TODO(lsm): propagate an error to the caller */ dsd->num_entries = 0; } else { dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name); @@ -2931,13 +3148,13 @@ static void handle_directory_request(struct mg_connection *conn, conn->request_info.uri, conn->request_info.uri, sort_direction, sort_direction, sort_direction); - // Print first entry - link to a parent directory + /* Print first entry - link to a parent directory */ conn->num_bytes_sent += mg_printf(conn, "%s" " %s  %s\n", conn->request_info.uri, "..", "Parent directory", "-", "-"); - // Sort and print directory entries + /* Sort and print directory entries */ if (data.entries != NULL) { qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]), compare_dir_entries); @@ -2952,14 +3169,14 @@ static void handle_directory_request(struct mg_connection *conn, conn->status_code = 200; } -// Send len bytes from the opened file to the client. +/* Send len bytes from the opened file to the client. */ static void send_file_data(struct mg_connection *conn, struct file *filep, int64_t offset, int64_t len) { char buf[MG_BUF_LEN]; int to_read, num_read, num_written; - // Sanity check the offset + /* Sanity check the offset */ offset = offset < 0 ? 0 : offset > filep->size ? filep->size : offset; if (len > 0 && filep->membuf != NULL && filep->size > 0) { @@ -2973,23 +3190,23 @@ static void send_file_data(struct mg_connection *conn, struct file *filep, __func__, strerror(ERRNO)); } while (len > 0) { - // Calculate how much to read from the file in the buffer + /* Calculate how much to read from the file in the buffer */ to_read = sizeof(buf); if ((int64_t) to_read > len) { to_read = (int) len; } - // Read from file, exit the loop on error + /* Read from file, exit the loop on error */ if ((num_read = (int) fread(buf, 1, (size_t) to_read, filep->fp)) <= 0) { break; } - // Send read bytes to the client, exit the loop on error + /* Send read bytes to the client, exit the loop on error */ if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) { break; } - // Both read and were successful, adjust counters + /* Both read and were successful, adjust counters */ conn->num_bytes_sent += num_written; len -= num_written; } @@ -3052,9 +3269,9 @@ static void handle_file_request(struct mg_connection *conn, const char *path, conn->status_code = 200; range[0] = '\0'; - // if this file is in fact a pre-gzipped file, rewrite its filename - // it's important to rewrite the filename after resolving - // the mime type from it, to preserve the actual file's type + /* if this file is in fact a pre-gzipped file, rewrite its filename + it's important to rewrite the filename after resolving + the mime type from it, to preserve the actual file's type */ if (filep->gzipped) { snprintf(gz_path, sizeof(gz_path), "%s.gz", path); path = gz_path; @@ -3069,13 +3286,13 @@ static void handle_file_request(struct mg_connection *conn, const char *path, fclose_on_exec(filep, conn); - // If Range: header specified, act accordingly + /* If Range: header specified, act accordingly */ r1 = r2 = 0; hdr = mg_get_header(conn, "Range"); if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && r1 >= 0 && r2 >= 0) { - // actually, range requests don't play well with a pre-gzipped - // file (since the range is specified in the uncompressed space) + /* actually, range requests don't play well with a pre-gzipped + file (since the range is specified in the uncompressed space) */ if (filep->gzipped) { send_http_error(conn, 501, "Not Implemented", "range requests in gzipped files are not supported"); mg_fclose(filep); @@ -3091,8 +3308,8 @@ cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; msg = "Partial Content"; } - // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 + /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to + http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */ gmt_time_string(date, sizeof(date), &curtime); gmt_time_string(lm, sizeof(lm), &filep->modification_time); construct_etag(etag, sizeof(etag), filep); @@ -3127,8 +3344,8 @@ void mg_send_file(struct mg_connection *conn, const char *path) } -// Parse HTTP headers from the given buffer, advance buffer to the point -// where parsing stopped. +/* Parse HTTP headers from the given buffer, advance buffer to the point + where parsing stopped. */ static void parse_http_headers(char **buf, struct mg_request_info *ri) { int i; @@ -3152,20 +3369,20 @@ static int is_valid_http_method(const char *method) ; } -// Parse HTTP request, fill in mg_request_info structure. -// This function modifies the buffer by NUL-terminating -// HTTP request components, header names and header values. +/* Parse HTTP request, fill in mg_request_info structure. + This function modifies the buffer by NUL-terminating + HTTP request components, header names and header values. */ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) { int is_request, request_length = get_request_len(buf, len); if (request_length > 0) { - // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port + /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port */ ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL; ri->num_headers = 0; buf[request_length - 1] = '\0'; - // RFC says that all initial whitespaces should be ingored + /* RFC says that all initial whitespaces should be ingored */ while (*buf != '\0' && isspace(* (unsigned char *) buf)) { buf++; } @@ -3173,8 +3390,8 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) ri->uri = skip(&buf, " "); ri->http_version = skip(&buf, "\r\n"); - // HTTP message could be either HTTP request or HTTP response, e.g. - // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." + /* HTTP message could be either HTTP request or HTTP response, e.g. + "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." */ is_request = is_valid_http_method(ri->request_method); if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { @@ -3189,11 +3406,11 @@ static int parse_http_message(char *buf, int len, struct mg_request_info *ri) return request_length; } -// Keep reading the input (either opened file descriptor fd, or socket sock, -// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the -// buffer (which marks the end of HTTP request). Buffer buf may already -// have some data. The length of the data is stored in nread. -// Upon every read operation, increase nread by the number of bytes read. +/* Keep reading the input (either opened file descriptor fd, or socket sock, + or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the + buffer (which marks the end of HTTP request). Buffer buf may already + have some data. The length of the data is stored in nread. + Upon every read operation, increase nread by the number of bytes read. */ static int read_request(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread) { @@ -3211,9 +3428,9 @@ static int read_request(FILE *fp, struct mg_connection *conn, return request_len <= 0 && n <= 0 ? -1 : request_len; } -// For given directory path, substitute it to valid index file. -// Return 0 if index file has been found, -1 if not found. -// If the file is found, it's stats is returned in stp. +/* For given directory path, substitute it to valid index file. + Return 0 if index file has been found, -1 if not found. + If the file is found, it's stats is returned in stp. */ static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct file *filep) { @@ -3223,35 +3440,35 @@ static int substitute_index_file(struct mg_connection *conn, char *path, size_t n = strlen(path); int found = 0; - // The 'path' given to us points to the directory. Remove all trailing - // directory separator characters from the end of the path, and - // then append single directory separator character. + /* The 'path' given to us points to the directory. Remove all trailing + directory separator characters from the end of the path, and + then append single directory separator character. */ while (n > 0 && path[n - 1] == '/') { n--; } path[n] = '/'; - // Traverse index files list. For each entry, append it to the given - // path and see if the file exists. If it exists, break the loop + /* Traverse index files list. For each entry, append it to the given + path and see if the file exists. If it exists, break the loop */ while ((list = next_option(list, &filename_vec, NULL)) != NULL) { - // Ignore too long entries that may overflow path buffer + /* Ignore too long entries that may overflow path buffer */ if (filename_vec.len > path_len - (n + 2)) continue; - // Prepare full path to the index file + /* Prepare full path to the index file */ mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); - // Does it exist? + /* Does it exist? */ if (mg_stat(conn, path, &file)) { - // Yes it does, break the loop + /* Yes it does, break the loop */ *filep = file; found = 1; break; } } - // If no index file exists, restore directory path + /* If no index file exists, restore directory path */ if (!found) { path[n] = '\0'; } @@ -3259,7 +3476,7 @@ static int substitute_index_file(struct mg_connection *conn, char *path, return found; } -// Return True if we should reply 304 Not Modified. +/* Return True if we should reply 304 Not Modified. */ static int is_not_modified(const struct mg_connection *conn, const struct file *filep) { @@ -3320,7 +3537,7 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, success = nread >= 0; } - // Each error code path in this function must send an error + /* Each error code path in this function must send an error */ if (!success) { send_http_error(conn, 577, http_500_error, "%s", ""); } @@ -3330,52 +3547,52 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, } #if !defined(NO_CGI) -// This structure helps to create an environment for the spawned CGI program. -// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, -// last element must be NULL. -// However, on Windows there is a requirement that all these VARIABLE=VALUE\0 -// strings must reside in a contiguous buffer. The end of the buffer is -// marked by two '\0' characters. -// We satisfy both worlds: we create an envp array (which is vars), all -// entries are actually pointers inside buf. +/* This structure helps to create an environment for the spawned CGI program. + Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, + last element must be NULL. + However, on Windows there is a requirement that all these VARIABLE=VALUE\0 + strings must reside in a contiguous buffer. The end of the buffer is + marked by two '\0' characters. + We satisfy both worlds: we create an envp array (which is vars), all + entries are actually pointers inside buf. */ struct cgi_env_block { struct mg_connection *conn; - char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer - int len; // Space taken - char *vars[MAX_CGI_ENVIR_VARS]; // char **envp - int nvars; // Number of variables + char buf[CGI_ENVIRONMENT_SIZE]; /* Environment buffer */ + int len; /* Space taken */ + char *vars[MAX_CGI_ENVIR_VARS]; /* char **envp */ + int nvars; /* Number of variables */ }; static char *addenv(struct cgi_env_block *block, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); -// Append VARIABLE=VALUE\0 string to the buffer, and add a respective -// pointer into the vars array. +/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective + pointer into the vars array. */ static char *addenv(struct cgi_env_block *block, const char *fmt, ...) { int n, space; char *added; va_list ap; - // Calculate how much space is left in the buffer + /* Calculate how much space is left in the buffer */ space = sizeof(block->buf) - block->len - 2; assert(space >= 0); - // Make a pointer to the free space int the buffer + /* Make a pointer to the free space int the buffer */ added = block->buf + block->len; - // Copy VARIABLE=VALUE\0 string into the free space + /* Copy VARIABLE=VALUE\0 string into the free space */ va_start(ap, fmt); n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap); va_end(ap); - // Make sure we do not overflow buffer and the envp array + /* Make sure we do not overflow buffer and the envp array */ if (n > 0 && n + 1 < space && block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { - // Append a pointer to the added string into the envp array + /* Append a pointer to the added string into the envp array */ block->vars[block->nvars++] = added; - // Bump up used length counter. Include \0 terminator + /* Bump up used length counter. Include \0 terminator */ block->len += n + 1; } else { mg_cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt); @@ -3402,12 +3619,12 @@ static void prepare_cgi_environment(struct mg_connection *conn, addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); addenv(blk, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version()); - // Prepare the environment block + /* Prepare the environment block */ addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); - addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP + addenv(blk, "%s", "REDIRECT_STATUS=200"); /* For PHP */ - // TODO(lsm): fix this for IPv6 case + /* TODO(lsm): fix this for IPv6 case */ addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port)); addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method); @@ -3415,7 +3632,7 @@ static void prepare_cgi_environment(struct mg_connection *conn, addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port); addenv(blk, "REQUEST_URI=%s", conn->request_info.uri); - // SCRIPT_NAME + /* SCRIPT_NAME */ assert(conn->request_info.uri[0] == '/'); slash = strrchr(conn->request_info.uri, '/'); if ((s = strrchr(prog, '/')) == NULL) @@ -3462,7 +3679,7 @@ static void prepare_cgi_environment(struct mg_connection *conn, #else if ((s = getenv("LD_LIBRARY_PATH")) != NULL) addenv(blk, "LD_LIBRARY_PATH=%s", s); -#endif // _WIN32 +#endif /* _WIN32 */ if ((s = getenv("PERLLIB")) != NULL) addenv(blk, "PERLLIB=%s", s); @@ -3472,13 +3689,13 @@ static void prepare_cgi_environment(struct mg_connection *conn, addenv(blk, "%s", "AUTH_TYPE=Digest"); } - // Add all headers as HTTP_* variables + /* Add all headers as HTTP_* variables */ for (i = 0; i < conn->request_info.num_headers; i++) { p = addenv(blk, "HTTP_%s=%s", conn->request_info.http_headers[i].name, conn->request_info.http_headers[i].value); - // Convert variable name into uppercase, and change - to _ + /* Convert variable name into uppercase, and change - to _ */ for (; *p != '=' && *p != '\0'; p++) { if (*p == '-') *p = '_'; @@ -3486,7 +3703,7 @@ static void prepare_cgi_environment(struct mg_connection *conn, } } - // Add user-specified variables + /* Add user-specified variables */ s = conn->ctx->config[CGI_ENVIRONMENT]; while ((s = next_option(s, &var_vec, NULL)) != NULL) { addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr); @@ -3505,7 +3722,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) char *buf; size_t buflen; int headers_len, data_len, i, fdin[2] = { 0, 0 }, fdout[2] = { 0, 0 }; - const char *status, *status_text; + const char *status, *status_text, *connection_state; char *pbuf, dir[PATH_MAX], *p; struct mg_request_info ri; struct cgi_env_block blk; @@ -3517,9 +3734,9 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) buflen = 16384; prepare_cgi_environment(conn, prog, &blk); - // CGI must be executed in its own directory. 'dir' must point to the - // directory containing executable program, 'p' must point to the - // executable program name relative to 'dir'. + /* CGI must be executed in its own directory. 'dir' must point to the + directory containing executable program, 'p' must point to the + executable program name relative to 'dir'. */ (void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog); if ((p = strrchr(dir, '/')) != NULL) { *p++ = '\0'; @@ -3541,16 +3758,16 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) goto done; } - // Make sure child closes all pipe descriptors. It must dup them to 0,1 + /* Make sure child closes all pipe descriptors. It must dup them to 0,1 */ set_close_on_exec(fdin[0], conn); set_close_on_exec(fdin[1], conn); set_close_on_exec(fdout[0], conn); set_close_on_exec(fdout[1], conn); - // Parent closes only one side of the pipes. - // If we don't mark them as closed, close() attempt before - // return from this function throws an exception on Windows. - // Windows does not like when closed descriptor is closed again. + /* Parent closes only one side of the pipes. + If we don't mark them as closed, close() attempt before + return from this function throws an exception on Windows. + Windows does not like when closed descriptor is closed again. */ (void) close(fdin[0]); (void) close(fdout[1]); fdin[0] = fdout[1] = -1; @@ -3567,21 +3784,21 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) setbuf(out, NULL); fout.fp = out; - // Send POST data to the CGI process if needed + /* Send POST data to the CGI process if needed */ if (!strcmp(conn->request_info.request_method, "POST") && !forward_body_data(conn, in, INVALID_SOCKET, NULL)) { goto done; } - // Close so child gets an EOF. + /* Close so child gets an EOF. */ fclose(in); in = NULL; fdin[1] = -1; - // Now read CGI reply into a buffer. We need to set correct - // status code, thus we need to see all HTTP headers first. - // Do not send anything back to client, until we buffer in all - // HTTP headers. + /* Now read CGI reply into a buffer. We need to set correct + status code, thus we need to see all HTTP headers first. + Do not send anything back to client, until we buffer in all + HTTP headers. */ data_len = 0; buf = malloc(buflen); if (buf == NULL) { @@ -3602,7 +3819,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) buf[headers_len - 1] = '\0'; parse_http_headers(&pbuf, &ri); - // Make up and send the status line + /* Make up and send the status line */ status_text = "OK"; if ((status = get_header(&ri, "Status")) != NULL) { conn->status_code = atoi(status); @@ -3615,25 +3832,26 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) } else { conn->status_code = 200; } - if (get_header(&ri, "Connection") != NULL && - !mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) { + connection_state = get_header(&ri, "Connection"); + if (connection_state == NULL || + mg_strcasecmp(connection_state, "keep-alive")) { conn->must_close = 1; } (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text); - // Send headers + /* Send headers */ for (i = 0; i < ri.num_headers; i++) { mg_printf(conn, "%s: %s\r\n", ri.http_headers[i].name, ri.http_headers[i].value); } mg_write(conn, "\r\n", 2); - // Send chunk of data that may have been read after the headers + /* Send chunk of data that may have been read after the headers */ conn->num_bytes_sent += mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len)); - // Read the rest of CGI output and send to the client + /* Read the rest of CGI output and send to the client */ send_file_data(conn, &fout, 0, INT64_MAX); done: @@ -3642,7 +3860,7 @@ done: #if !defined(_WIN32) { int st; - while (waitpid(pid, &st, 0) != -1); // clean zombies + while (waitpid(pid, &st, 0) != -1); /* clean zombies */ } #endif } @@ -3668,11 +3886,11 @@ done: free(buf); } } -#endif // !NO_CGI +#endif /* !NO_CGI */ -// For a given PUT path, create all intermediate subdirectories -// for given path. Return 0 if the path itself is a directory, -// or -1 on error, 1 if OK. +/* For a given PUT path, create all intermediate subdirectories + for given path. Return 0 if the path itself is a directory, + or -1 on error, 1 if OK. */ static int put_dir(struct mg_connection *conn, const char *path) { char buf[PATH_MAX]; @@ -3689,14 +3907,14 @@ static int put_dir(struct mg_connection *conn, const char *path) memcpy(buf, path, len); buf[len] = '\0'; - // Try to create intermediate directory + /* Try to create intermediate directory */ DEBUG_TRACE(("mkdir(%s)", buf)); if (!mg_stat(conn, buf, &file) && mg_mkdir(buf, 0755) != 0) { res = -1; break; } - // Is path itself a directory? + /* Is path itself a directory? */ if (p[1] == '\0') { res = 0; } @@ -3793,19 +4011,20 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi, char file_name[MG_BUF_LEN], path[PATH_MAX], *p; struct file file = STRUCT_FILE_INITIALIZER; - // sscanf() is safe here, since send_ssi_file() also uses buffer - // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. + /* sscanf() is safe here, since send_ssi_file() also uses buffer + of size MG_BUF_LEN to get the tag. So strlen(tag) is + always < MG_BUF_LEN. */ if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { - // File name is relative to the webserver root + /* File name is relative to the webserver root */ (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s", conn->ctx->config[DOCUMENT_ROOT], '/', file_name); } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) { - // File name is relative to the webserver working directory - // or it is absolute system path + /* File name is relative to the webserver working directory + or it is absolute system path */ (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name); } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || sscanf(tag, " \"%[^\"]\"", file_name) == 1) { - // File name is relative to the currect document + /* File name is relative to the currect document */ (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi); if ((p = strrchr(path, '/')) != NULL) { p[1] = '\0'; @@ -3847,7 +4066,7 @@ static void do_ssi_exec(struct mg_connection *conn, char *tag) pclose(file.fp); } } -#endif // !NO_POPEN +#endif /* !NO_POPEN */ static int mg_fgetc(struct file *filep, int offset) { @@ -3879,7 +4098,7 @@ static void send_ssi_file(struct mg_connection *conn, const char *path, buf[len] = '\0'; assert(len <= (int) sizeof(buf)); if (len < 6 || memcmp(buf, "