From 6ce8b4e92cef9e60e8021f180106d98c7f8599e9 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 17 May 2015 01:30:21 +0200 Subject: [PATCH] Set autoformat options and autoformat all source files --- .clang-format | 17 +- include/CivetServer.h | 586 +- include/civetweb.h | 267 +- src/CivetServer.cpp | 376 +- src/civetweb.c | 12335 ++++++++++++++++++++-------------------- src/main.c | 2596 ++++----- src/md5.inl | 481 +- src/mod_lua.inl | 2275 ++++---- src/timer.inl | 227 +- 9 files changed, 9603 insertions(+), 9557 deletions(-) diff --git a/.clang-format b/.clang-format index caea8189..74d635a9 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,14 @@ -BasedOnStyle: LLVM -IndentWidth: 4 -Language: Cpp +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html + +BasedOnStyle: LLVM + +IndentWidth: 4 +TabWidth: 4 +UseTab: ForIndentation +ColumnLimit: 80 + +Language: Cpp + +AccessModifierOffset: 2 +AlignAfterOpenBracket: true +AllowShortIfStatementsOnASingleLine: false diff --git a/include/CivetServer.h b/include/CivetServer.h index 4e66609f..d50b42f9 100644 --- a/include/CivetServer.h +++ b/include/CivetServer.h @@ -21,8 +21,8 @@ class CivetServer; * Exception class for thrown exceptions within the CivetHandler object. */ class CIVETWEB_API CivetException : public std::runtime_error { - public: - CivetException(const std::string &msg) : std::runtime_error(msg) {} + public: + CivetException(const std::string &msg) : std::runtime_error(msg) {} }; /** @@ -30,56 +30,56 @@ class CIVETWEB_API CivetException : public std::runtime_error { * must be reentrant. */ class CIVETWEB_API CivetHandler { - public: - /** - * Destructor - */ - virtual ~CivetHandler() {} + public: + /** + * Destructor + */ + virtual ~CivetHandler() {} - /** - * Callback method for GET request. - * - * @param server - the calling server - * @param conn - the connection information - * @returns true if implemented, false otherwise - */ - virtual bool handleGet(CivetServer *server, struct mg_connection *conn); + /** + * Callback method for GET request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handleGet(CivetServer *server, struct mg_connection *conn); - /** - * Callback method for POST request. - * - * @param server - the calling server - * @param conn - the connection information - * @returns true if implemented, false otherwise - */ - virtual bool handlePost(CivetServer *server, struct mg_connection *conn); + /** + * Callback method for POST request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handlePost(CivetServer *server, struct mg_connection *conn); - /** - * Callback method for PUT request. - * - * @param server - the calling server - * @param conn - the connection information - * @returns true if implemented, false otherwise - */ - virtual bool handlePut(CivetServer *server, struct mg_connection *conn); + /** + * Callback method for PUT request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handlePut(CivetServer *server, struct mg_connection *conn); - /** - * Callback method for DELETE request. - * - * @param server - the calling server - * @param conn - the connection information - * @returns true if implemented, false otherwise - */ - virtual bool handleDelete(CivetServer *server, struct mg_connection *conn); + /** + * Callback method for DELETE request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handleDelete(CivetServer *server, struct mg_connection *conn); - /** - * Callback method for OPTIONS request. - * - * @param server - the calling server - * @param conn - the connection information - * @returns true if implemented, false otherwise - */ - virtual bool handleOptions(CivetServer *server, struct mg_connection *conn); + /** + * Callback method for OPTIONS request. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if implemented, false otherwise + */ + virtual bool handleOptions(CivetServer *server, struct mg_connection *conn); }; /** @@ -88,277 +88,277 @@ class CIVETWEB_API CivetHandler { * Basic class for embedded web server. This has an URL mapping built-in. */ class CIVETWEB_API CivetServer { - public: - /** - * Constructor - * - * This automatically starts the sever. - * It is good practice to call getContext() after this in case there - * were errors starting the server. - * - * @param options - the web server options. - * @param callbacks - optional web server callback methods. - * - * @throws CivetException - */ - CivetServer(const char **options, const struct mg_callbacks *callbacks = 0); + public: + /** + * Constructor + * + * This automatically starts the sever. + * It is good practice to call getContext() after this in case there + * were errors starting the server. + * + * @param options - the web server options. + * @param callbacks - optional web server callback methods. + * + * @throws CivetException + */ + CivetServer(const char **options, const struct mg_callbacks *callbacks = 0); - /** - * Destructor - */ - virtual ~CivetServer(); + /** + * Destructor + */ + virtual ~CivetServer(); - /** - * close() - * - * Stops server and frees resources. - */ - void close(); + /** + * close() + * + * Stops server and frees resources. + */ + void close(); - /** - * getContext() - * - * @return the context or 0 if not running. - */ - const struct mg_context *getContext() const { return context; } + /** + * getContext() + * + * @return the context or 0 if not running. + */ + const struct mg_context *getContext() const { return context; } - /** - * addHandler(const std::string &, CivetHandler *) - * - * Adds a URI handler. If there is existing URI handler, it will - * be replaced with this one. - * - * URI's are ordered and prefix (REST) URI's are supported. - * - * @param uri - URI to match. - * @param handler - handler instance to use. - */ - void addHandler(const std::string &uri, CivetHandler *handler); + /** + * addHandler(const std::string &, CivetHandler *) + * + * Adds a URI handler. If there is existing URI handler, it will + * be replaced with this one. + * + * URI's are ordered and prefix (REST) URI's are supported. + * + * @param uri - URI to match. + * @param handler - handler instance to use. + */ + void addHandler(const std::string &uri, CivetHandler *handler); - void addHandler(const std::string &uri, CivetHandler &handler) { - addHandler(uri, &handler); - } + void addHandler(const std::string &uri, CivetHandler &handler) { + addHandler(uri, &handler); + } - /** - * removeHandler(const std::string &) - * - * Removes a handler. - * - * @param uri - the exact URL used in addHandler(). - */ - void removeHandler(const std::string &uri); + /** + * removeHandler(const std::string &) + * + * Removes a handler. + * + * @param uri - the exact URL used in addHandler(). + */ + void removeHandler(const std::string &uri); - /** - * getListeningPorts() - * - * Returns a list of ports that are listening - * - * @return A vector of ports - */ + /** + * getListeningPorts() + * + * Returns a list of ports that are listening + * + * @return A vector of ports + */ - std::vector getListeningPorts(); + std::vector getListeningPorts(); - /** - * getCookie(struct mg_connection *conn, const std::string &cookieName, - *std::string &cookieValue) - * - * Puts the cookie value string that matches the cookie name in the - *cookieValue destinaton string. - * - * @param conn - the connection information - * @param cookieName - cookie name to get the value from - * @param cookieValue - cookie value is returned using thiis reference - * @returns the size of the cookie value string read. - */ - static int getCookie(struct mg_connection *conn, - const std::string &cookieName, - std::string &cookieValue); + /** + * getCookie(struct mg_connection *conn, const std::string &cookieName, + *std::string &cookieValue) + * + * Puts the cookie value string that matches the cookie name in the + *cookieValue destinaton string. + * + * @param conn - the connection information + * @param cookieName - cookie name to get the value from + * @param cookieValue - cookie value is returned using thiis reference + * @returns the size of the cookie value string read. + */ + static int getCookie(struct mg_connection *conn, + const std::string &cookieName, + std::string &cookieValue); - /** - * getHeader(struct mg_connection *conn, const std::string &headerName) - * @param conn - the connection information - * @param headerName - header name to get the value from - * @returns a char array whcih contains the header value as string - */ - static const char *getHeader(struct mg_connection *conn, - const std::string &headerName); + /** + * getHeader(struct mg_connection *conn, const std::string &headerName) + * @param conn - the connection information + * @param headerName - header name to get the value from + * @returns a char array whcih contains the header value as string + */ + static const char *getHeader(struct mg_connection *conn, + const std::string &headerName); - /** - * getParam(struct mg_connection *conn, const char *, std::string &, size_t) - * - * Returns a query paramter contained in the supplied buffer. The - * occurance value is a zero-based index of a particular key name. This - * should not be confused with the index over all of the keys. Note that - *this - * function assumes that parameters are sent as text in http query string - * format, which is the default for web forms. This function will work for - * html forms with method="GET" and method="POST" attributes. In other - *cases, - * you may use a getParam version that directly takes the data instead of - *the - * connection as a first argument. - * - * @param conn - parameters are read from the data sent through this - *connection - * @param name - the key to search for - * @param dst - the destination string - * @param occurrence - the occurrence of the selected name in the query (0 - *based). - * @return true if key was found - */ - static bool getParam(struct mg_connection *conn, const char *name, - std::string &dst, size_t occurrence = 0); + /** + * getParam(struct mg_connection *conn, const char *, std::string &, size_t) + * + * Returns a query paramter contained in the supplied buffer. The + * occurance value is a zero-based index of a particular key name. This + * should not be confused with the index over all of the keys. Note that + *this + * function assumes that parameters are sent as text in http query string + * format, which is the default for web forms. This function will work for + * html forms with method="GET" and method="POST" attributes. In other + *cases, + * you may use a getParam version that directly takes the data instead of + *the + * connection as a first argument. + * + * @param conn - parameters are read from the data sent through this + *connection + * @param name - the key to search for + * @param dst - the destination string + * @param occurrence - the occurrence of the selected name in the query (0 + *based). + * @return true if key was found + */ + static bool getParam(struct mg_connection *conn, const char *name, + std::string &dst, size_t occurrence = 0); - /** - * getParam(const std::string &, const char *, std::string &, size_t) - * - * Returns a query paramter contained in the supplied buffer. The - * occurance value is a zero-based index of a particular key name. This - * should not be confused with the index over all of the keys. - * - * @param data - the query string (text) - * @param name - the key to search for - * @param dst - the destination string - * @param occurrence - the occurrence of the selected name in the query (0 - *based). - * @return true if key was found - */ - static bool getParam(const std::string &data, const char *name, - std::string &dst, size_t occurrence = 0) { - return getParam(data.c_str(), data.length(), name, dst, occurrence); - } + /** + * getParam(const std::string &, const char *, std::string &, size_t) + * + * Returns a query paramter contained in the supplied buffer. The + * occurance value is a zero-based index of a particular key name. This + * should not be confused with the index over all of the keys. + * + * @param data - the query string (text) + * @param name - the key to search for + * @param dst - the destination string + * @param occurrence - the occurrence of the selected name in the query (0 + *based). + * @return true if key was found + */ + static bool getParam(const std::string &data, const char *name, + std::string &dst, size_t occurrence = 0) { + return getParam(data.c_str(), data.length(), name, dst, occurrence); + } - /** - * getParam(const char *, size_t, const char *, std::string &, size_t) - * - * Returns a query paramter contained in the supplied buffer. The - * occurance value is a zero-based index of a particular key name. This - * should not be confused with the index over all of the keys. - * - * @param data the - query string (text) - * @param data_len - length of the query string - * @param name - the key to search for - * @param dst - the destination string - * @param occurrence - the occurrence of the selected name in the query (0 - *based). - * @return true if key was found - */ - static bool getParam(const char *data, size_t data_len, const char *name, - std::string &dst, size_t occurrence = 0); + /** + * getParam(const char *, size_t, const char *, std::string &, size_t) + * + * Returns a query paramter contained in the supplied buffer. The + * occurance value is a zero-based index of a particular key name. This + * should not be confused with the index over all of the keys. + * + * @param data the - query string (text) + * @param data_len - length of the query string + * @param name - the key to search for + * @param dst - the destination string + * @param occurrence - the occurrence of the selected name in the query (0 + *based). + * @return true if key was found + */ + static bool getParam(const char *data, size_t data_len, const char *name, + std::string &dst, size_t occurrence = 0); - /** - * urlDecode(const std::string &, std::string &, bool) - * - * @param src - string to be decoded - * @param dst - destination string - * @param is_form_url_encoded - true if form url encoded - * 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 - */ - static void urlDecode(const std::string &src, std::string &dst, - bool is_form_url_encoded = true) { - urlDecode(src.c_str(), src.length(), dst, is_form_url_encoded); - } + /** + * urlDecode(const std::string &, std::string &, bool) + * + * @param src - string to be decoded + * @param dst - destination string + * @param is_form_url_encoded - true if form url encoded + * 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 + */ + static void urlDecode(const std::string &src, std::string &dst, + bool is_form_url_encoded = true) { + urlDecode(src.c_str(), src.length(), dst, is_form_url_encoded); + } - /** - * urlDecode(const char *, size_t, std::string &, bool) - * - * @param src - buffer to be decoded - * @param src_len - length of buffer to be decoded - * @param dst - destination string - * @param is_form_url_encoded - true if form url encoded - * 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 - */ - static void urlDecode(const char *src, size_t src_len, std::string &dst, - bool is_form_url_encoded = true); + /** + * urlDecode(const char *, size_t, std::string &, bool) + * + * @param src - buffer to be decoded + * @param src_len - length of buffer to be decoded + * @param dst - destination string + * @param is_form_url_encoded - true if form url encoded + * 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 + */ + static void urlDecode(const char *src, size_t src_len, std::string &dst, + bool is_form_url_encoded = true); - /** - * urlDecode(const char *, std::string &, bool) - * - * @param src - buffer to be decoded (0 terminated) - * @param dst - destination string - * @param is_form_url_encoded true - if form url encoded - * 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 - */ - static void urlDecode(const char *src, std::string &dst, - bool is_form_url_encoded = true); + /** + * urlDecode(const char *, std::string &, bool) + * + * @param src - buffer to be decoded (0 terminated) + * @param dst - destination string + * @param is_form_url_encoded true - if form url encoded + * 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 + */ + static void urlDecode(const char *src, std::string &dst, + bool is_form_url_encoded = true); - /** - * urlEncode(const std::string &, std::string &, bool) - * - * @param src - buffer to be encoded - * @param dst - destination string - * @param append - true if string should not be cleared before encoding. - */ - static void urlEncode(const std::string &src, std::string &dst, - bool append = false) { - urlEncode(src.c_str(), src.length(), dst, append); - } + /** + * urlEncode(const std::string &, std::string &, bool) + * + * @param src - buffer to be encoded + * @param dst - destination string + * @param append - true if string should not be cleared before encoding. + */ + static void urlEncode(const std::string &src, std::string &dst, + bool append = false) { + urlEncode(src.c_str(), src.length(), dst, append); + } - /** - * urlEncode(const char *, size_t, std::string &, bool) - * - * @param src - buffer to be encoded (0 terminated) - * @param dst - destination string - * @param append - true if string should not be cleared before encoding. - */ - static void urlEncode(const char *src, std::string &dst, - bool append = false); + /** + * urlEncode(const char *, size_t, std::string &, bool) + * + * @param src - buffer to be encoded (0 terminated) + * @param dst - destination string + * @param append - true if string should not be cleared before encoding. + */ + static void urlEncode(const char *src, std::string &dst, + bool append = false); - /** - * urlEncode(const char *, size_t, std::string &, bool) - * - * @param src - buffer to be encoded - * @param src_len - length of buffer to be decoded - * @param dst - destination string - * @param append - true if string should not be cleared before encoding. - */ - static void urlEncode(const char *src, size_t src_len, std::string &dst, - bool append = false); + /** + * urlEncode(const char *, size_t, std::string &, bool) + * + * @param src - buffer to be encoded + * @param src_len - length of buffer to be decoded + * @param dst - destination string + * @param append - true if string should not be cleared before encoding. + */ + static void urlEncode(const char *src, size_t src_len, std::string &dst, + bool append = false); - protected: - class CivetConnection { - public: - char *postData; - unsigned long postDataLen; + protected: + class CivetConnection { + public: + char *postData; + unsigned long postDataLen; - CivetConnection(); - ~CivetConnection(); - }; + CivetConnection(); + ~CivetConnection(); + }; - struct mg_context *context; - std::map connections; + struct mg_context *context; + std::map connections; - private: - /** - * requestHandler(struct mg_connection *, void *cbdata) - * - * Handles the incomming request. - * - * @param conn - the connection information - * @param cbdata - pointer to the CivetHandler instance. - * @returns 0 if implemented, false otherwise - */ - static int requestHandler(struct mg_connection *conn, void *cbdata); + private: + /** + * requestHandler(struct mg_connection *, void *cbdata) + * + * Handles the incomming request. + * + * @param conn - the connection information + * @param cbdata - pointer to the CivetHandler instance. + * @returns 0 if implemented, false otherwise + */ + static int requestHandler(struct mg_connection *conn, void *cbdata); - /** - * closeHandler(struct mg_connection *) - * - * Handles closing a request (internal handler) - * - * @param conn - the connection information - */ - static void closeHandler(const struct mg_connection *conn); + /** + * closeHandler(struct mg_connection *) + * + * Handles closing a request (internal handler) + * + * @param conn - the connection information + */ + static void closeHandler(const struct mg_connection *conn); - /** - * Stores the user provided close handler - */ - void (*userCloseHandler)(const struct mg_connection *conn); + /** + * Stores the user provided close handler + */ + void (*userCloseHandler)(const struct mg_connection *conn); }; #endif /* __cplusplus */ diff --git a/include/civetweb.h b/include/civetweb.h index decfed1a..0df6fbf3 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -51,146 +51,145 @@ struct mg_connection; /* Handle for the individual connection */ /* 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 */ - char remote_addr[48]; /* Client's IP address as a string. */ - long - remote_ip; /* Client's IP address. Deprecated: use remote_addr instead - */ + 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 */ + char remote_addr[48]; /* Client's IP address as a string. */ + long remote_ip; /* Client's IP address. Deprecated: use remote_addr instead + */ - long long content_length; /* Length (in bytes) of the request body, - can be -1 if no length was given. */ - int remote_port; /* Client's port */ - int is_ssl; /* 1 if SSL-ed, 0 if not */ - void *user_data; /* User data pointer passed to mg_start() */ - void *conn_data; /* Connection-specific user data */ + long long content_length; /* Length (in bytes) of the request body, + can be -1 if no length was given. */ + int remote_port; /* Client's port */ + int is_ssl; /* 1 if SSL-ed, 0 if not */ + void *user_data; /* User data pointer passed to mg_start() */ + void *conn_data; /* Connection-specific user data */ - int num_headers; /* Number of HTTP headers */ - struct mg_header { - const char *name; /* HTTP header name */ - const char *value; /* HTTP header value */ - } http_headers[64]; /* Maximum 64 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 */ }; /* This structure needs to be passed to mg_start(), to let civetweb know which callbacks to invoke. For a detailed description, see https://github.com/bel2125/civetweb/blob/master/docs/UserManual.md */ struct mg_callbacks { - /* Called when civetweb has received new HTTP request. - If the callback returns one, it must process the request - by sending valid HTTP headers and a body. Civetweb will not do - any further processing. Otherwise it must return zero. - Note that since V1.7 the "begin_request" function is called - before an authorization check. If an authorization check is - required, use a request_handler instead. - Return value: - 0: civetweb will process the request itself. In this case, - the callback must not send any data to the client. - 1: callback already processed the request. Civetweb will - not send any data after the callback returned. */ - int (*begin_request)(struct mg_connection *); + /* Called when civetweb has received new HTTP request. + If the callback returns one, it must process the request + by sending valid HTTP headers and a body. Civetweb will not do + any further processing. Otherwise it must return zero. + Note that since V1.7 the "begin_request" function is called + before an authorization check. If an authorization check is + required, use a request_handler instead. + Return value: + 0: civetweb will process the request itself. In this case, + the callback must not send any data to the client. + 1: callback already processed the request. Civetweb will + not send any data after the callback returned. */ + int (*begin_request)(struct mg_connection *); - /* Called when civetweb has finished processing request. */ - void (*end_request)(const struct mg_connection *, int reply_status_code); + /* Called when civetweb has finished processing request. */ + void (*end_request)(const struct mg_connection *, int reply_status_code); - /* Called when civetweb is about to log a message. If callback returns - non-zero, civetweb does not log anything. */ - int (*log_message)(const struct mg_connection *, const char *message); + /* Called when civetweb is about to log a message. If callback returns + non-zero, civetweb does not log anything. */ + int (*log_message)(const struct mg_connection *, const char *message); - /* Called when civetweb is about to log access. If callback returns - non-zero, civetweb does not log anything. */ - int (*log_access)(const struct mg_connection *, const char *message); + /* Called when civetweb is about to log access. If callback returns + non-zero, civetweb does not log anything. */ + int (*log_access)(const struct mg_connection *, const char *message); - /* Called when civetweb initializes SSL library. - Parameters: - user_data: parameter user_data passed when starting the server. - Return value: - 0: civetweb will set up the SSL certificate. - 1: civetweb assumes the callback already set up the certificate. - -1: initializing ssl fails. */ - int (*init_ssl)(void *ssl_context, void *user_data); + /* Called when civetweb initializes SSL library. + Parameters: + user_data: parameter user_data passed when starting the server. + Return value: + 0: civetweb will set up the SSL certificate. + 1: civetweb assumes the callback already set up the certificate. + -1: initializing ssl fails. */ + int (*init_ssl)(void *ssl_context, void *user_data); - /* Called when websocket request is received, before websocket handshake. - Return value: - 0: civetweb proceeds with websocket handshake. - 1: connection is closed immediately. - This callback is deprecated, use mg_set_websocket_handler instead. */ - int (*websocket_connect)(const struct mg_connection *); + /* Called when websocket request is received, before websocket handshake. + Return value: + 0: civetweb proceeds with websocket handshake. + 1: connection is closed immediately. + This callback is deprecated, use mg_set_websocket_handler instead. */ + int (*websocket_connect)(const struct mg_connection *); - /* Called when websocket handshake is successfully completed, and - connection is ready for data exchange. - This callback is deprecated, use mg_set_websocket_handler instead. */ - void (*websocket_ready)(struct mg_connection *); + /* Called when websocket handshake is successfully completed, and + connection is ready for data exchange. + This callback is deprecated, use mg_set_websocket_handler instead. */ + void (*websocket_ready)(struct mg_connection *); - /* Called when data frame has been received from the client. - Parameters: - bits: first byte of the websocket frame, see websocket RFC at - http://tools.ietf.org/html/rfc6455, section 5.2 - data, data_len: payload, with mask (if any) already applied. - Return value: - 1: keep this websocket connection open. - 0: close this websocket connection. - This callback is deprecated, use mg_set_websocket_handler instead. */ - int (*websocket_data)(struct mg_connection *, int bits, char *data, - size_t data_len); + /* Called when data frame has been received from the client. + Parameters: + bits: first byte of the websocket frame, see websocket RFC at + http://tools.ietf.org/html/rfc6455, section 5.2 + data, data_len: payload, with mask (if any) already applied. + Return value: + 1: keep this websocket connection open. + 0: close this websocket connection. + This callback is deprecated, use mg_set_websocket_handler instead. */ + int (*websocket_data)(struct mg_connection *, int bits, char *data, + size_t data_len); - /* 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. - Using this callback for websocket connections is deprecated, use - mg_set_websocket_handler instead. */ - void (*connection_close)(const struct mg_connection *); + /* 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. + Using this callback for websocket connections is deprecated, use + mg_set_websocket_handler instead. */ + void (*connection_close)(const 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. */ - const char *(*open_file)(const struct mg_connection *, const char *path, - size_t *data_len); + /* 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, if - Lua support is enabled. - Parameters: - lua_context: "lua_State *" pointer. */ - void (*init_lua)(const struct mg_connection *, void *lua_context); + /* Called when civetweb is about to serve Lua server page, if + Lua support is enabled. + Parameters: + lua_context: "lua_State *" pointer. */ + void (*init_lua)(const 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_name: full path name to the uploaded file. */ - void (*upload)(struct mg_connection *, const char *file_name); + /* Called when civetweb has uploaded a file to a temporary directory as a + result of mg_upload() call. + Parameters: + file_name: 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. - Return value: - 1: run civetweb error handler. - 0: callback already handled the error. */ - int (*http_error)(struct mg_connection *, int status); + /* 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. + Return value: + 1: run civetweb error handler. + 0: callback already handled the error. */ + int (*http_error)(struct mg_connection *, int status); - /* Called after civetweb context has been created, before requests - are processed. - Parameters: - ctx: context handle */ - void (*init_context)(const struct mg_context *ctx); + /* Called after civetweb context has been created, before requests + are processed. + Parameters: + ctx: context handle */ + void (*init_context)(const struct mg_context *ctx); - /* Called when civetweb context is deleted. - Parameters: - ctx: context handle */ - void (*exit_context)(const struct mg_context *ctx); + /* Called when civetweb context is deleted. + Parameters: + ctx: context handle */ + void (*exit_context)(const struct mg_context *ctx); }; /* Start web server. @@ -347,19 +346,19 @@ CIVETWEB_API const char **mg_get_valid_option_names(void); #endif struct mg_option { - const char *name; - int type; - const char *default_value; + const char *name; + int type; + const char *default_value; }; enum { - CONFIG_TYPE_UNKNOWN = 0x0, - CONFIG_TYPE_NUMBER = 0x1, - CONFIG_TYPE_STRING = 0x2, - CONFIG_TYPE_FILE = 0x3, - CONFIG_TYPE_DIRECTORY = 0x4, - CONFIG_TYPE_BOOLEAN = 0x5, - CONFIG_TYPE_EXT_PATTERN = 0x6 + CONFIG_TYPE_UNKNOWN = 0x0, + CONFIG_TYPE_NUMBER = 0x1, + CONFIG_TYPE_STRING = 0x2, + CONFIG_TYPE_FILE = 0x3, + CONFIG_TYPE_DIRECTORY = 0x4, + CONFIG_TYPE_BOOLEAN = 0x5, + CONFIG_TYPE_EXT_PATTERN = 0x6 }; /* Return array of struct mg_option, representing all valid configuration @@ -439,12 +438,12 @@ CIVETWEB_API void mg_unlock_context(struct mg_context *ctx); /* Opcodes, from http://tools.ietf.org/html/rfc6455 */ enum { - WEBSOCKET_OPCODE_CONTINUATION = 0x0, - WEBSOCKET_OPCODE_TEXT = 0x1, - WEBSOCKET_OPCODE_BINARY = 0x2, - WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8, - WEBSOCKET_OPCODE_PING = 0x9, - WEBSOCKET_OPCODE_PONG = 0xa + WEBSOCKET_OPCODE_CONTINUATION = 0x0, + WEBSOCKET_OPCODE_TEXT = 0x1, + WEBSOCKET_OPCODE_BINARY = 0x2, + WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8, + WEBSOCKET_OPCODE_PING = 0x9, + WEBSOCKET_OPCODE_PONG = 0xa }; /* Macros for enabling compiler-specific checks for printf-like arguments. */ diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp index a69648c4..5bab2aac 100644 --- a/src/CivetServer.cpp +++ b/src/CivetServer.cpp @@ -16,288 +16,288 @@ #endif bool CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn) { - UNUSED_PARAMETER(server); - UNUSED_PARAMETER(conn); - return false; + UNUSED_PARAMETER(server); + UNUSED_PARAMETER(conn); + return false; } bool CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn) { - UNUSED_PARAMETER(server); - UNUSED_PARAMETER(conn); - return false; + UNUSED_PARAMETER(server); + UNUSED_PARAMETER(conn); + return false; } bool CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn) { - UNUSED_PARAMETER(server); - UNUSED_PARAMETER(conn); - return false; + UNUSED_PARAMETER(server); + UNUSED_PARAMETER(conn); + return false; } bool CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn) { - UNUSED_PARAMETER(server); - UNUSED_PARAMETER(conn); - return false; + UNUSED_PARAMETER(server); + UNUSED_PARAMETER(conn); + return false; } bool CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn) { - UNUSED_PARAMETER(server); - UNUSED_PARAMETER(conn); - return false; + UNUSED_PARAMETER(server); + UNUSED_PARAMETER(conn); + return false; } int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata) { - const struct mg_request_info *request_info = mg_get_request_info(conn); - assert(request_info != NULL); - CivetServer *me = (CivetServer *)(request_info->user_data); - assert(me != NULL); + const struct mg_request_info *request_info = mg_get_request_info(conn); + assert(request_info != NULL); + CivetServer *me = (CivetServer *)(request_info->user_data); + assert(me != NULL); - // Happens when a request hits the server before the context is saved - if (me->context == NULL) - return 0; + // Happens when a request hits the server before the context is saved + if (me->context == NULL) + return 0; - mg_lock_context(me->context); - me->connections[conn] = CivetConnection(); - mg_unlock_context(me->context); + mg_lock_context(me->context); + me->connections[conn] = CivetConnection(); + mg_unlock_context(me->context); - CivetHandler *handler = (CivetHandler *)cbdata; + CivetHandler *handler = (CivetHandler *)cbdata; - if (handler) { - if (strcmp(request_info->request_method, "GET") == 0) { - return handler->handleGet(me, conn) ? 1 : 0; - } else if (strcmp(request_info->request_method, "POST") == 0) { - return handler->handlePost(me, conn) ? 1 : 0; - } else if (strcmp(request_info->request_method, "PUT") == 0) { - return handler->handlePut(me, conn) ? 1 : 0; - } else if (strcmp(request_info->request_method, "DELETE") == 0) { - return handler->handleDelete(me, conn) ? 1 : 0; - } else if (strcmp(request_info->request_method, "OPTIONS") == 0) { - return handler->handleOptions(me, conn) ? 1 : 0; - } - } + if (handler) { + if (strcmp(request_info->request_method, "GET") == 0) { + return handler->handleGet(me, conn) ? 1 : 0; + } else if (strcmp(request_info->request_method, "POST") == 0) { + return handler->handlePost(me, conn) ? 1 : 0; + } else if (strcmp(request_info->request_method, "PUT") == 0) { + return handler->handlePut(me, conn) ? 1 : 0; + } else if (strcmp(request_info->request_method, "DELETE") == 0) { + return handler->handleDelete(me, conn) ? 1 : 0; + } else if (strcmp(request_info->request_method, "OPTIONS") == 0) { + return handler->handleOptions(me, conn) ? 1 : 0; + } + } - return 0; // No handler found + return 0; // No handler found } CivetServer::CivetServer(const char **options, const struct mg_callbacks *_callbacks) : context(0) { - struct mg_callbacks callbacks; - memset(&callbacks, 0, sizeof(callbacks)); + struct mg_callbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); - if (_callbacks) { - callbacks = *_callbacks; - userCloseHandler = _callbacks->connection_close; - } else { - userCloseHandler = NULL; - } - callbacks.connection_close = closeHandler; - context = mg_start(&callbacks, this, options); - if (context == NULL) - throw CivetException("null context when constructing CivetServer. " - "Possible problem binding to port."); + if (_callbacks) { + callbacks = *_callbacks; + userCloseHandler = _callbacks->connection_close; + } else { + userCloseHandler = NULL; + } + callbacks.connection_close = closeHandler; + context = mg_start(&callbacks, this, options); + if (context == NULL) + throw CivetException("null context when constructing CivetServer. " + "Possible problem binding to port."); } CivetServer::~CivetServer() { close(); } void CivetServer::closeHandler(const struct mg_connection *conn) { - const struct mg_request_info *request_info = mg_get_request_info(conn); - assert(request_info != NULL); - CivetServer *me = (CivetServer *)(request_info->user_data); - assert(me != NULL); + const struct mg_request_info *request_info = mg_get_request_info(conn); + assert(request_info != NULL); + CivetServer *me = (CivetServer *)(request_info->user_data); + assert(me != NULL); - // Happens when a request hits the server before the context is saved - if (me->context == NULL) - return; + // Happens when a request hits the server before the context is saved + if (me->context == NULL) + return; - if (me->userCloseHandler) - me->userCloseHandler(conn); - mg_lock_context(me->context); - me->connections.erase(const_cast(conn)); - mg_unlock_context(me->context); + if (me->userCloseHandler) + me->userCloseHandler(conn); + mg_lock_context(me->context); + me->connections.erase(const_cast(conn)); + mg_unlock_context(me->context); } void CivetServer::addHandler(const std::string &uri, CivetHandler *handler) { - mg_set_request_handler(context, uri.c_str(), requestHandler, handler); + mg_set_request_handler(context, uri.c_str(), requestHandler, handler); } void CivetServer::removeHandler(const std::string &uri) { - mg_set_request_handler(context, uri.c_str(), NULL, NULL); + mg_set_request_handler(context, uri.c_str(), NULL, NULL); } void CivetServer::close() { - if (context) { - mg_stop(context); - context = 0; - } + if (context) { + mg_stop(context); + context = 0; + } } int CivetServer::getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue) { - // Maximum cookie length as per microsoft is 4096. - // http://msdn.microsoft.com/en-us/library/ms178194.aspx - char _cookieValue[4096]; - const char *cookie = mg_get_header(conn, "Cookie"); - int lRead = mg_get_cookie(cookie, cookieName.c_str(), _cookieValue, - sizeof(_cookieValue)); - cookieValue.clear(); - cookieValue.append(_cookieValue); - return lRead; + // Maximum cookie length as per microsoft is 4096. + // http://msdn.microsoft.com/en-us/library/ms178194.aspx + char _cookieValue[4096]; + const char *cookie = mg_get_header(conn, "Cookie"); + int lRead = mg_get_cookie(cookie, cookieName.c_str(), _cookieValue, + sizeof(_cookieValue)); + cookieValue.clear(); + cookieValue.append(_cookieValue); + return lRead; } const char *CivetServer::getHeader(struct mg_connection *conn, const std::string &headerName) { - return mg_get_header(conn, headerName.c_str()); + return mg_get_header(conn, headerName.c_str()); } void CivetServer::urlDecode(const char *src, std::string &dst, bool is_form_url_encoded) { - urlDecode(src, strlen(src), dst, is_form_url_encoded); + urlDecode(src, strlen(src), dst, is_form_url_encoded); } void CivetServer::urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded) { - int i, j, a, b; + int i, j, a, b; #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') - dst.clear(); - for (i = j = 0; i < (int)src_len; i++, j++) { - if (i < (int)src_len - 2 && src[i] == '%' && - isxdigit(*(const unsigned char *)(src + i + 1)) && - isxdigit(*(const unsigned char *)(src + i + 2))) { - a = tolower(*(const unsigned char *)(src + i + 1)); - b = tolower(*(const unsigned char *)(src + i + 2)); - dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b))); - i += 2; - } else if (is_form_url_encoded && src[i] == '+') { - dst.push_back(' '); - } else { - dst.push_back(src[i]); - } - } + dst.clear(); + for (i = j = 0; i < (int)src_len; i++, j++) { + if (i < (int)src_len - 2 && src[i] == '%' && + isxdigit(*(const unsigned char *)(src + i + 1)) && + isxdigit(*(const unsigned char *)(src + i + 2))) { + a = tolower(*(const unsigned char *)(src + i + 1)); + b = tolower(*(const unsigned char *)(src + i + 2)); + dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b))); + i += 2; + } else if (is_form_url_encoded && src[i] == '+') { + dst.push_back(' '); + } else { + dst.push_back(src[i]); + } + } } bool CivetServer::getParam(struct mg_connection *conn, const char *name, std::string &dst, size_t occurrence) { - const char *formParams = NULL; - const struct mg_request_info *ri = mg_get_request_info(conn); - assert(ri != NULL); - CivetServer *me = (CivetServer *)(ri->user_data); - assert(me != NULL); - mg_lock_context(me->context); - CivetConnection &conobj = me->connections[conn]; - mg_lock_connection(conn); - mg_unlock_context(me->context); + const char *formParams = NULL; + const struct mg_request_info *ri = mg_get_request_info(conn); + assert(ri != NULL); + CivetServer *me = (CivetServer *)(ri->user_data); + assert(me != NULL); + mg_lock_context(me->context); + CivetConnection &conobj = me->connections[conn]; + mg_lock_connection(conn); + mg_unlock_context(me->context); - if (conobj.postData != NULL) { - formParams = conobj.postData; - } else { - const char *con_len_str = mg_get_header(conn, "Content-Length"); - if (con_len_str) { - unsigned long con_len = atoi(con_len_str); - if (con_len > 0) { - // Add one extra character: in case the post-data is a text, it - // is required as 0-termination. - // Do not increment con_len, since the 0 terminating is not part - // of the content (text or binary). - conobj.postData = (char *)malloc(con_len + 1); - if (conobj.postData != NULL) { - // malloc may fail for huge requests - mg_read(conn, conobj.postData, con_len); - conobj.postData[con_len] = 0; - formParams = conobj.postData; - conobj.postDataLen = con_len; - } - } - } - } - if (formParams == NULL) { - // get requests do store html
field values in the http - // query_string - formParams = ri->query_string; - } - mg_unlock_connection(conn); + if (conobj.postData != NULL) { + formParams = conobj.postData; + } else { + const char *con_len_str = mg_get_header(conn, "Content-Length"); + if (con_len_str) { + unsigned long con_len = atoi(con_len_str); + if (con_len > 0) { + // Add one extra character: in case the post-data is a text, it + // is required as 0-termination. + // Do not increment con_len, since the 0 terminating is not part + // of the content (text or binary). + conobj.postData = (char *)malloc(con_len + 1); + if (conobj.postData != NULL) { + // malloc may fail for huge requests + mg_read(conn, conobj.postData, con_len); + conobj.postData[con_len] = 0; + formParams = conobj.postData; + conobj.postDataLen = con_len; + } + } + } + } + if (formParams == NULL) { + // get requests do store html field values in the http + // query_string + formParams = ri->query_string; + } + mg_unlock_connection(conn); - if (formParams != NULL) { - return getParam(formParams, strlen(formParams), name, dst, occurrence); - } + if (formParams != NULL) { + return getParam(formParams, strlen(formParams), name, dst, occurrence); + } - return false; + return false; } bool CivetServer::getParam(const char *data, size_t data_len, const char *name, std::string &dst, size_t occurrence) { - const char *p, *e, *s; - size_t name_len; + const char *p, *e, *s; + size_t name_len; - dst.clear(); - if (data == NULL || name == NULL || data_len == 0) { - return false; - } - name_len = strlen(name); - e = data + data_len; + dst.clear(); + if (data == NULL || name == NULL || data_len == 0) { + return false; + } + name_len = strlen(name); + e = data + data_len; - // 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--) { + // 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 - p += name_len + 1; + // Point p to variable value + p += name_len + 1; - // 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); + // 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 - urlDecode(p, (int)(s - p), dst, true); - return true; - } - } - return false; + // Decode variable into destination buffer + urlDecode(p, (int)(s - p), dst, true); + return true; + } + } + return false; } void CivetServer::urlEncode(const char *src, std::string &dst, bool append) { - urlEncode(src, strlen(src), dst, append); + urlEncode(src, strlen(src), dst, append); } void CivetServer::urlEncode(const char *src, size_t src_len, std::string &dst, bool append) { - static const char *dont_escape = "._-$,;~()"; - static const char *hex = "0123456789abcdef"; + static const char *dont_escape = "._-$,;~()"; + static const char *hex = "0123456789abcdef"; - if (!append) - dst.clear(); + if (!append) + dst.clear(); - for (; src_len > 0; src++, src_len--) { - if (isalnum(*(const unsigned char *)src) || - strchr(dont_escape, *(const unsigned char *)src) != NULL) { - dst.push_back(*src); - } else { - dst.push_back('%'); - dst.push_back(hex[(*(const unsigned char *)src) >> 4]); - dst.push_back(hex[(*(const unsigned char *)src) & 0xf]); - } - } + for (; src_len > 0; src++, src_len--) { + if (isalnum(*(const unsigned char *)src) || + strchr(dont_escape, *(const unsigned char *)src) != NULL) { + dst.push_back(*src); + } else { + dst.push_back('%'); + dst.push_back(hex[(*(const unsigned char *)src) >> 4]); + dst.push_back(hex[(*(const unsigned char *)src) & 0xf]); + } + } } std::vector CivetServer::getListeningPorts() { - std::vector ports(10); - std::vector ssl(10); - size_t size = mg_get_ports(context, ports.size(), &ports[0], &ssl[0]); - ports.resize(size); - ssl.resize(size); - return ports; + std::vector ports(10); + std::vector ssl(10); + size_t size = mg_get_ports(context, ports.size(), &ports[0], &ssl[0]); + ports.resize(size); + ssl.resize(size); + return ports; } CivetServer::CivetConnection::CivetConnection() { - postData = NULL; - postDataLen = 0; + postData = NULL; + postDataLen = 0; } CivetServer::CivetConnection::~CivetConnection() { free(postData); } diff --git a/src/civetweb.c b/src/civetweb.c index 5add937f..e725d338 100755 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -98,41 +98,41 @@ // clock_gettime is not implemented on OSX int clock_gettime(int clk_id, struct timespec *t) { - if (clk_id == CLOCK_REALTIME) { - struct timeval now; - int rv = gettimeofday(&now, NULL); - if (rv) - return rv; - t->tv_sec = now.tv_sec; - t->tv_nsec = now.tv_usec * 1000; - return 0; + if (clk_id == CLOCK_REALTIME) { + struct timeval now; + int rv = gettimeofday(&now, NULL); + if (rv) + return rv; + t->tv_sec = now.tv_sec; + t->tv_nsec = now.tv_usec * 1000; + return 0; - } else if (clk_id == CLOCK_MONOTONIC) { - static uint64_t start_time = 0; - static mach_timebase_info_data_t timebase_ifo = {0, 0}; + } else if (clk_id == CLOCK_MONOTONIC) { + static uint64_t start_time = 0; + static mach_timebase_info_data_t timebase_ifo = {0, 0}; - uint64_t now = mach_absolute_time(); + uint64_t now = mach_absolute_time(); - if (start_time == 0) { - kern_return_t mach_status = mach_timebase_info(&timebase_ifo); + if (start_time == 0) { + kern_return_t mach_status = mach_timebase_info(&timebase_ifo); #if defined(DEBUG) - assert(mach_status == KERN_SUCCESS); + assert(mach_status == KERN_SUCCESS); #else - (void)mach_status; // appease "unused variable" warning for release + (void)mach_status; // appease "unused variable" warning for release // builds #endif - start_time = now; - } + start_time = now; + } - now = - (uint64_t)((double)(now - start_time) * (double)timebase_ifo.numer / - (double)timebase_ifo.denom); + now = + (uint64_t)((double)(now - start_time) * (double)timebase_ifo.numer / + (double)timebase_ifo.denom); - t->tv_sec = now / 1000000000; - t->tv_nsec = now % 1000000000; - return 0; - } - return -1; /* EINVAL - Clock ID is unknown */ + t->tv_sec = now / 1000000000; + t->tv_nsec = now % 1000000000; + return 0; + } + return -1; /* EINVAL - Clock ID is unknown */ } #endif @@ -180,11 +180,11 @@ typedef long off_t; #endif /* _WIN32_WCE */ #define MAKEUQUAD(lo, hi) \ - ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32)) + ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32)) #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) + (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__ @@ -255,9 +255,9 @@ typedef HANDLE pthread_mutex_t; typedef DWORD pthread_key_t; typedef HANDLE pthread_t; typedef struct { - CRITICAL_SECTION threadIdSec; - int waitingthreadcount; /* The number of threads queued. */ - pthread_t *waitingthreadhdls; /* The thread handles. */ + CRITICAL_SECTION threadIdSec; + int waitingthreadcount; /* The number of threads queued. */ + pthread_t *waitingthreadhdls; /* The thread handles. */ } pthread_cond_t; #ifndef __clockid_t_defined @@ -272,8 +272,8 @@ typedef DWORD clockid_t; #ifndef _TIMESPEC_DEFINED struct timespec { - time_t tv_sec; /* seconds */ - long tv_nsec; /* nanoseconds */ + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ }; #endif @@ -297,21 +297,21 @@ typedef __int64 int64_t; /* POSIX dirent interface */ struct dirent { - char d_name[PATH_MAX]; + char d_name[PATH_MAX]; }; typedef struct DIR { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; + HANDLE handle; + WIN32_FIND_DATAW info; + struct dirent result; } DIR; #if defined(_WIN32) && !defined(POLLIN) #ifndef HAVE_POLL struct pollfd { - SOCKET fd; - short events; - short revents; + SOCKET fd; + short events; + short revents; }; #define POLLIN 0x0300 #endif @@ -403,18 +403,18 @@ 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; + 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; + return TlsSetValue(key, value) ? 0 : 1; } void *pthread_getspecific(pthread_key_t key) { return TlsGetValue(key); } @@ -440,20 +440,20 @@ static void DEBUG_TRACE_FUNC(const char *func, unsigned line, static void DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...) { - va_list args; - flockfile(stdout); - printf("*** %lu.%p.%s.%u: ", (unsigned long)time(NULL), - (void *)pthread_self(), func, line); - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - putchar('\n'); - fflush(stdout); - funlockfile(stdout); + va_list args; + flockfile(stdout); + printf("*** %lu.%p.%s.%u: ", (unsigned long)time(NULL), + (void *)pthread_self(), func, line); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putchar('\n'); + fflush(stdout); + funlockfile(stdout); } #define DEBUG_TRACE(fmt, ...) \ - DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) + DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) #else #define DEBUG_TRACE(fmt, ...) @@ -465,113 +465,113 @@ unsigned long mg_memory_debug_blockCount = 0; unsigned long mg_memory_debug_totalMemUsed = 0; static void *mg_malloc_ex(size_t size, const char *file, unsigned line) { - void *data = malloc(size + sizeof(size_t)); - void *memory = 0; - char mallocStr[256]; + void *data = malloc(size + sizeof(size_t)); + void *memory = 0; + char mallocStr[256]; - if (data) { - *(size_t *)data = size; - mg_memory_debug_totalMemUsed += size; - mg_memory_debug_blockCount++; - memory = (void *)(((char *)data) + sizeof(size_t)); - } + if (data) { + *(size_t *)data = size; + mg_memory_debug_totalMemUsed += size; + mg_memory_debug_blockCount++; + memory = (void *)(((char *)data) + sizeof(size_t)); + } - sprintf(mallocStr, "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", memory, - (unsigned long)size, mg_memory_debug_totalMemUsed, - mg_memory_debug_blockCount, file, line); + sprintf(mallocStr, "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", memory, + (unsigned long)size, mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, file, line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - return memory; + return memory; } static void *mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line) { - void *data = mg_malloc_ex(size * count, file, line); - if (data) - memset(data, 0, size); + void *data = mg_malloc_ex(size * count, file, line); + if (data) + memset(data, 0, size); - return data; + return data; } static void mg_free_ex(void *memory, const char *file, unsigned line) { - char mallocStr[256]; - void *data = (void *)(((char *)memory) - sizeof(size_t)); - size_t size; + char mallocStr[256]; + void *data = (void *)(((char *)memory) - sizeof(size_t)); + size_t size; - if (memory) { - size = *(size_t *)data; - mg_memory_debug_totalMemUsed -= size; - mg_memory_debug_blockCount--; - sprintf(mallocStr, "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", memory, - (unsigned long)size, mg_memory_debug_totalMemUsed, - mg_memory_debug_blockCount, file, line); + if (memory) { + size = *(size_t *)data; + mg_memory_debug_totalMemUsed -= size; + mg_memory_debug_blockCount--; + sprintf(mallocStr, "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", memory, + (unsigned long)size, mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, file, line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - free(data); - } + free(data); + } } static void *mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line) { - char mallocStr[256]; - void *data; - void *_realloc; - size_t oldsize; + char mallocStr[256]; + void *data; + void *_realloc; + size_t oldsize; - if (newsize) { - if (memory) { - data = (void *)(((char *)memory) - sizeof(size_t)); - oldsize = *(size_t *)data; - _realloc = realloc(data, newsize + sizeof(size_t)); - if (_realloc) { - data = _realloc; - mg_memory_debug_totalMemUsed -= oldsize; - sprintf(mallocStr, "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", - memory, (unsigned long)oldsize, - mg_memory_debug_totalMemUsed, - mg_memory_debug_blockCount, file, line); + if (newsize) { + if (memory) { + data = (void *)(((char *)memory) - sizeof(size_t)); + oldsize = *(size_t *)data; + _realloc = realloc(data, newsize + sizeof(size_t)); + if (_realloc) { + data = _realloc; + mg_memory_debug_totalMemUsed -= oldsize; + sprintf(mallocStr, "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", + memory, (unsigned long)oldsize, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, file, line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - mg_memory_debug_totalMemUsed += newsize; - sprintf(mallocStr, "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", - memory, (unsigned long)newsize, - mg_memory_debug_totalMemUsed, - mg_memory_debug_blockCount, file, line); + mg_memory_debug_totalMemUsed += newsize; + sprintf(mallocStr, "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", + memory, (unsigned long)newsize, + mg_memory_debug_totalMemUsed, + mg_memory_debug_blockCount, file, line); #if defined(_WIN32) - OutputDebugStringA(mallocStr); + OutputDebugStringA(mallocStr); #else - DEBUG_TRACE("%s", mallocStr); + DEBUG_TRACE("%s", mallocStr); #endif - *(size_t *)data = newsize; - data = (void *)(((char *)data) + sizeof(size_t)); - } else { + *(size_t *)data = newsize; + data = (void *)(((char *)data) + sizeof(size_t)); + } else { #if defined(_WIN32) - OutputDebugStringA("MEM: realloc failed\n"); + OutputDebugStringA("MEM: realloc failed\n"); #else - DEBUG_TRACE("%s", "MEM: realloc failed\n"); + DEBUG_TRACE("%s", "MEM: realloc failed\n"); #endif - return _realloc; - } - } else { - data = mg_malloc_ex(newsize, file, line); - } - } else { - data = 0; - mg_free_ex(memory, file, line); - } + return _realloc; + } + } else { + data = mg_malloc_ex(newsize, file, line); + } + } else { + data = 0; + mg_free_ex(memory, file, line); + } - return data; + return data; } #define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) @@ -588,7 +588,8 @@ static __inline void mg_free(void *a) { free(a); } #endif -/* This following lines are just meant as a reminder to use the mg-functions for +/* This following lines are just meant as a reminder to use the mg-functions + * for * memory management */ #ifdef malloc #undef malloc @@ -648,8 +649,8 @@ 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) @@ -664,15 +665,15 @@ struct ssl_func { #define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr) #define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr) #define SSL_CTX_use_PrivateKey_file \ - (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr) + (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr) #define SSL_CTX_use_certificate_file \ - (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr) + (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr) #define SSL_CTX_set_default_passwd_cb \ - (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr) + (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr) #define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr) #define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr) #define SSL_CTX_use_certificate_chain_file \ - (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr) + (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr) #define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr) #define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr) #define SSL_CTX_set_verify (*(void (*)(SSL_CTX *, int, int))ssl_sw[19].ptr) @@ -680,9 +681,9 @@ struct ssl_func { #define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr) #define CRYPTO_set_locking_callback \ - (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr) + (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr) #define CRYPTO_set_id_callback \ - (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr) + (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr) #define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr) #define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr) @@ -731,86 +732,86 @@ static const char *month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", /* Unified socket address. For IPv6 support, add IPv6 address structure in the * union u. */ union usa { - struct sockaddr sa; - struct sockaddr_in sin; + struct sockaddr sa; + struct sockaddr_in sin; #if defined(USE_IPV6) - struct sockaddr_in6 sin6; + struct sockaddr_in6 sin6; #endif }; /* Describes a string (chunk of memory). */ struct vec { - const char *ptr; - size_t len; + const char *ptr; + size_t len; }; struct file { - int is_directory; - 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 */ - int gzipped; + int is_directory; + 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 */ + int gzipped; }; #define STRUCT_FILE_INITIALIZER \ - { 0, 0, 0, NULL, NULL, 0 } + { 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. */ struct socket { - SOCKET sock; /* Listening socket */ - union usa lsa; /* Local socket address */ - union usa rsa; /* Remote socket address */ - unsigned char is_ssl; /* Is port SSL-ed */ - unsigned char ssl_redir; /* 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 char is_ssl; /* Is port SSL-ed */ + unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL + port */ }; /* 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, - ACCESS_LOG_FILE, - ENABLE_DIRECTORY_LISTING, - ERROR_LOG_FILE, - GLOBAL_PASSWORDS_FILE, - INDEX_FILES, - ENABLE_KEEP_ALIVE, - ACCESS_CONTROL_LIST, - EXTRA_MIME_TYPES, - LISTENING_PORTS, - DOCUMENT_ROOT, - SSL_CERTIFICATE, - NUM_THREADS, - RUN_AS_USER, - REWRITE, - HIDE_FILES, - REQUEST_TIMEOUT, - DECODE_URL, + CGI_EXTENSIONS, + CGI_ENVIRONMENT, + PUT_DELETE_PASSWORDS_FILE, + CGI_INTERPRETER, + PROTECT_URI, + AUTHENTICATION_DOMAIN, + SSI_EXTENSIONS, + THROTTLE, + ACCESS_LOG_FILE, + ENABLE_DIRECTORY_LISTING, + ERROR_LOG_FILE, + GLOBAL_PASSWORDS_FILE, + INDEX_FILES, + ENABLE_KEEP_ALIVE, + ACCESS_CONTROL_LIST, + EXTRA_MIME_TYPES, + LISTENING_PORTS, + DOCUMENT_ROOT, + SSL_CERTIFICATE, + NUM_THREADS, + RUN_AS_USER, + REWRITE, + HIDE_FILES, + REQUEST_TIMEOUT, + DECODE_URL, #if defined(USE_LUA) - LUA_PRELOAD_FILE, - LUA_SCRIPT_EXTENSIONS, - LUA_SERVER_PAGE_EXTENSIONS, + LUA_PRELOAD_FILE, + LUA_SCRIPT_EXTENSIONS, + LUA_SERVER_PAGE_EXTENSIONS, #endif #if defined(USE_WEBSOCKET) - WEBSOCKET_ROOT, + WEBSOCKET_ROOT, #endif #if defined(USE_LUA) && defined(USE_WEBSOCKET) - LUA_WEBSOCKET_EXTENSIONS, + LUA_WEBSOCKET_EXTENSIONS, #endif - ACCESS_CONTROL_ALLOW_ORIGIN, - ERROR_PAGES, + ACCESS_CONTROL_ALLOW_ORIGIN, + ERROR_PAGES, - NUM_OPTIONS + NUM_OPTIONS }; /* Config option name, config types, default value */ @@ -864,105 +865,105 @@ static struct mg_option config_options[] = { {NULL, CONFIG_TYPE_UNKNOWN, NULL}}; struct mg_request_handler_info { - /* Name/Pattern of the URI. */ - char *uri; - size_t uri_len; + /* Name/Pattern of the URI. */ + char *uri; + size_t uri_len; - /* URI type: ws/wss (websocket) or http/https (web page). */ - int is_websocket_handler; + /* URI type: ws/wss (websocket) or http/https (web page). */ + int is_websocket_handler; - /* Handler for http/https requests. */ - mg_request_handler handler; + /* Handler for http/https requests. */ + mg_request_handler handler; - /* Handler for ws/wss (websocket) requests. */ - mg_websocket_connect_handler connect_handler; - mg_websocket_ready_handler ready_handler; - mg_websocket_data_handler data_handler; - mg_websocket_close_handler close_handler; + /* Handler for ws/wss (websocket) requests. */ + mg_websocket_connect_handler connect_handler; + mg_websocket_ready_handler ready_handler; + mg_websocket_data_handler data_handler; + mg_websocket_close_handler close_handler; - /* User supplied argument for the handler function. */ - void *cbdata; + /* User supplied argument for the handler function. */ + void *cbdata; - /* next request handler in a linked list */ - struct mg_request_handler_info *next; + /* next request handler in a linked list */ + struct mg_request_handler_info *next; }; struct mg_context { - volatile int stop_flag; /* Should we stop event loop */ - 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 */ - int context_type; /* 1 = server context, 2 = client context */ + volatile int stop_flag; /* Should we stop event loop */ + 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 */ + int context_type; /* 1 = server context, 2 = client context */ - struct socket *listening_sockets; - in_port_t *listening_ports; - int num_listening_sockets; + struct socket *listening_sockets; + in_port_t *listening_ports; + int num_listening_sockets; - volatile int num_threads; /* Number of threads */ - pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */ - pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */ + volatile int num_threads; /* Number of threads */ + pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */ + pthread_cond_t thread_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 */ - unsigned long start_time; /* Server start time, used for authentication */ - pthread_mutex_t nonce_mutex; /* Protects nonce_count */ - unsigned long nonce_count; /* Used nonces, used for authentication */ + unsigned long start_time; /* Server start time, used for authentication */ + pthread_mutex_t nonce_mutex; /* Protects nonce_count */ + unsigned long nonce_count; /* Used nonces, used for authentication */ - char *systemName; /* What operating system is running */ + char *systemName; /* What operating system is running */ - /* linked list of uri handlers */ - struct mg_request_handler_info *request_handlers; + /* linked list of uri handlers */ + struct mg_request_handler_info *request_handlers; #if defined(USE_LUA) && defined(USE_WEBSOCKET) - /* linked list of shared lua websockets */ - struct mg_shared_lua_websocket_list *shared_lua_websockets; + /* linked list of shared lua websockets */ + struct mg_shared_lua_websocket_list *shared_lua_websockets; #endif #ifdef USE_TIMERS - struct ttimers *timers; + struct ttimers *timers; #endif }; 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 - conn_birth_time; /* Time (wall clock) when connection was established */ - struct timespec - req_time; /* Time (since system start) when the 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 */ - int is_chunked; /* Transfer-encoding is chunked: 0=no, 1=yes: data - available, - 2: all data read */ - size_t chunk_remainder; /* Unread data from the last chunk */ - 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 in_error_handler; /* 1 if in handler for user defined error pages */ - 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_connection/mg_unlock_connection to - ensure atomic transmissions for websockets */ + 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 conn_birth_time; /* Time (wall clock) when connection was + established */ + struct timespec + req_time; /* Time (since system start) when the 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 */ + int is_chunked; /* Transfer-encoding is chunked: 0=no, 1=yes: data + available, + 2: all data read */ + size_t chunk_remainder; /* Unread data from the last chunk */ + 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 in_error_handler; /* 1 if in handler for user defined error pages */ + 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_connection/mg_unlock_connection to + ensure atomic transmissions for websockets */ #if defined(USE_LUA) && defined(USE_WEBSOCKET) - void *lua_websocket_state; /* Lua_State for a websocket connection */ + void *lua_websocket_state; /* Lua_State for a websocket connection */ #endif }; @@ -970,17 +971,17 @@ static pthread_key_t sTlsKey; /* Thread local storage index */ static int sTlsInit = 0; struct mg_workerTLS { - int is_master; + int is_master; #if defined(_WIN32) && !defined(__SYMBIAN32__) - HANDLE pthread_cond_helper_mutex; + HANDLE pthread_cond_helper_mutex; #endif }; /* Directory entry */ struct de { - struct mg_connection *conn; - char *file_name; - struct file file; + struct mg_connection *conn; + char *file_name; + struct file file; }; #if defined(USE_WEBSOCKET) @@ -990,35 +991,35 @@ static int is_websocket_protocol(const struct mg_connection *conn); #endif int mg_atomic_inc(volatile int *addr) { - int ret; + int ret; #if defined(_WIN32) && !defined(__SYMBIAN32__) - /* Depending on the SDK, this is either (volatile unsigned int *) or - (volatile - LONG *), - whatever you use, the other SDK is likely to raise a warning. */ - ret = InterlockedIncrement((volatile unsigned int *)addr); + /* Depending on the SDK, this is either (volatile unsigned int *) or + (volatile + LONG *), + whatever you use, the other SDK is likely to raise a warning. */ + ret = InterlockedIncrement((volatile unsigned int *)addr); #elif defined(__GNUC__) - ret = __sync_add_and_fetch(addr, 1); + ret = __sync_add_and_fetch(addr, 1); #else - ret = (++(*addr)); + ret = (++(*addr)); #endif - return ret; + return ret; } int mg_atomic_dec(volatile int *addr) { - int ret; + int ret; #if defined(_WIN32) && !defined(__SYMBIAN32__) - /* Depending on the SDK, this is either (volatile unsigned int *) or - (volatile - LONG *), - whatever you use, the other SDK is likely to raise a warning. */ - ret = InterlockedDecrement((volatile unsigned int *)addr); + /* Depending on the SDK, this is either (volatile unsigned int *) or + (volatile + LONG *), + whatever you use, the other SDK is likely to raise a warning. */ + ret = InterlockedDecrement((volatile unsigned int *)addr); #elif defined(__GNUC__) - ret = __sync_sub_and_fetch(addr, 1); + ret = __sync_sub_and_fetch(addr, 1); #else - ret = (--(*addr)); + ret = (--(*addr)); #endif - return ret; + return ret; } #if !defined(NO_THREAD_NAME) @@ -1028,10 +1029,10 @@ http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ #pragma pack(push, 8) typedef struct tagTHREADNAME_INFO { - DWORD dwType; /* Must be 0x1000. */ - LPCSTR szName; /* Pointer to name (in user addr space). */ - DWORD dwThreadID; /* Thread ID (-1=caller thread). */ - DWORD dwFlags; /* Reserved for future use, must be zero. */ + DWORD dwType; /* Must be 0x1000. */ + LPCSTR szName; /* Pointer to name (in user addr space). */ + DWORD dwThreadID; /* Thread ID (-1=caller thread). */ + DWORD dwFlags; /* Reserved for future use, must be zero. */ } THREADNAME_INFO; #pragma pack(pop) #elif defined(__linux__) @@ -1039,43 +1040,43 @@ typedef struct tagTHREADNAME_INFO { #endif void mg_set_thread_name(const char *name) { - char threadName[16]; /* Max. thread length in Linux/OSX/.. */ + char threadName[16]; /* Max. thread length in Linux/OSX/.. */ - if (snprintf(threadName, sizeof(threadName), "civetweb-%s", name) < 0) - return; - threadName[sizeof(threadName) - 1] = 0; + if (snprintf(threadName, sizeof(threadName), "civetweb-%s", name) < 0) + return; + threadName[sizeof(threadName) - 1] = 0; #if defined(_WIN32) #if defined(_MSC_VER) - /* Windows and Visual Studio Compiler */ - __try { - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = threadName; - info.dwThreadID = -1; - info.dwFlags = 0; + /* Windows and Visual Studio Compiler */ + __try { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = -1; + info.dwFlags = 0; - RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), - (ULONG_PTR *)&info); - } - __except(EXCEPTION_EXECUTE_HANDLER) {} + RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR *)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) {} #elif defined(__MINGW32__) /* No option known to set thread name for MinGW */ #endif #elif defined(__linux__) - /* Linux */ - (void)prctl(PR_SET_NAME, threadName, 0, 0, 0); + /* Linux */ + (void)prctl(PR_SET_NAME, threadName, 0, 0, 0); #elif defined(__APPLE__) || defined(__MACH__) - /* OS X (TODO: test) */ - (void)pthread_setname_np(threadName); + /* OS X (TODO: test) */ + (void)pthread_setname_np(threadName); #elif defined(BSD) || defined(__FreeBSD__) || defined(__OpenBSD__) - /* BSD (TODO: test) */ - pthread_set_name_np(pthread_self(), threadName); + /* BSD (TODO: test) */ + pthread_set_name_np(pthread_self(), threadName); #elif defined(__AIX__) || defined(_AIX) || defined(__hpux) || defined(__sun) /* pthread_set_name_np seems to be missing on AIX, hpux, sun, ... */ #else - /* POSIX */ - (void)pthread_setname_np(pthread_self(), threadName); + /* POSIX */ + (void)pthread_setname_np(pthread_self(), threadName); #endif } #else /* !defined(NO_THREAD_NAME) */ @@ -1084,16 +1085,16 @@ void mg_set_thread_name(const char *threadName) {} #if defined(MG_LEGACY_INTERFACE) const char **mg_get_valid_option_names(void) { - static const char * - data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; - int i; + static const char * + data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; + int i; - for (i = 0; config_options[i].name != NULL; i++) { - data[i * 2] = config_options[i].name; - data[i * 2 + 1] = config_options[i].default_value; - } + for (i = 0; config_options[i].name != NULL; i++) { + data[i * 2] = config_options[i].name; + data[i * 2 + 1] = config_options[i].default_value; + } - return data; + return data; } #endif @@ -1101,108 +1102,108 @@ const struct mg_option *mg_get_valid_options(void) { return config_options; } static int is_file_in_memory(struct mg_connection *conn, const char *path, struct file *filep) { - size_t size = 0; - if (!conn || !filep) - return 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()) ... */ - filep->size = size; - } - return filep->membuf != NULL; + size_t size = 0; + if (!conn || !filep) + return 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()) ... */ + filep->size = size; + } + return filep->membuf != NULL; } static int is_file_opened(const struct file *filep) { - if (!filep) { - return 0; - } + if (!filep) { + return 0; + } - return filep->membuf != NULL || filep->fp != NULL; + return filep->membuf != NULL || filep->fp != NULL; } static int mg_fopen(struct mg_connection *conn, const char *path, const char *mode, struct file *filep) { - if (!filep) { - return 0; - } + if (!filep) { + return 0; + } - if (!is_file_in_memory(conn, path, filep)) { + if (!is_file_in_memory(conn, path, filep)) { #ifdef _WIN32 - wchar_t wbuf[PATH_MAX], wmode[20]; - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); - filep->fp = _wfopen(wbuf, wmode); + wchar_t wbuf[PATH_MAX], wmode[20]; + to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); + MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); + filep->fp = _wfopen(wbuf, wmode); #else - filep->fp = fopen(path, mode); + filep->fp = fopen(path, mode); #endif - } + } - return is_file_opened(filep); + return is_file_opened(filep); } static void mg_fclose(struct file *filep) { - if (filep != NULL && filep->fp != NULL) { - fclose(filep->fp); - } + if (filep != NULL && filep->fp != NULL) { + fclose(filep->fp); + } } static void mg_strlcpy(register char *dst, register const char *src, size_t n) { - for (; *src != '\0' && n > 1; n--) { - *dst++ = *src++; - } - *dst = '\0'; + for (; *src != '\0' && n > 1; n--) { + *dst++ = *src++; + } + *dst = '\0'; } static int lowercase(const char *s) { - return tolower(*(const unsigned char *)s); + return tolower(*(const unsigned char *)s); } int mg_strncasecmp(const char *s1, const char *s2, size_t len) { - int diff = 0; + int diff = 0; - if (len > 0) - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0' && --len > 0); + if (len > 0) + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); - return diff; + return diff; } static int mg_strcasecmp(const char *s1, const char *s2) { - int diff; + int diff; - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0'); + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0'); - return diff; + return diff; } static char *mg_strndup(const char *ptr, size_t len) { - char *p; + char *p; - if ((p = (char *)mg_malloc(len + 1)) != NULL) { - mg_strlcpy(p, ptr, len + 1); - } + if ((p = (char *)mg_malloc(len + 1)) != NULL) { + mg_strlcpy(p, ptr, len + 1); + } - return p; + return p; } static char *mg_strdup(const char *str) { return mg_strndup(str, strlen(str)); } static const char *mg_strcasestr(const char *big_str, const char *small_str) { - int i, big_len = (int)strlen(big_str), small_len = (int)strlen(small_str); + int i, big_len = (int)strlen(big_str), small_len = (int)strlen(small_str); - for (i = 0; i <= big_len - small_len; i++) { - if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { - return big_str + i; - } - } + for (i = 0; i <= big_len - small_len; i++) { + if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { + return big_str + i; + } + } - return NULL; + return NULL; } /* Like snprintf(), but never returns negative value, or a value @@ -1211,10 +1212,10 @@ 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) { - int n; + int n; - if (buflen == 0) - return 0; + if (buflen == 0) + return 0; #ifdef __clang__ #pragma clang diagnostic push @@ -1223,23 +1224,23 @@ static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen, * indirectly by mg_snprintf */ #endif - n = vsnprintf(buf, buflen, fmt, ap); + n = vsnprintf(buf, buflen, fmt, ap); #ifdef __clang__ #pragma clang diagnostic pop #endif - if (n < 0) { - mg_cry(conn, "vsnprintf error"); - n = 0; - } else if (n >= (int)buflen) { - mg_cry(conn, "truncating vsnprintf buffer: [%.*s]", n > 200 ? 200 : n, - buf); - n = (int)buflen - 1; - } - buf[n] = '\0'; + if (n < 0) { + mg_cry(conn, "vsnprintf error"); + n = 0; + } else if (n >= (int)buflen) { + mg_cry(conn, "truncating vsnprintf buffer: [%.*s]", n > 200 ? 200 : n, + buf); + n = (int)buflen - 1; + } + buf[n] = '\0'; - return n; + return n; } static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, @@ -1248,84 +1249,84 @@ static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, const char *fmt, ...) { - va_list ap; - int n; + va_list ap; + int n; - va_start(ap, fmt); - n = mg_vsnprintf(conn, buf, buflen, fmt, ap); - va_end(ap); + va_start(ap, fmt); + n = mg_vsnprintf(conn, buf, buflen, fmt, ap); + va_end(ap); - return n; + return n; } static int get_option_index(const char *name) { - int i; + int i; - for (i = 0; config_options[i].name != NULL; i++) { - if (strcmp(config_options[i].name, name) == 0) { - return i; - } - } - return -1; + for (i = 0; config_options[i].name != NULL; i++) { + if (strcmp(config_options[i].name, name) == 0) { + return i; + } + } + return -1; } const char *mg_get_option(const struct mg_context *ctx, const char *name) { - int i; - if ((i = get_option_index(name)) == -1) { - return NULL; - } else if (!ctx || ctx->config[i] == NULL) { - return ""; - } else { - return ctx->config[i]; - } + int i; + if ((i = get_option_index(name)) == -1) { + return NULL; + } else if (!ctx || ctx->config[i] == NULL) { + return ""; + } else { + return ctx->config[i]; + } } struct mg_context *mg_get_context(const struct mg_connection *conn) { - return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx); + return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx); } void *mg_get_user_data(const struct mg_context *ctx) { - return (ctx == NULL) ? NULL : ctx->user_data; + return (ctx == NULL) ? NULL : ctx->user_data; } void mg_set_user_connection_data(const struct mg_connection *conn, void *data) { - if (conn != NULL) { - ((struct mg_connection *)conn)->request_info.conn_data = data; - } + if (conn != NULL) { + ((struct mg_connection *)conn)->request_info.conn_data = data; + } } void *mg_get_user_connection_data(const struct mg_connection *conn) { - if (conn != NULL) { - return conn->request_info.conn_data; - } - return NULL; + if (conn != NULL) { + return conn->request_info.conn_data; + } + return NULL; } size_t mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl) { - size_t i; - if (!ctx) { - return 0; - } - for (i = 0; i < size && i < (size_t)ctx->num_listening_sockets; i++) { - ssl[i] = ctx->listening_sockets[i].is_ssl; - ports[i] = ctx->listening_ports[i]; - } - return i; + size_t i; + if (!ctx) { + return 0; + } + for (i = 0; i < size && i < (size_t)ctx->num_listening_sockets; i++) { + ssl[i] = ctx->listening_sockets[i].is_ssl; + ports[i] = ctx->listening_ports[i]; + } + return i; } static void sockaddr_to_string(char *buf, size_t len, const union usa *usa) { - buf[0] = '\0'; + buf[0] = '\0'; - if (usa->sa.sa_family == AF_INET) { - getnameinfo(&usa->sa, sizeof(usa->sin), buf, len, NULL, 0, - NI_NUMERICHOST); - } + if (usa->sa.sa_family == AF_INET) { + getnameinfo(&usa->sa, sizeof(usa->sin), buf, len, NULL, 0, + NI_NUMERICHOST); + } #if defined(USE_IPV6) - else if (usa->sa.sa_family == AF_INET6) { - getnameinfo(&usa->sa, sizeof(usa->sin6), buf, len, NULL, 0, - NI_NUMERICHOST); - } + else if (usa->sa.sa_family == AF_INET6) { + getnameinfo(&usa->sa, sizeof(usa->sin6), buf, len, NULL, 0, + NI_NUMERICHOST); + } #endif #if 0 @@ -1346,81 +1347,81 @@ static void sockaddr_to_string(char *buf, size_t len, const union usa *usa) { /* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be * included in all responses other than 100, 101, 5xx. */ static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { - struct tm *tm; + struct tm *tm; - tm = gmtime(t); - if (tm != NULL) { - strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); - } else { - mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); - buf[buf_len - 1] = '\0'; - } + tm = gmtime(t); + if (tm != NULL) { + strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); + } else { + mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); + buf[buf_len - 1] = '\0'; + } } /* difftime for struct timespec. Return value is in seconds. */ static double mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before) { - return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9 + - (double)(ts_now->tv_sec - ts_before->tv_sec); + return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9 + + (double)(ts_now->tv_sec - ts_before->tv_sec); } /* Print error message to the opened error log stream. */ void mg_cry(const struct mg_connection *conn, const char *fmt, ...) { - char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; - va_list ap; - FILE *fp; - time_t timestamp; + char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; + va_list ap; + FILE *fp; + time_t timestamp; - va_start(ap, fmt); - IGNORE_UNUSED_RESULT(vsnprintf(buf, sizeof(buf), fmt, ap)); - va_end(ap); + va_start(ap, 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. */ - if (conn && (conn->ctx->callbacks.log_message == NULL || - conn->ctx->callbacks.log_message(conn, buf) == 0)) { - fp = conn->ctx->config[ERROR_LOG_FILE] == NULL - ? NULL - : fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); + /* 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 && (conn->ctx->callbacks.log_message == NULL || + conn->ctx->callbacks.log_message(conn, buf) == 0)) { + fp = conn->ctx->config[ERROR_LOG_FILE] == NULL + ? NULL + : fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); - if (fp != NULL) { - flockfile(fp); - timestamp = time(NULL); + if (fp != NULL) { + flockfile(fp); + timestamp = time(NULL); - sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - fprintf(fp, "[%010lu] [error] [client %s] ", - (unsigned long)timestamp, src_addr); + sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); + fprintf(fp, "[%010lu] [error] [client %s] ", + (unsigned long)timestamp, src_addr); - if (conn->request_info.request_method != NULL) { - fprintf(fp, "%s %s: ", conn->request_info.request_method, - conn->request_info.uri); - } + if (conn->request_info.request_method != NULL) { + fprintf(fp, "%s %s: ", conn->request_info.request_method, + conn->request_info.uri); + } - fprintf(fp, "%s", buf); - fputc('\n', fp); - funlockfile(fp); - fclose(fp); - } - } + fprintf(fp, "%s", buf); + fputc('\n', fp); + funlockfile(fp); + fclose(fp); + } + } } /* 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; - fake_connection.ctx = ctx; - return &fake_connection; + static struct mg_connection fake_connection; + fake_connection.ctx = ctx; + return &fake_connection; } const char *mg_version(void) { return CIVETWEB_VERSION; } const struct mg_request_info * mg_get_request_info(const struct mg_connection *conn) { - if (!conn) { - return NULL; - } - return &conn->request_info; + if (!conn) { + return NULL; + } + return &conn->request_info; } /* Skip the characters until one of the delimiters characters found. @@ -1429,77 +1430,77 @@ 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) { - char *p, *begin_word, *end_word, *end_whitespace; + char *p, *begin_word, *end_word, *end_whitespace; - begin_word = *buf; - end_word = begin_word + strcspn(begin_word, delimiters); + begin_word = *buf; + end_word = begin_word + strcspn(begin_word, delimiters); - /* Check for quotechar */ - if (end_word > begin_word) { - p = end_word - 1; - while (*p == quotechar) { - /* TODO (bel): it seems this code is never reached, so quotechar is - actually - not needed - check if this code may be droped */ + /* Check for quotechar */ + if (end_word > begin_word) { + p = end_word - 1; + while (*p == quotechar) { + /* TODO (bel): it seems this code is never reached, so quotechar is + actually + not needed - check if this code may be droped */ - /* 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 */ - end_word += end_off + 1; - } - } - for (p++; p < end_word; p++) { - *p = '\0'; - } - } + /* 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 */ + end_word += end_off + 1; + } + } + for (p++; p < end_word; p++) { + *p = '\0'; + } + } - if (*end_word == '\0') { - *buf = end_word; - } else { - end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); + if (*end_word == '\0') { + *buf = end_word; + } else { + end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); - for (p = end_word; p < end_whitespace; p++) { - *p = '\0'; - } + for (p = end_word; p < end_whitespace; p++) { + *p = '\0'; + } - *buf = end_whitespace; - } + *buf = end_whitespace; + } - return begin_word; + return begin_word; } /* 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 skip_quoted(buf, delimiters, delimiters, 0); } /* Return HTTP header value, or NULL if not found. */ static const char *get_header(const struct mg_request_info *ri, const char *name) { - int i; - if (ri) { - for (i = 0; i < ri->num_headers; i++) { - if (!mg_strcasecmp(name, ri->http_headers[i].name)) { - return ri->http_headers[i].value; - } - } - } + int i; + if (ri) { + for (i = 0; i < ri->num_headers; i++) { + if (!mg_strcasecmp(name, ri->http_headers[i].name)) { + return ri->http_headers[i].value; + } + } + } - return NULL; + return NULL; } const char *mg_get_header(const struct mg_connection *conn, const char *name) { - if (!conn) { - return NULL; - } + if (!conn) { + return NULL; + } - return get_header(&conn->request_info, name); + return get_header(&conn->request_info, name); } /* A helper function for traversing a comma separated list of values. @@ -1510,108 +1511,108 @@ const char *mg_get_header(const struct mg_connection *conn, const char *name) { * is adjusted to point only to "x". */ static const char *next_option(const char *list, struct vec *val, struct vec *eq_val) { - if (val == NULL || list == NULL || *list == '\0') { - /* 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 */ - val->len = list - val->ptr; - list++; - } else { - /* This value is the last one */ - list = val->ptr + strlen(val->ptr); - val->len = list - val->ptr; - } + if (val == NULL || list == NULL || *list == '\0') { + /* 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 */ + val->len = list - val->ptr; + list++; + } else { + /* 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". */ - 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->len = val->ptr + val->len - eq_val->ptr; - val->len = (eq_val->ptr - val->ptr) - 1; - } - } - } + 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". */ + 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->len = val->ptr + val->len - eq_val->ptr; + val->len = (eq_val->ptr - val->ptr) - 1; + } + } + } - return list; + return list; } /* 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; - int i, j, len, res; + const char *or_str; + int i, j, len, res; - if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) { - res = match_prefix(pattern, (int)(or_str - pattern), str); - return res > 0 - ? res - : match_prefix(or_str + 1, - (int)((pattern + pattern_len) - (or_str + 1)), - str); - } + if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) { + res = match_prefix(pattern, (int)(or_str - pattern), str); + return res > 0 + ? res + : match_prefix(or_str + 1, + (int)((pattern + pattern_len) - (or_str + 1)), + str); + } - i = j = 0; - for (; i < pattern_len; i++, j++) { - if (pattern[i] == '?' && str[j] != '\0') { - continue; - } else if (pattern[i] == '$') { - return str[j] == '\0' ? j : -1; - } else if (pattern[i] == '*') { - i++; - if (pattern[i] == '*') { - i++; - len = (int)strlen(str + j); - } else { - len = (int)strcspn(str + j, "/"); - } - if (i == pattern_len) { - return j + len; - } - do { - res = match_prefix(pattern + i, pattern_len - i, str + j + len); - } while (res == -1 && len-- > 0); - return res == -1 ? -1 : j + res + len; - } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { - return -1; - } - } - return j; + i = j = 0; + for (; i < pattern_len; i++, j++) { + if (pattern[i] == '?' && str[j] != '\0') { + continue; + } else if (pattern[i] == '$') { + return str[j] == '\0' ? j : -1; + } else if (pattern[i] == '*') { + i++; + if (pattern[i] == '*') { + i++; + len = (int)strlen(str + j); + } else { + len = (int)strcspn(str + j, "/"); + } + if (i == pattern_len) { + return j + len; + } + do { + res = match_prefix(pattern + i, pattern_len - i, str + j + len); + } while (res == -1 && len-- > 0); + return res == -1 ? -1 : j + res + len; + } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { + return -1; + } + } + 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. */ static int should_keep_alive(const struct mg_connection *conn) { - if (conn != NULL) { - const char *http_version = conn->request_info.http_version; - const char *header = mg_get_header(conn, "Connection"); - if (conn->must_close || conn->status_code == 401 || - mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || - (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || - (header == NULL && http_version && - 0 != strcmp(http_version, "1.1"))) { - return 0; - } - return 1; - } - return 0; + if (conn != NULL) { + const char *http_version = conn->request_info.http_version; + const char *header = mg_get_header(conn, "Connection"); + if (conn->must_close || conn->status_code == 401 || + mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || + (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || + (header == NULL && http_version && + 0 != strcmp(http_version, "1.1"))) { + return 0; + } + return 1; + } + return 0; } static int should_decode_url(const struct mg_connection *conn) { - if (!conn || !conn->ctx) { - return 0; - } + if (!conn || !conn->ctx) { + return 0; + } - return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0); + return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0); } static const char *suggest_connection_header(const struct mg_connection *conn) { - return should_keep_alive(conn) ? "keep-alive" : "close"; + return should_keep_alive(conn) ? "keep-alive" : "close"; } static void handle_file_based_request(struct mg_connection *conn, @@ -1621,180 +1622,181 @@ static int mg_stat(struct mg_connection *conn, const char *path, static const char *mg_get_response_code_text(int response_code, struct mg_connection *conn) { - switch (response_code) { - /* RFC2616 Section 10.1 - Informational 1xx */ - case 100: - return "Continue"; /* RFC2616 Section 10.1.1 */ - case 101: - return "Switching Protocols"; /* RFC2616 Section 10.1.2 */ - case 102: - return "Processing"; /* RFC2518 Section 10.1 */ + switch (response_code) { + /* RFC2616 Section 10.1 - Informational 1xx */ + case 100: + return "Continue"; /* RFC2616 Section 10.1.1 */ + case 101: + return "Switching Protocols"; /* RFC2616 Section 10.1.2 */ + case 102: + return "Processing"; /* RFC2518 Section 10.1 */ - /* RFC2616 Section 10.2 - Successful 2xx */ - case 200: - return "OK"; /* RFC2616 Section 10.2.1 */ - case 201: - return "Created"; /* RFC2616 Section 10.2.2 */ - case 202: - return "Accepted"; /* RFC2616 Section 10.2.3 */ - case 203: - return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */ - case 204: - return "No Content"; /* RFC2616 Section 10.2.5 */ - case 205: - return "Reset Content"; /* RFC2616 Section 10.2.6 */ - case 206: - return "Partial Content"; /* RFC2616 Section 10.2.7 */ - case 207: - return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */ + /* RFC2616 Section 10.2 - Successful 2xx */ + case 200: + return "OK"; /* RFC2616 Section 10.2.1 */ + case 201: + return "Created"; /* RFC2616 Section 10.2.2 */ + case 202: + return "Accepted"; /* RFC2616 Section 10.2.3 */ + case 203: + return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */ + case 204: + return "No Content"; /* RFC2616 Section 10.2.5 */ + case 205: + return "Reset Content"; /* RFC2616 Section 10.2.6 */ + case 206: + return "Partial Content"; /* RFC2616 Section 10.2.7 */ + case 207: + return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */ - /* RFC2616 Section 10.3 - Redirection 3xx */ - case 300: - return "Multiple Choices"; /* RFC2616 Section 10.3.1 */ - case 301: - return "Moved Permanently"; /* RFC2616 Section 10.3.2 */ - case 302: - return "Found"; /* RFC2616 Section 10.3.3 */ - case 303: - return "See Other"; /* RFC2616 Section 10.3.4 */ - case 304: - return "Not Modified"; /* RFC2616 Section 10.3.5 */ - case 305: - return "Use Proxy"; /* RFC2616 Section 10.3.6 */ - case 307: - return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */ + /* RFC2616 Section 10.3 - Redirection 3xx */ + case 300: + return "Multiple Choices"; /* RFC2616 Section 10.3.1 */ + case 301: + return "Moved Permanently"; /* RFC2616 Section 10.3.2 */ + case 302: + return "Found"; /* RFC2616 Section 10.3.3 */ + case 303: + return "See Other"; /* RFC2616 Section 10.3.4 */ + case 304: + return "Not Modified"; /* RFC2616 Section 10.3.5 */ + case 305: + return "Use Proxy"; /* RFC2616 Section 10.3.6 */ + case 307: + return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */ - /* RFC2616 Section 10.4 - Client Error 4xx */ - case 400: - return "Bad Request"; /* RFC2616 Section 10.4.1 */ - case 401: - return "Unauthorized"; /* RFC2616 Section 10.4.2 */ - case 402: - return "Payment Required"; /* RFC2616 Section 10.4.3 */ - case 403: - return "Forbidden"; /* RFC2616 Section 10.4.4 */ - case 404: - return "Not Found"; /* RFC2616 Section 10.4.5 */ - case 405: - return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */ - case 406: - return "Not Acceptable"; /* RFC2616 Section 10.4.7 */ - case 407: - return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */ - case 408: - return "Request Time-out"; /* RFC2616 Section 10.4.9 */ - case 409: - return "Conflict"; /* RFC2616 Section 10.4.10 */ - case 410: - return "Gone"; /* RFC2616 Section 10.4.11 */ - case 411: - return "Length Required"; /* RFC2616 Section 10.4.12 */ - case 412: - return "Precondition Failed"; /* RFC2616 Section 10.4.13 */ - case 413: - return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */ - case 414: - return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */ - case 415: - return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */ - case 416: - return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 */ - case 417: - return "Expectation Failed"; /* RFC2616 Section 10.4.18 */ - case 422: - return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918 Section - 11.2 */ - case 423: - return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */ - case 424: - return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918 Section - * 11.4 - */ - case 428: - return "Precondition Required"; /* RFC 6585, Section 3 */ - case 429: - return "Too Many Requests"; /* RFC 6585, Section 4 */ - case 431: - return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */ + /* RFC2616 Section 10.4 - Client Error 4xx */ + case 400: + return "Bad Request"; /* RFC2616 Section 10.4.1 */ + case 401: + return "Unauthorized"; /* RFC2616 Section 10.4.2 */ + case 402: + return "Payment Required"; /* RFC2616 Section 10.4.3 */ + case 403: + return "Forbidden"; /* RFC2616 Section 10.4.4 */ + case 404: + return "Not Found"; /* RFC2616 Section 10.4.5 */ + case 405: + return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */ + case 406: + return "Not Acceptable"; /* RFC2616 Section 10.4.7 */ + case 407: + return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */ + case 408: + return "Request Time-out"; /* RFC2616 Section 10.4.9 */ + case 409: + return "Conflict"; /* RFC2616 Section 10.4.10 */ + case 410: + return "Gone"; /* RFC2616 Section 10.4.11 */ + case 411: + return "Length Required"; /* RFC2616 Section 10.4.12 */ + case 412: + return "Precondition Failed"; /* RFC2616 Section 10.4.13 */ + case 413: + return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */ + case 414: + return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */ + case 415: + return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */ + case 416: + return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 */ + case 417: + return "Expectation Failed"; /* RFC2616 Section 10.4.18 */ + case 422: + return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918 + Section + 11.2 */ + case 423: + return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */ + case 424: + return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918 Section + * 11.4 + */ + case 428: + return "Precondition Required"; /* RFC 6585, Section 3 */ + case 429: + return "Too Many Requests"; /* RFC 6585, Section 4 */ + case 431: + return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */ - /* RFC2616 Section 10.5 - Server Error 5xx */ - case 500: - return "Internal Server Error"; /* RFC2616 Section 10.5.1 */ - case 501: - return "Not Implemented"; /* RFC2616 Section 10.5.2 */ - case 502: - return "Bad Gateway"; /* RFC2616 Section 10.5.3 */ - case 503: - return "Service Unavailable"; /* RFC2616 Section 10.5.4 */ - case 504: - return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */ - case 505: - return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */ - case 507: - return "Insufficient Storage"; /* RFC2518 Section 10.6, , RFC4918 - Section - 11.5 */ - case 511: - return "Network Authentication Required"; /* RFC 6585, Section 6 */ + /* RFC2616 Section 10.5 - Server Error 5xx */ + case 500: + return "Internal Server Error"; /* RFC2616 Section 10.5.1 */ + case 501: + return "Not Implemented"; /* RFC2616 Section 10.5.2 */ + case 502: + return "Bad Gateway"; /* RFC2616 Section 10.5.3 */ + case 503: + return "Service Unavailable"; /* RFC2616 Section 10.5.4 */ + case 504: + return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */ + case 505: + return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */ + case 507: + return "Insufficient Storage"; /* RFC2518 Section 10.6, , RFC4918 + Section + 11.5 */ + case 511: + return "Network Authentication Required"; /* RFC 6585, Section 6 */ - /* Other RFCs */ - case 426: - return "Upgrade Required"; /* RFC 2817 */ + /* Other RFCs */ + case 426: + return "Upgrade Required"; /* RFC 2817 */ - /* Return codes from non normative RFCs: */ - /* Informative and experimental RFCs, "de facto" standards due to common - * use, ... */ - case 208: - return "Already Reported"; /* RFC5842 Section 7.1 */ - case 226: - return "IM used"; /* RFC3229 Section 10.4.1 */ - case 308: - return "Permanent Redirect"; /* RFC7238 Section 3 */ - case 418: - return "I am a teapot"; /* RFC2324 Section 2.3.2 */ - case 419: - return "Authentication Timeout"; /* common use */ - case 451: - return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05, - Section 3 */ - case 506: - return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */ - case 508: - return "Loop Detected"; /* RFC5842 Section 7.1 */ - case 510: - return "Not Extended"; /* RFC 2774, Section 7 */ + /* Return codes from non normative RFCs: */ + /* Informative and experimental RFCs, "de facto" standards due to common + * use, ... */ + case 208: + return "Already Reported"; /* RFC5842 Section 7.1 */ + case 226: + return "IM used"; /* RFC3229 Section 10.4.1 */ + case 308: + return "Permanent Redirect"; /* RFC7238 Section 3 */ + case 418: + return "I am a teapot"; /* RFC2324 Section 2.3.2 */ + case 419: + return "Authentication Timeout"; /* common use */ + case 451: + return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05, + Section 3 */ + case 506: + return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */ + case 508: + return "Loop Detected"; /* RFC5842 Section 7.1 */ + case 510: + return "Not Extended"; /* RFC 2774, Section 7 */ - default: - /* This error code is unknown. This should not happen. */ - if (conn) { - mg_cry(conn, "Unknown HTTP response code: %u", response_code); - } + default: + /* This error code is unknown. This should not happen. */ + if (conn) { + mg_cry(conn, "Unknown HTTP response code: %u", response_code); + } - /* Return at least a category according to RFC 2616 Section 10. */ - if (response_code >= 100 && response_code < 200) { - /* Unknown informational status code */ - return "Information"; - } - if (response_code >= 200 && response_code < 300) { - /* Unknown success code */ - return "Success"; - } - if (response_code >= 300 && response_code < 400) { - /* Unknown redirection code */ - return "Redirection"; - } - if (response_code >= 400 && response_code < 500) { - /* Unknown request error code */ - return "Client Error"; - } - if (response_code >= 500 && response_code < 600) { - /* Unknown server error code */ - return "Server Error"; - } + /* Return at least a category according to RFC 2616 Section 10. */ + if (response_code >= 100 && response_code < 200) { + /* Unknown informational status code */ + return "Information"; + } + if (response_code >= 200 && response_code < 300) { + /* Unknown success code */ + return "Success"; + } + if (response_code >= 300 && response_code < 400) { + /* Unknown redirection code */ + return "Redirection"; + } + if (response_code >= 400 && response_code < 500) { + /* Unknown request error code */ + return "Client Error"; + } + if (response_code >= 500 && response_code < 600) { + /* Unknown server error code */ + return "Server Error"; + } - /* Response code not even within reasonable range */ - return ""; - } + /* Response code not even within reasonable range */ + return ""; + } } static void send_http_error(struct mg_connection *, int, @@ -1803,348 +1805,348 @@ static void send_http_error(struct mg_connection *, int, static void send_http_error(struct mg_connection *conn, int status, const char *fmt, ...) { - char buf[MG_BUF_LEN]; - va_list ap; - int len = 0, i, page_handler_found, scope; - char date[64]; - time_t curtime = time(NULL); - const char *error_handler = NULL; - struct file error_page_file = STRUCT_FILE_INITIALIZER; - const char *error_page_file_ext, *tstr; + char buf[MG_BUF_LEN]; + va_list ap; + int len = 0, i, page_handler_found, scope; + char date[64]; + time_t curtime = time(NULL); + const char *error_handler = NULL; + struct file error_page_file = STRUCT_FILE_INITIALIZER; + const char *error_page_file_ext, *tstr; - const char *status_text = mg_get_response_code_text(status, conn); + const char *status_text = mg_get_response_code_text(status, conn); - if (conn == NULL) { - return; - } + if (conn == NULL) { + return; + } - conn->status_code = status; - if (conn->in_error_handler || conn->ctx->callbacks.http_error == NULL || - conn->ctx->callbacks.http_error(conn, status)) { - if (!conn->in_error_handler) { - /* Send user defined error pages, if defined */ - error_handler = conn->ctx->config[ERROR_PAGES]; - error_page_file_ext = conn->ctx->config[INDEX_FILES]; - page_handler_found = 0; - if (error_handler != NULL) { - for (scope = 1; (scope <= 3) && !page_handler_found; scope++) { - switch (scope) { - case 1: - len = - mg_snprintf(conn, buf, sizeof(buf) - 32, - "%serror%03u.", error_handler, status); - break; - case 2: - len = mg_snprintf(conn, buf, sizeof(buf) - 32, - "%serror%01uxx.", error_handler, - status / 100); - break; - default: - len = mg_snprintf(conn, buf, sizeof(buf) - 32, - "%serror.", error_handler); - break; - } - tstr = strchr(error_page_file_ext, '.'); - while (tstr) { - for (i = 1; i < 32 && tstr[i] != 0 && tstr[i] != ','; - i++) - buf[len + i - 1] = tstr[i]; - buf[len + i - 1] = 0; - if (mg_stat(conn, buf, &error_page_file)) { - page_handler_found = 1; - break; - } - tstr = strchr(tstr + i, '.'); - } - } - } + conn->status_code = status; + if (conn->in_error_handler || conn->ctx->callbacks.http_error == NULL || + conn->ctx->callbacks.http_error(conn, status)) { + if (!conn->in_error_handler) { + /* Send user defined error pages, if defined */ + error_handler = conn->ctx->config[ERROR_PAGES]; + error_page_file_ext = conn->ctx->config[INDEX_FILES]; + page_handler_found = 0; + if (error_handler != NULL) { + for (scope = 1; (scope <= 3) && !page_handler_found; scope++) { + switch (scope) { + case 1: + len = + mg_snprintf(conn, buf, sizeof(buf) - 32, + "%serror%03u.", error_handler, status); + break; + case 2: + len = mg_snprintf(conn, buf, sizeof(buf) - 32, + "%serror%01uxx.", error_handler, + status / 100); + break; + default: + len = mg_snprintf(conn, buf, sizeof(buf) - 32, + "%serror.", error_handler); + break; + } + tstr = strchr(error_page_file_ext, '.'); + while (tstr) { + for (i = 1; i < 32 && tstr[i] != 0 && tstr[i] != ','; + i++) + buf[len + i - 1] = tstr[i]; + buf[len + i - 1] = 0; + if (mg_stat(conn, buf, &error_page_file)) { + page_handler_found = 1; + break; + } + tstr = strchr(tstr + i, '.'); + } + } + } - if (page_handler_found) { - conn->in_error_handler = 1; - handle_file_based_request(conn, buf, &error_page_file); - conn->in_error_handler = 0; - return; - } - } + if (page_handler_found) { + conn->in_error_handler = 1; + handle_file_based_request(conn, buf, &error_page_file); + conn->in_error_handler = 0; + return; + } + } - buf[0] = '\0'; - gmt_time_string(date, sizeof(date), &curtime); + buf[0] = '\0'; + gmt_time_string(date, sizeof(date), &curtime); - /* Errors 1xx, 204 and 304 MUST NOT send a body */ - if (status > 199 && status != 204 && status != 304) { - len = mg_snprintf(conn, buf, sizeof(buf) - 1, "Error %d: %s", - status, status_text); - buf[len] = '\n'; - len++; - buf[len] = 0; + /* Errors 1xx, 204 and 304 MUST NOT send a body */ + if (status > 199 && status != 204 && status != 304) { + len = mg_snprintf(conn, buf, sizeof(buf) - 1, "Error %d: %s", + status, status_text); + buf[len] = '\n'; + len++; + buf[len] = 0; - va_start(ap, fmt); - len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap); - va_end(ap); - } - DEBUG_TRACE("[%s]", buf); + va_start(ap, fmt); + len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap); + va_end(ap); + } + DEBUG_TRACE("[%s]", buf); - mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text); - if (len > 0) { - mg_printf(conn, "Content-Type: text/plain\r\n"); - } - mg_printf(conn, "Content-Length: %d\r\n" - "Date: %s\r\n" - "Connection: %s\r\n\r\n", - len, date, suggest_connection_header(conn)); - conn->num_bytes_sent += mg_printf(conn, "%s", buf); - } + mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text); + if (len > 0) { + mg_printf(conn, "Content-Type: text/plain\r\n"); + } + mg_printf(conn, "Content-Length: %d\r\n" + "Date: %s\r\n" + "Connection: %s\r\n\r\n", + len, date, suggest_connection_header(conn)); + conn->num_bytes_sent += mg_printf(conn, "%s", buf); + } } #if defined(_WIN32) && !defined(__SYMBIAN32__) static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) { - (void)unused; - *mutex = CreateMutex(NULL, FALSE, NULL); - return *mutex == NULL ? -1 : 0; + (void)unused; + *mutex = CreateMutex(NULL, FALSE, NULL); + return *mutex == NULL ? -1 : 0; } static int pthread_mutex_destroy(pthread_mutex_t *mutex) { - return CloseHandle(*mutex) == 0 ? -1 : 0; + return CloseHandle(*mutex) == 0 ? -1 : 0; } static int pthread_mutex_lock(pthread_mutex_t *mutex) { - return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1; + return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1; } static int pthread_mutex_trylock(pthread_mutex_t *mutex) { - switch (WaitForSingleObject(*mutex, 0)) { - case WAIT_OBJECT_0: - return 0; - case WAIT_TIMEOUT: - return -2; /* EBUSY */ - } - return -1; + switch (WaitForSingleObject(*mutex, 0)) { + case WAIT_OBJECT_0: + return 0; + case WAIT_TIMEOUT: + return -2; /* EBUSY */ + } + return -1; } static int pthread_mutex_unlock(pthread_mutex_t *mutex) { - return ReleaseMutex(*mutex) == 0 ? -1 : 0; + return ReleaseMutex(*mutex) == 0 ? -1 : 0; } #ifndef WIN_PTHREADS_TIME_H 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; + 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.0) { - QueryPerformanceFrequency((LARGE_INTEGER *)&li); - perfcnt_per_sec = 1.0 / li.QuadPart; - } - if (perfcnt_per_sec != 0.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; - } - } - } + 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.0) { + QueryPerformanceFrequency((LARGE_INTEGER *)&li); + perfcnt_per_sec = 1.0 / li.QuadPart; + } + if (perfcnt_per_sec != 0.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; + return ok ? 0 : -1; } #endif static int pthread_cond_init(pthread_cond_t *cv, const void *unused) { - (void)unused; - InitializeCriticalSection(&cv->threadIdSec); - cv->waitingthreadcount = 0; - cv->waitingthreadhdls = - (pthread_t *)mg_calloc(MAX_WORKER_THREADS, sizeof(pthread_t)); - return (cv->waitingthreadhdls != NULL) ? 0 : -1; + (void)unused; + InitializeCriticalSection(&cv->threadIdSec); + cv->waitingthreadcount = 0; + cv->waitingthreadhdls = + (pthread_t *)mg_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; + 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); + 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) * 1000000000) + tsnow.tv_nsec; - nswaitabs = - (((uint64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec; - nswaitrel = nswaitabs - nsnow; - if (nswaitrel < 0) - nswaitrel = 0; - mswaitrel = (DWORD)(nswaitrel / 1000000); - } else { - mswaitrel = INFINITE; - } + if (abstime) { + clock_gettime(CLOCK_REALTIME, &tsnow); + nsnow = (((uint64_t)tsnow.tv_sec) * 1000000000) + tsnow.tv_nsec; + nswaitabs = + (((uint64_t)abstime->tv_sec) * 1000000000) + 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); + pthread_mutex_unlock(mutex); + ok = (WAIT_OBJECT_0 == + WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel)); + pthread_mutex_lock(mutex); - return ok ? 0 : -1; + return ok ? 0 : -1; } static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) { - return pthread_cond_timedwait(cv, mutex, NULL); + return pthread_cond_timedwait(cv, mutex, NULL); } static int pthread_cond_signal(pthread_cond_t *cv) { - int i; - HANDLE wkup = NULL; - BOOL ok = FALSE; + int i; + HANDLE wkup = NULL; + BOOL ok = FALSE; - EnterCriticalSection(&cv->threadIdSec); - if (cv->waitingthreadcount) { - wkup = cv->waitingthreadhdls[0]; - ok = SetEvent(wkup); + EnterCriticalSection(&cv->threadIdSec); + if (cv->waitingthreadcount) { + wkup = cv->waitingthreadhdls[0]; + ok = SetEvent(wkup); - for (i = 1; i < cv->waitingthreadcount; i++) { - cv->waitingthreadhdls[i - 1] = cv->waitingthreadhdls[i]; - } - cv->waitingthreadcount--; + for (i = 1; i < cv->waitingthreadcount; i++) { + cv->waitingthreadhdls[i - 1] = cv->waitingthreadhdls[i]; + } + cv->waitingthreadcount--; - assert(ok); - } - LeaveCriticalSection(&cv->threadIdSec); + assert(ok); + } + LeaveCriticalSection(&cv->threadIdSec); - return ok ? 0 : 1; + return ok ? 0 : 1; } static int pthread_cond_broadcast(pthread_cond_t *cv) { - EnterCriticalSection(&cv->threadIdSec); - while (cv->waitingthreadcount) { - pthread_cond_signal(cv); - } - LeaveCriticalSection(&cv->threadIdSec); + EnterCriticalSection(&cv->threadIdSec); + while (cv->waitingthreadcount) { + pthread_cond_signal(cv); + } + LeaveCriticalSection(&cv->threadIdSec); - return 0; + return 0; } static int pthread_cond_destroy(pthread_cond_t *cv) { - EnterCriticalSection(&cv->threadIdSec); - assert(cv->waitingthreadcount == 0); - mg_free(cv->waitingthreadhdls); - cv->waitingthreadhdls = 0; - LeaveCriticalSection(&cv->threadIdSec); - DeleteCriticalSection(&cv->threadIdSec); + EnterCriticalSection(&cv->threadIdSec); + assert(cv->waitingthreadcount == 0); + mg_free(cv->waitingthreadhdls); + cv->waitingthreadhdls = 0; + LeaveCriticalSection(&cv->threadIdSec); + DeleteCriticalSection(&cv->threadIdSec); - return 0; + return 0; } /* For Windows, change all slashes to backslashes in path names. */ static void change_slashes_to_backslashes(char *path) { - int i; + int i; - for (i = 0; path[i] != '\0'; i++) { - if (path[i] == '/') { - path[i] = '\\'; - } + for (i = 0; path[i] != '\0'; i++) { + if (path[i] == '/') { + path[i] = '\\'; + } - /* remove double backslash (check i > 0 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, path + i + 2, strlen(path + i + 1)); - } - } - } + /* remove double backslash (check i > 0 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, path + i + 2, strlen(path + i + 1)); + } + } + } } /* 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]; + char buf[PATH_MAX], buf2[PATH_MAX]; - mg_strlcpy(buf, path, sizeof(buf)); - change_slashes_to_backslashes(buf); + 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. */ - 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), - NULL, NULL); - if (strcmp(buf, buf2) != 0) { - wbuf[0] = L'\0'; - } + /* 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), + NULL, NULL); + if (strcmp(buf, buf2) != 0) { + wbuf[0] = L'\0'; + } } #if defined(_WIN32_WCE) static time_t time(time_t *ptime) { - time_t t; - SYSTEMTIME st; - FILETIME ft; + time_t t; + SYSTEMTIME st; + FILETIME ft; - GetSystemTime(&st); - SystemTimeToFileTime(&st, &ft); - t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime); + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime); - if (ptime != NULL) { - *ptime = t; - } + if (ptime != NULL) { + *ptime = t; + } - return t; + return t; } static struct tm *localtime(const time_t *ptime, struct tm *ptm) { - int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF; - FILETIME ft, lft; - SYSTEMTIME st; - TIME_ZONE_INFORMATION tzinfo; + int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF; + FILETIME ft, lft; + SYSTEMTIME st; + TIME_ZONE_INFORMATION tzinfo; - if (ptm == NULL) { - return NULL; - } + if (ptm == NULL) { + return NULL; + } - *(int64_t *)&ft = t; - FileTimeToLocalFileTime(&ft, &lft); - FileTimeToSystemTime(&lft, &st); - ptm->tm_year = st.wYear - 1900; - ptm->tm_mon = st.wMonth - 1; - ptm->tm_wday = st.wDayOfWeek; - ptm->tm_mday = st.wDay; - 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_isdst = - GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0; + *(int64_t *)&ft = t; + FileTimeToLocalFileTime(&ft, &lft); + FileTimeToSystemTime(&lft, &st); + ptm->tm_year = st.wYear - 1900; + ptm->tm_mon = st.wMonth - 1; + ptm->tm_wday = st.wDayOfWeek; + ptm->tm_mday = st.wDay; + 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_isdst = + GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0; - return ptm; + return ptm; } static struct tm *gmtime(const time_t *ptime, struct tm *ptm) { - /* FIXME(lsm): fix this. */ - return localtime(ptime, ptm); + /* FIXME(lsm): fix this. */ + return localtime(ptime, ptm); } static size_t strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm) { - (void)snprintf(dst, dst_size, "implement strftime() for WinCE"); - return 0; + (void)snprintf(dst, dst_size, "implement strftime() for WinCE"); + return 0; } #endif @@ -2153,175 +2155,176 @@ 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 = "_-"; - int last = path[strlen(path) - 1]; - return isalnum(last) || strchr(allowed_last_characters, last) != NULL; + static const char *allowed_last_characters = "_-"; + int last = path[strlen(path) - 1]; + return isalnum(last) || strchr(allowed_last_characters, last) != NULL; } static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep) { - wchar_t wbuf[PATH_MAX]; - WIN32_FILE_ATTRIBUTE_DATA info; + wchar_t wbuf[PATH_MAX]; + WIN32_FILE_ATTRIBUTE_DATA info; - if (!is_file_in_memory(conn, path, filep)) { - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { - filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); - filep->modification_time = - SYS2UNIX_TIME(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 (!filep->is_directory && !path_cannot_disclose_cgi(path)) { - memset(filep, 0, sizeof(*filep)); - } - } - } + if (!is_file_in_memory(conn, path, filep)) { + to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); + if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { + filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); + filep->modification_time = + SYS2UNIX_TIME(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 (!filep->is_directory && !path_cannot_disclose_cgi(path)) { + memset(filep, 0, sizeof(*filep)); + } + } + } - return filep->membuf != NULL || filep->modification_time != 0; + return filep->membuf != NULL || filep->modification_time != 0; } static int mg_remove(const char *path) { - wchar_t wbuf[PATH_MAX]; - to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); - return DeleteFileW(wbuf) ? 0 : -1; + wchar_t wbuf[PATH_MAX]; + to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); + return DeleteFileW(wbuf) ? 0 : -1; } static int mg_mkdir(const char *path, int mode) { - char buf[PATH_MAX]; - wchar_t wbuf[PATH_MAX]; + char buf[PATH_MAX]; + wchar_t wbuf[PATH_MAX]; - (void)mode; - mg_strlcpy(buf, path, sizeof(buf)); - change_slashes_to_backslashes(buf); + (void)mode; + mg_strlcpy(buf, path, sizeof(buf)); + change_slashes_to_backslashes(buf); - (void)MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf)); + (void)MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf)); - return CreateDirectoryW(wbuf, NULL) ? 0 : -1; + return CreateDirectoryW(wbuf, NULL) ? 0 : -1; } /* Implementation of POSIX opendir/closedir/readdir for Windows. */ static DIR *opendir(const char *name) { - DIR *dir = NULL; - wchar_t wpath[PATH_MAX]; - DWORD attrs; + DIR *dir = NULL; + wchar_t wpath[PATH_MAX]; + DWORD attrs; - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - to_unicode(name, wpath, ARRAY_SIZE(wpath)); - attrs = GetFileAttributesW(wpath); - if (attrs != 0xFFFFFFFF && - ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { - (void)wcscat(wpath, L"\\*"); - dir->handle = FindFirstFileW(wpath, &dir->info); - dir->result.d_name[0] = '\0'; - } else { - mg_free(dir); - dir = NULL; - } - } + if (name == NULL) { + SetLastError(ERROR_BAD_ARGUMENTS); + } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + } else { + to_unicode(name, wpath, ARRAY_SIZE(wpath)); + attrs = GetFileAttributesW(wpath); + if (attrs != 0xFFFFFFFF && + ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { + (void)wcscat(wpath, L"\\*"); + dir->handle = FindFirstFileW(wpath, &dir->info); + dir->result.d_name[0] = '\0'; + } else { + mg_free(dir); + dir = NULL; + } + } - return dir; + return dir; } static int closedir(DIR *dir) { - int result = 0; + int result = 0; - if (dir != NULL) { - if (dir->handle != INVALID_HANDLE_VALUE) - result = FindClose(dir->handle) ? 0 : -1; + if (dir != NULL) { + if (dir->handle != INVALID_HANDLE_VALUE) + result = FindClose(dir->handle) ? 0 : -1; - mg_free(dir); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } + mg_free(dir); + } else { + result = -1; + SetLastError(ERROR_BAD_ARGUMENTS); + } - return result; + return result; } static struct dirent *readdir(DIR *dir) { - struct dirent *result = 0; + struct dirent *result = 0; - if (dir) { - if (dir->handle != INVALID_HANDLE_VALUE) { - result = &dir->result; - (void)WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName, -1, - result->d_name, sizeof(result->d_name), - NULL, NULL); + if (dir) { + if (dir->handle != INVALID_HANDLE_VALUE) { + result = &dir->result; + (void)WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName, -1, + result->d_name, sizeof(result->d_name), + NULL, NULL); - if (!FindNextFileW(dir->handle, &dir->info)) { - (void)FindClose(dir->handle); - dir->handle = INVALID_HANDLE_VALUE; - } + if (!FindNextFileW(dir->handle, &dir->info)) { + (void)FindClose(dir->handle); + dir->handle = INVALID_HANDLE_VALUE; + } - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } + } else { + SetLastError(ERROR_FILE_NOT_FOUND); + } + } else { + SetLastError(ERROR_BAD_ARGUMENTS); + } - return result; + return result; } #ifndef HAVE_POLL static int poll(struct pollfd *pfd, int n, int milliseconds) { - struct timeval tv; - fd_set set; - int i, result; - SOCKET maxfd = 0; + struct timeval tv; + fd_set set; + int i, result; + SOCKET maxfd = 0; - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = (milliseconds % 1000) * 1000; - FD_ZERO(&set); + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = (milliseconds % 1000) * 1000; + FD_ZERO(&set); - for (i = 0; i < n; i++) { - FD_SET((SOCKET)pfd[i].fd, &set); - pfd[i].revents = 0; + for (i = 0; i < n; i++) { + FD_SET((SOCKET)pfd[i].fd, &set); + pfd[i].revents = 0; - if (pfd[i].fd > maxfd) { - maxfd = pfd[i].fd; - } - } + if (pfd[i].fd > maxfd) { + maxfd = pfd[i].fd; + } + } - if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) { - for (i = 0; i < n; i++) { - if (FD_ISSET(pfd[i].fd, &set)) { - pfd[i].revents = POLLIN; - } - } - } + if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) { + for (i = 0; i < n; i++) { + if (FD_ISSET(pfd[i].fd, &set)) { + pfd[i].revents = POLLIN; + } + } + } - return result; + return result; } #endif /* HAVE_POLL */ static void set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be null */) { - (void)conn; /* Unused. */ - (void)SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0); + (void)conn; /* Unused. */ + (void)SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0); } 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 ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p) == - ((uintptr_t)(-1L))) - ? -1 - : 0); + /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 + */ + return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p) == + ((uintptr_t)(-1L))) + ? -1 + : 0); #else - return ( - (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L))) - ? -1 - : 0); + return ( + (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L))) + ? -1 + : 0); #endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ } @@ -2329,303 +2332,303 @@ int mg_start_thread(mg_thread_func_t f, void *p) { static int mg_start_thread_with_id(unsigned(__stdcall *f)(void *), void *p, pthread_t *threadidptr) { - uintptr_t uip; - HANDLE threadhandle; - int result = -1; + uintptr_t uip; + HANDLE threadhandle; + int result = -1; - uip = _beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))f, p, 0, NULL); - threadhandle = (HANDLE)uip; - if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) { - *threadidptr = threadhandle; - result = 0; - } + uip = _beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))f, p, 0, NULL); + threadhandle = (HANDLE)uip; + if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) { + *threadidptr = threadhandle; + result = 0; + } - return result; + return result; } /* Wait for a thread to finish. */ static int mg_join_thread(pthread_t threadid) { - int result; - DWORD dwevent; + int result; + DWORD dwevent; - result = -1; - dwevent = WaitForSingleObject(threadid, INFINITE); - if (dwevent == WAIT_FAILED) { - int err = GetLastError(); - (void)err; - DEBUG_TRACE("WaitForSingleObject() failed, error %d", err); - } else { - if (dwevent == WAIT_OBJECT_0) { - CloseHandle(threadid); - result = 0; - } - } + result = -1; + dwevent = WaitForSingleObject(threadid, INFINITE); + if (dwevent == WAIT_FAILED) { + int err = GetLastError(); + (void)err; + DEBUG_TRACE("WaitForSingleObject() failed, error %d", err); + } else { + if (dwevent == WAIT_OBJECT_0) { + CloseHandle(threadid); + result = 0; + } + } - return result; + return result; } static HANDLE dlopen(const char *dll_name, int flags) { - wchar_t wbuf[PATH_MAX]; - (void)flags; - to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf)); - return LoadLibraryW(wbuf); + wchar_t wbuf[PATH_MAX]; + (void)flags; + to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf)); + return LoadLibraryW(wbuf); } static int dlclose(void *handle) { - int result; + int result; - if (FreeLibrary((HMODULE)handle) != 0) { - result = 0; - } else { - result = -1; - } + if (FreeLibrary((HMODULE)handle) != 0) { + result = 0; + } else { + result = -1; + } - return result; + return result; } #if !defined(NO_CGI) #define SIGKILL 0 static int kill(pid_t pid, int sig_num) { - (void)TerminateProcess(pid, sig_num); - (void)CloseHandle(pid); - return 0; + (void)TerminateProcess(pid, sig_num); + (void)CloseHandle(pid); + return 0; } static void trim_trailing_whitespaces(char *s) { - char *e = s + strlen(s) - 1; - while (e > s && isspace(*(unsigned char *)e)) { - *e-- = '\0'; - } + char *e = s + strlen(s) - 1; + while (e > s && isspace(*(unsigned char *)e)) { + *e-- = '\0'; + } } static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir) { - HANDLE me; - char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX], - cmdline[PATH_MAX], buf[PATH_MAX]; - struct file file = STRUCT_FILE_INITIALIZER; - STARTUPINFOA si; - PROCESS_INFORMATION pi = {0}; + HANDLE me; + char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX], + cmdline[PATH_MAX], buf[PATH_MAX]; + struct file file = STRUCT_FILE_INITIALIZER; + STARTUPINFOA si; + PROCESS_INFORMATION pi = {0}; - (void)envp; + (void)envp; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); - /* TODO(lsm): redirect CGI errors to the error log file */ - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; + /* TODO(lsm): redirect CGI errors to the error log file */ + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; - me = GetCurrentProcess(); - DuplicateHandle(me, (HANDLE)_get_osfhandle(fdin), me, &si.hStdInput, 0, - TRUE, DUPLICATE_SAME_ACCESS); - DuplicateHandle(me, (HANDLE)_get_osfhandle(fdout), me, &si.hStdOutput, 0, - TRUE, DUPLICATE_SAME_ACCESS); + me = GetCurrentProcess(); + DuplicateHandle(me, (HANDLE)_get_osfhandle(fdin), me, &si.hStdInput, 0, + TRUE, DUPLICATE_SAME_ACCESS); + 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 */ - interp = conn->ctx->config[CGI_INTERPRETER]; - if (interp == NULL) { - buf[0] = buf[1] = '\0'; + /* 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 */ - snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog); - if (mg_fopen(conn, cmdline, "r", &file)) { - p = (char *)file.membuf; - mg_fgets(buf, sizeof(buf), &file, &p); - mg_fclose(&file); - buf[sizeof(buf) - 1] = '\0'; - } + /* 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; + mg_fgets(buf, sizeof(buf), &file, &p); + mg_fclose(&file); + buf[sizeof(buf) - 1] = '\0'; + } - if (buf[0] == '#' && buf[1] == '!') { - trim_trailing_whitespaces(buf + 2); - } else { - buf[2] = '\0'; - } - interp = buf + 2; - } + if (buf[0] == '#' && buf[1] == '!') { + trim_trailing_whitespaces(buf + 2); + } else { + buf[2] = '\0'; + } + interp = buf + 2; + } - if (interp[0] != '\0') { - GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL); - interp = full_interp; - } - GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL); + if (interp[0] != '\0') { + GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL); + interp = full_interp; + } + GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL); - if (interp[0] != '\0') - mg_snprintf(conn, cmdline, sizeof(cmdline), "\"%s\" \"%s\\%s\"", interp, - full_dir, prog); - else - mg_snprintf(conn, cmdline, sizeof(cmdline), "\"%s\\%s\"", full_dir, - prog); + if (interp[0] != '\0') + mg_snprintf(conn, cmdline, sizeof(cmdline), "\"%s\" \"%s\\%s\"", interp, + full_dir, prog); + else + mg_snprintf(conn, cmdline, sizeof(cmdline), "\"%s\\%s\"", full_dir, + prog); - DEBUG_TRACE("Running [%s]", cmdline); - if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, - CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) { - mg_cry(conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, ERRNO); - pi.hProcess = (pid_t)-1; - } + DEBUG_TRACE("Running [%s]", cmdline); + if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, + CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) { + mg_cry(conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, ERRNO); + pi.hProcess = (pid_t)-1; + } - (void)CloseHandle(si.hStdOutput); - (void)CloseHandle(si.hStdInput); - if (pi.hThread != NULL) - (void)CloseHandle(pi.hThread); + (void)CloseHandle(si.hStdOutput); + (void)CloseHandle(si.hStdInput); + if (pi.hThread != NULL) + (void)CloseHandle(pi.hThread); - return (pid_t)pi.hProcess; + return (pid_t)pi.hProcess; } #endif /* !NO_CGI */ static int set_non_blocking_mode(SOCKET sock) { - unsigned long on = 1; - return ioctlsocket(sock, FIONBIO, &on); + unsigned long on = 1; + return ioctlsocket(sock, FIONBIO, &on); } #else static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep) { - struct stat st; - if (!filep) { - return 0; - } - if (!is_file_in_memory(conn, path, filep) && !stat(path, &st)) { - filep->size = st.st_size; - filep->modification_time = st.st_mtime; - filep->is_directory = S_ISDIR(st.st_mode); - } else { - filep->modification_time = (time_t)0; - } + struct stat st; + if (!filep) { + return 0; + } + if (!is_file_in_memory(conn, path, filep) && !stat(path, &st)) { + filep->size = st.st_size; + filep->modification_time = st.st_mtime; + filep->is_directory = S_ISDIR(st.st_mode); + } else { + filep->modification_time = (time_t)0; + } - return filep->membuf != NULL || filep->modification_time != (time_t)0; + return filep->membuf != NULL || filep->modification_time != (time_t)0; } static void set_close_on_exec(int fd, struct mg_connection *conn /* may be null */) { - if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { - if (conn) { - mg_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, - strerror(ERRNO)); - } - } + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { + if (conn) { + mg_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, + strerror(ERRNO)); + } + } } int mg_start_thread(mg_thread_func_t func, void *param) { - pthread_t thread_id; - pthread_attr_t attr; - int result; + pthread_t thread_id; + pthread_attr_t attr; + int result; - (void)pthread_attr_init(&attr); - (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + (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 */ - (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); + /* 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) */ - result = pthread_create(&thread_id, &attr, func, param); - pthread_attr_destroy(&attr); + result = pthread_create(&thread_id, &attr, func, param); + pthread_attr_destroy(&attr); - return result; + return result; } /* Start a thread storing the thread context. */ static int mg_start_thread_with_id(mg_thread_func_t func, void *param, pthread_t *threadidptr) { - pthread_t thread_id; - pthread_attr_t attr; - int result; + pthread_t thread_id; + pthread_attr_t attr; + int result; - (void)pthread_attr_init(&attr); + (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 */ - (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); + /* 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 */ - result = pthread_create(&thread_id, &attr, func, param); - pthread_attr_destroy(&attr); - if ((result == 0) && (threadidptr != NULL)) { - *threadidptr = thread_id; - } - return result; + result = pthread_create(&thread_id, &attr, func, param); + pthread_attr_destroy(&attr); + if ((result == 0) && (threadidptr != NULL)) { + *threadidptr = thread_id; + } + return result; } /* Wait for a thread to finish. */ static int mg_join_thread(pthread_t threadid) { - int result; + int result; - result = pthread_join(threadid, NULL); - return result; + result = pthread_join(threadid, NULL); + return result; } #ifndef NO_CGI static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir) { - pid_t pid; - const char *interp; + pid_t pid; + const char *interp; - (void)envblk; + (void)envblk; - if (conn == NULL) { - return 0; - } + if (conn == NULL) { + return 0; + } - if ((pid = fork()) == -1) { - /* Parent */ - send_http_error(conn, 500, "Error: Creating CGI process\nfork(): %s", - strerror(ERRNO)); - } else if (pid == 0) { - /* Child */ - if (chdir(dir) != 0) { - mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); - } else if (dup2(fdin, 0) == -1) { - mg_cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, - strerror(ERRNO)); - } 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. */ - (void)close(fdin); - (void)close(fdout); + if ((pid = fork()) == -1) { + /* Parent */ + send_http_error(conn, 500, "Error: Creating CGI process\nfork(): %s", + strerror(ERRNO)); + } else if (pid == 0) { + /* Child */ + if (chdir(dir) != 0) { + mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); + } else if (dup2(fdin, 0) == -1) { + mg_cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, + strerror(ERRNO)); + } 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. */ + (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. */ - signal(SIGCHLD, SIG_DFL); + /* 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]; - if (interp == NULL) { - (void)execle(prog, prog, NULL, envp); - mg_cry(conn, "%s: execle(%s): %s", __func__, prog, - strerror(ERRNO)); - } else { - (void)execle(interp, interp, prog, NULL, envp); - mg_cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog, - strerror(ERRNO)); - } - } - exit(EXIT_FAILURE); - } + interp = conn->ctx->config[CGI_INTERPRETER]; + if (interp == NULL) { + (void)execle(prog, prog, NULL, envp); + mg_cry(conn, "%s: execle(%s): %s", __func__, prog, + strerror(ERRNO)); + } else { + (void)execle(interp, interp, prog, NULL, envp); + mg_cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog, + strerror(ERRNO)); + } + } + exit(EXIT_FAILURE); + } - return pid; + return pid; } #endif /* !NO_CGI */ static int set_non_blocking_mode(SOCKET sock) { - int flags; + int flags; - flags = fcntl(sock, F_GETFL, 0); - (void)fcntl(sock, F_SETFL, flags | O_NONBLOCK); + flags = fcntl(sock, F_GETFL, 0); + (void)fcntl(sock, F_SETFL, flags | O_NONBLOCK); - return 0; + return 0; } #endif /* _WIN32 */ @@ -2633,612 +2636,613 @@ static int set_non_blocking_mode(SOCKET sock) { 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; + int64_t sent; + int n, k; - (void)ssl; /* Get rid of warning */ - sent = 0; - while (sent < len) { - /* How many bytes we send in this iteration */ - k = len - sent > INT_MAX ? INT_MAX : (int)(len - sent); + (void)ssl; /* Get rid of warning */ + sent = 0; + while (sent < len) { + /* How many bytes we send in this iteration */ + k = len - sent > INT_MAX ? INT_MAX : (int)(len - sent); #ifndef NO_SSL - if (ssl != NULL) { - n = SSL_write(ssl, buf + sent, k); - } else + if (ssl != NULL) { + n = SSL_write(ssl, buf + sent, k); + } else #endif - if (fp != NULL) { - n = (int)fwrite(buf + sent, 1, (size_t)k, fp); - if (ferror(fp)) - n = -1; - } else { - n = (int)send(sock, buf + sent, (size_t)k, MSG_NOSIGNAL); - } + if (fp != NULL) { + n = (int)fwrite(buf + sent, 1, (size_t)k, fp); + if (ferror(fp)) + n = -1; + } else { + n = (int)send(sock, buf + sent, (size_t)k, MSG_NOSIGNAL); + } - if (n <= 0) - break; + if (n <= 0) + break; - sent += n; - } + sent += n; + } - return sent; + 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. */ static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) { - int nread; - double timeout = -1; - struct timespec start, now; + int nread; + double timeout = -1; + struct timespec start, now; - memset(&start, 0, sizeof(start)); - memset(&now, 0, sizeof(now)); + memset(&start, 0, sizeof(start)); + memset(&now, 0, sizeof(now)); - if (conn->ctx->config[REQUEST_TIMEOUT]) { - timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; - } - if (timeout > 0) { - clock_gettime(CLOCK_MONOTONIC, &start); - } + if (conn->ctx->config[REQUEST_TIMEOUT]) { + timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; + } + if (timeout > 0) { + clock_gettime(CLOCK_MONOTONIC, &start); + } - do { - 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. */ - nread = (int)read(fileno(fp), buf, (size_t)len); + do { + 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. */ + nread = (int)read(fileno(fp), buf, (size_t)len); #ifndef NO_SSL - } else if (conn->ssl != NULL) { - nread = SSL_read(conn->ssl, buf, len); + } else if (conn->ssl != NULL) { + nread = SSL_read(conn->ssl, buf, len); #endif - } else { - nread = (int)recv(conn->client.sock, buf, (size_t)len, 0); - } - if (conn->ctx->stop_flag) { - return -1; - } - if (nread >= 0) { - return nread; - } - if (timeout > 0) { - clock_gettime(CLOCK_MONOTONIC, &now); - } - } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout)); + } else { + nread = (int)recv(conn->client.sock, buf, (size_t)len, 0); + } + if (conn->ctx->stop_flag) { + return -1; + } + if (nread >= 0) { + return nread; + } + if (timeout > 0) { + clock_gettime(CLOCK_MONOTONIC, &now); + } + } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout)); - /* Timeout occured, but no data available. */ - return -1; + /* Timeout occured, but no data available. */ + return -1; } static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) { - int n, nread = 0; + int n, nread = 0; - while (len > 0 && conn->ctx->stop_flag == 0) { - n = pull(fp, conn, buf + nread, len); - if (n < 0) { - nread = n; /* Propagate the error */ - break; - } else if (n == 0) { - break; /* No more data to read */ - } else { - conn->consumed_content += n; - nread += n; - len -= n; - } - } + while (len > 0 && conn->ctx->stop_flag == 0) { + n = pull(fp, conn, buf + nread, len); + if (n < 0) { + nread = n; /* Propagate the error */ + break; + } else if (n == 0) { + break; /* No more data to read */ + } else { + conn->consumed_content += n; + nread += n; + len -= n; + } + } - return nread; + return nread; } static void discard_unread_request_data(struct mg_connection *conn) { - char buf[MG_BUF_LEN]; - int to_read, nread; + char buf[MG_BUF_LEN]; + int to_read, nread; - if (conn == NULL) { - return; - } + if (conn == NULL) { + return; + } - to_read = sizeof(buf); + to_read = sizeof(buf); - if (conn->is_chunked) { - /* Chunked encoding: 1=chunk not read completely, 2=chunk read - * completely */ - while (conn->is_chunked == 1) { - nread = mg_read(conn, buf, to_read); - if (nread <= 0) { - break; - } - } + if (conn->is_chunked) { + /* Chunked encoding: 1=chunk not read completely, 2=chunk read + * completely */ + while (conn->is_chunked == 1) { + nread = mg_read(conn, buf, to_read); + if (nread <= 0) { + break; + } + } - } else { - /* Not chunked: content length is known */ - while (conn->consumed_content < conn->content_len) { - if ((int64_t)to_read > - (conn->content_len - conn->consumed_content)) { - to_read = (int)(conn->content_len - conn->consumed_content); - } + } else { + /* Not chunked: content length is known */ + while (conn->consumed_content < conn->content_len) { + if ((int64_t)to_read > + (conn->content_len - conn->consumed_content)) { + to_read = (int)(conn->content_len - conn->consumed_content); + } - nread = mg_read(conn, buf, to_read); - if (nread <= 0) { - break; - } - } - } + nread = mg_read(conn, buf, to_read); + if (nread <= 0) { + break; + } + } + } } int mg_read_inner(struct mg_connection *conn, void *buf, size_t len) { - int64_t n, buffered_len, nread; - int64_t len64 = - (int64_t)(len > INT_MAX ? INT_MAX : len); /* since the return value is - int, we may not read more - bytes */ - const char *body; + int64_t n, buffered_len, nread; + int64_t len64 = + (int64_t)(len > INT_MAX ? INT_MAX : len); /* since the return value is + int, we may not read more + bytes */ + const char *body; - if (conn == NULL) - return 0; + if (conn == NULL) + return 0; - /* If Content-Length is not set for a PUT or POST request, read until socket - * is closed */ - if (conn->consumed_content == 0 && conn->content_len == -1) { - conn->content_len = INT64_MAX; - conn->must_close = 1; - } + /* If Content-Length is not set for a PUT or POST request, read until + * socket + * is closed */ + if (conn->consumed_content == 0 && conn->content_len == -1) { + conn->content_len = INT64_MAX; + conn->must_close = 1; + } - nread = 0; - if (conn->consumed_content < conn->content_len) { - /* Adjust number of bytes to read. */ - int64_t left_to_read = conn->content_len - conn->consumed_content; - if (left_to_read < len64) { - /* Do not reade more than the total content length of the request. - */ - len64 = left_to_read; - } + nread = 0; + if (conn->consumed_content < conn->content_len) { + /* Adjust number of bytes to read. */ + int64_t left_to_read = conn->content_len - conn->consumed_content; + if (left_to_read < len64) { + /* Do not reade more than the total content length of the request. + */ + len64 = left_to_read; + } - /* Return buffered data */ - buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len - - conn->consumed_content; - if (buffered_len > 0) { - if (len64 < buffered_len) { - buffered_len = len64; - } - body = conn->buf + conn->request_len + conn->consumed_content; - memcpy(buf, body, (size_t)buffered_len); - len64 -= buffered_len; - conn->consumed_content += buffered_len; - nread += buffered_len; - buf = (char *)buf + buffered_len; - } + /* Return buffered data */ + buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len - + conn->consumed_content; + if (buffered_len > 0) { + if (len64 < buffered_len) { + buffered_len = len64; + } + body = conn->buf + conn->request_len + conn->consumed_content; + memcpy(buf, body, (size_t)buffered_len); + len64 -= buffered_len; + conn->consumed_content += buffered_len; + nread += buffered_len; + buf = (char *)buf + buffered_len; + } - /* We have returned all buffered data. Read new data from the remote - * socket. - */ - if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) { - nread += n; - } else { - nread = (nread > 0 ? nread : n); - } - } - return (int)nread; + /* We have returned all buffered data. Read new data from the remote + * socket. + */ + if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) { + nread += n; + } else { + nread = (nread > 0 ? nread : n); + } + } + return (int)nread; } static char mg_getc(struct mg_connection *conn) { - char c; - if (conn == NULL) { - return 0; - } - conn->content_len++; - if (mg_read_inner(conn, &c, 1) <= 0) { - return (char)0; - } - return c; + char c; + if (conn == NULL) { + return 0; + } + conn->content_len++; + if (mg_read_inner(conn, &c, 1) <= 0) { + return (char)0; + } + return c; } int mg_read(struct mg_connection *conn, void *buf, size_t len) { - if (len > INT_MAX) - len = INT_MAX; + if (len > INT_MAX) + len = INT_MAX; - if (conn == NULL) - return 0; + if (conn == NULL) + return 0; - if (conn->is_chunked) { - size_t all_read = 0; - while (len > 0) { - if (conn->chunk_remainder) { - /* copy from the remainder of the last received chunk */ - long read_ret; - int read_now = (int)((conn->chunk_remainder > len) - ? (len) - : (conn->chunk_remainder)); + if (conn->is_chunked) { + size_t all_read = 0; + while (len > 0) { + if (conn->chunk_remainder) { + /* copy from the remainder of the last received chunk */ + long read_ret; + int read_now = (int)((conn->chunk_remainder > len) + ? (len) + : (conn->chunk_remainder)); - conn->content_len += read_now; - read_ret = - mg_read_inner(conn, (char *)buf + all_read, read_now); - all_read += read_ret; + conn->content_len += read_now; + read_ret = + mg_read_inner(conn, (char *)buf + all_read, read_now); + all_read += read_ret; - conn->chunk_remainder -= read_now; - len -= read_now; + conn->chunk_remainder -= read_now; + len -= read_now; - if (conn->chunk_remainder == 0) { - /* the rest of the data in the current chunk has been read - */ - if ((mg_getc(conn) != '\r') || (mg_getc(conn) != '\n')) { - /* Protocol violation */ - return -1; - } - } + if (conn->chunk_remainder == 0) { + /* the rest of the data in the current chunk has been read + */ + if ((mg_getc(conn) != '\r') || (mg_getc(conn) != '\n')) { + /* Protocol violation */ + return -1; + } + } - } else { - /* fetch a new chunk */ - int i = 0; - char lenbuf[64]; - char *end = 0; - unsigned long chunkSize = 0; + } else { + /* fetch a new chunk */ + int i = 0; + char lenbuf[64]; + char *end = 0; + unsigned long chunkSize = 0; - for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) { - lenbuf[i] = mg_getc(conn); - if (i > 0 && lenbuf[i] == '\r' && lenbuf[i - 1] != '\r') - continue; - if (i > 1 && lenbuf[i] == '\n' && lenbuf[i - 1] == '\r') { - lenbuf[i + 1] = 0; - chunkSize = strtoul(lenbuf, &end, 16); - break; - } - if (!isalnum(lenbuf[i])) { - /* illegal character for chunk length */ - return -1; - } - } - if ((end == NULL) || (*end != '\r')) { - /* chunksize not set correctly */ - return -1; - } + for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) { + lenbuf[i] = mg_getc(conn); + if (i > 0 && lenbuf[i] == '\r' && lenbuf[i - 1] != '\r') + continue; + if (i > 1 && lenbuf[i] == '\n' && lenbuf[i - 1] == '\r') { + lenbuf[i + 1] = 0; + chunkSize = strtoul(lenbuf, &end, 16); + break; + } + if (!isalnum(lenbuf[i])) { + /* illegal character for chunk length */ + return -1; + } + } + if ((end == NULL) || (*end != '\r')) { + /* chunksize not set correctly */ + return -1; + } - conn->chunk_remainder = chunkSize; - if (chunkSize == 0) { - /* regular end of content */ - conn->is_chunked = 2; - break; - } - } - } + conn->chunk_remainder = chunkSize; + if (chunkSize == 0) { + /* regular end of content */ + conn->is_chunked = 2; + break; + } + } + } - return (int)all_read; - } - return mg_read_inner(conn, buf, len); + return (int)all_read; + } + return mg_read_inner(conn, buf, len); } int mg_write(struct mg_connection *conn, const void *buf, size_t len) { - time_t now; - int64_t n, total, allowed; + time_t now; + int64_t n, total, allowed; - if (conn == NULL) { - return 0; - } + if (conn == NULL) { + return 0; + } - if (conn->throttle > 0) { - if ((now = time(NULL)) != conn->last_throttle_time) { - conn->last_throttle_time = now; - conn->last_throttle_bytes = 0; - } - allowed = conn->throttle - conn->last_throttle_bytes; - if (allowed > (int64_t)len) { - allowed = len; - } - if ((total = push(NULL, conn->client.sock, conn->ssl, (const char *)buf, - (int64_t)allowed)) == allowed) { - buf = (char *)buf + total; - conn->last_throttle_bytes += total; - while (total < (int64_t)len && conn->ctx->stop_flag == 0) { - allowed = conn->throttle > (int64_t)len - total - ? (int64_t)len - total - : conn->throttle; - if ((n = push(NULL, conn->client.sock, conn->ssl, - (const char *)buf, (int64_t)allowed)) != - allowed) { - break; - } - sleep(1); - conn->last_throttle_bytes = allowed; - conn->last_throttle_time = time(NULL); - buf = (char *)buf + n; - total += n; - } - } - } else { - total = push(NULL, conn->client.sock, conn->ssl, (const char *)buf, - (int64_t)len); - } - return (int)total; + if (conn->throttle > 0) { + if ((now = time(NULL)) != conn->last_throttle_time) { + conn->last_throttle_time = now; + conn->last_throttle_bytes = 0; + } + allowed = conn->throttle - conn->last_throttle_bytes; + if (allowed > (int64_t)len) { + allowed = len; + } + if ((total = push(NULL, conn->client.sock, conn->ssl, (const char *)buf, + (int64_t)allowed)) == allowed) { + buf = (char *)buf + total; + conn->last_throttle_bytes += total; + while (total < (int64_t)len && conn->ctx->stop_flag == 0) { + allowed = conn->throttle > (int64_t)len - total + ? (int64_t)len - total + : conn->throttle; + if ((n = push(NULL, conn->client.sock, conn->ssl, + (const char *)buf, (int64_t)allowed)) != + allowed) { + break; + } + sleep(1); + conn->last_throttle_bytes = allowed; + conn->last_throttle_time = time(NULL); + buf = (char *)buf + n; + total += n; + } + } + } else { + total = push(NULL, conn->client.sock, conn->ssl, (const char *)buf, + (int64_t)len); + } + return (int)total; } /* Alternative alloc_vprintf() for non-compliant C runtimes */ static int alloc_vprintf2(char **buf, const char *fmt, va_list ap) { - va_list ap_copy; - int size = MG_BUF_LEN; - int len = -1; + va_list ap_copy; + int size = MG_BUF_LEN; + int len = -1; - *buf = NULL; - while (len == -1) { - if (*buf) - mg_free(*buf); - *buf = (char *)mg_malloc(size *= 4); - if (!*buf) - break; - va_copy(ap_copy, ap); - len = vsnprintf(*buf, size, fmt, ap_copy); - va_end(ap_copy); - } + *buf = NULL; + while (len == -1) { + if (*buf) + mg_free(*buf); + *buf = (char *)mg_malloc(size *= 4); + if (!*buf) + break; + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); + } - return len; + 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. */ static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { - va_list ap_copy; - int len; + 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. */ - va_copy(ap_copy, ap); - len = vsnprintf(NULL, 0, fmt, ap_copy); - va_end(ap_copy); + /* 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. - */ - 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 *)mg_malloc(size)) == NULL) { - len = -1; /* Allocation failed, mark failure */ - } else { - va_copy(ap_copy, ap); - IGNORE_UNUSED_RESULT(vsnprintf(*buf, size, 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. + */ + 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 *)mg_malloc(size)) == NULL) { + len = -1; /* Allocation failed, mark failure */ + } else { + va_copy(ap_copy, ap); + IGNORE_UNUSED_RESULT(vsnprintf(*buf, size, fmt, ap_copy)); + va_end(ap_copy); + } - return len; + return len; } int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { - char mem[MG_BUF_LEN], *buf = mem; - int len; + char mem[MG_BUF_LEN], *buf = mem; + int len; - if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - len = mg_write(conn, buf, (size_t)len); - } - if (buf != mem && buf != NULL) { - mg_free(buf); - } + if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + len = mg_write(conn, buf, (size_t)len); + } + if (buf != mem && buf != NULL) { + mg_free(buf); + } - return len; + return len; } int mg_printf(struct mg_connection *conn, const char *fmt, ...) { - va_list ap; - int result; + va_list ap; + int result; - va_start(ap, fmt); - result = mg_vprintf(conn, fmt, ap); - va_end(ap); + va_start(ap, fmt); + result = mg_vprintf(conn, fmt, ap); + va_end(ap); - return result; + return result; } int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) { - int i, j, a, b; + int i, j, a, b; #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - if (i < src_len - 2 && src[i] == '%' && - isxdigit(*(const unsigned char *)(src + i + 1)) && - isxdigit(*(const unsigned char *)(src + i + 2))) { - a = tolower(*(const unsigned char *)(src + i + 1)); - b = tolower(*(const unsigned char *)(src + i + 2)); - dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (i < src_len - 2 && src[i] == '%' && + isxdigit(*(const unsigned char *)(src + i + 1)) && + isxdigit(*(const unsigned char *)(src + i + 2))) { + a = tolower(*(const unsigned char *)(src + i + 1)); + b = tolower(*(const unsigned char *)(src + i + 2)); + dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } - dst[j] = '\0'; /* Null-terminate the destination */ + dst[j] = '\0'; /* Null-terminate the destination */ - return i >= src_len ? j : -1; + return i >= src_len ? j : -1; } int mg_get_var(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len) { - return mg_get_var2(data, data_len, name, dst, dst_len, 0); + return mg_get_var2(data, data_len, name, dst, dst_len, 0); } int mg_get_var2(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len, size_t occurrence) { - const char *p, *e, *s; - size_t name_len; - int len; + const char *p, *e, *s; + size_t name_len; + int len; - if (dst == NULL || dst_len == 0) { - len = -2; - } else if (data == NULL || name == NULL || data_len == 0) { - len = -1; - dst[0] = '\0'; - } else { - name_len = strlen(name); - e = data + data_len; - len = -1; - dst[0] = '\0'; + if (dst == NULL || dst_len == 0) { + len = -2; + } else if (data == NULL || name == NULL || data_len == 0) { + len = -1; + dst[0] = '\0'; + } else { + name_len = strlen(name); + e = data + data_len; + len = -1; + dst[0] = '\0'; - /* 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 */ - p += name_len + 1; + /* 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 */ + p += name_len + 1; - /* 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); */ - if (s < p) { - return -3; - } + /* 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); */ + if (s < p) { + return -3; + } - /* Decode variable into destination buffer */ - len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1); + /* 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). */ - if (len == -1) { - len = -2; - } - break; - } - } - } + /* Redirect error code from -1 to -2 (destination buffer too + small). */ + if (len == -1) { + len = -2; + } + break; + } + } + } - return len; + return len; } int mg_get_cookie(const char *cookie_header, const char *var_name, char *dst, size_t dst_size) { - const char *s, *p, *end; - int name_len, len = -1; + const char *s, *p, *end; + int name_len, len = -1; - if (dst == NULL || dst_size == 0) { - len = -2; - } else if (var_name == NULL || (s = cookie_header) == NULL) { - len = -1; - dst[0] = '\0'; - } else { - name_len = (int)strlen(var_name); - end = s + strlen(s); - dst[0] = '\0'; + if (dst == NULL || dst_size == 0) { + len = -2; + } else if (var_name == NULL || (s = cookie_header) == NULL) { + len = -1; + dst[0] = '\0'; + } else { + name_len = (int)strlen(var_name); + end = s + strlen(s); + dst[0] = '\0'; - for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { - if (s[name_len] == '=') { - s += name_len + 1; - if ((p = strchr(s, ' ')) == NULL) - p = end; - if (p[-1] == ';') - p--; - if (*s == '"' && p[-1] == '"' && p > s + 1) { - s++; - p--; - } - if ((size_t)(p - s) < dst_size) { - len = (int)(p - s); - mg_strlcpy(dst, s, (size_t)len + 1); - } else { - len = -3; - } - break; - } - } - } - return len; + for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { + if (s[name_len] == '=') { + s += name_len + 1; + if ((p = strchr(s, ' ')) == NULL) + p = end; + if (p[-1] == ';') + p--; + if (*s == '"' && p[-1] == '"' && p > s + 1) { + s++; + p--; + } + if ((size_t)(p - s) < dst_size) { + len = (int)(p - s); + mg_strlcpy(dst, s, (size_t)len + 1); + } else { + len = -3; + } + break; + } + } + } + return len; } #if defined(USE_WEBSOCKET) || defined(USE_LUA) static void base64_encode(const unsigned char *src, int src_len, char *dst) { - static const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int i, j, a, b, c; + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; - for (i = j = 0; i < src_len; i += 3) { - a = src[i]; - b = i + 1 >= src_len ? 0 : src[i + 1]; - c = i + 2 >= src_len ? 0 : src[i + 2]; + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; - dst[j++] = b64[a >> 2]; - dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; - if (i + 1 < src_len) { - dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; - } - if (i + 2 < src_len) { - dst[j++] = b64[c & 63]; - } - } - while (j % 4 != 0) { - dst[j++] = '='; - } - dst[j++] = '\0'; + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 63]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; } #endif #if defined(USE_LUA) static unsigned char b64reverse(char letter) { - if (letter >= 'A' && letter <= 'Z') - return letter - 'A'; - if (letter >= 'a' && letter <= 'z') - return letter - 'a' + 26; - if (letter >= '0' && letter <= '9') - return letter - '0' + 52; - if (letter == '+') - return 62; - if (letter == '/') - return 63; - if (letter == '=') - return 255; /* normal end */ - return 254; /* error */ + if (letter >= 'A' && letter <= 'Z') + return letter - 'A'; + if (letter >= 'a' && letter <= 'z') + return letter - 'a' + 26; + if (letter >= '0' && letter <= '9') + return letter - '0' + 52; + if (letter == '+') + return 62; + if (letter == '/') + return 63; + if (letter == '=') + return 255; /* normal end */ + return 254; /* error */ } static int base64_decode(const unsigned char *src, int src_len, char *dst, size_t *dst_len) { - int i; - unsigned char a, b, c, d; + int i; + unsigned char a, b, c, d; - *dst_len = 0; + *dst_len = 0; - for (i = 0; i < src_len; i += 4) { - a = b64reverse(src[i]); - if (a >= 254) - return i; + for (i = 0; i < src_len; i += 4) { + a = b64reverse(src[i]); + if (a >= 254) + return i; - b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]); - if (b >= 254) - return i + 1; + b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]); + if (b >= 254) + return i + 1; - c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]); - if (c == 254) - return i + 2; + c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]); + if (c == 254) + return i + 2; - d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]); - if (c == 254) - return i + 3; + d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]); + if (c == 254) + return i + 3; - dst[(*dst_len)++] = (a << 2) + (b >> 4); - if (c != 255) { - dst[(*dst_len)++] = (b << 4) + (c >> 2); - if (d != 255) { - dst[(*dst_len)++] = (c << 6) + d; - } - } - } - return -1; + dst[(*dst_len)++] = (a << 2) + (b >> 4); + if (c != 255) { + dst[(*dst_len)++] = (b << 4) + (c >> 2); + if (d != 255) { + dst[(*dst_len)++] = (c << 6) + d; + } + } + } + return -1; } #endif static int is_put_or_delete_method(const struct mg_connection *conn) { - if (conn) { - const char *s = conn->request_info.request_method; - return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") || - !strcmp(s, "MKCOL")); - } - return 0; + if (conn) { + const char *s = conn->request_info.request_method; + return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") || + !strcmp(s, "MKCOL")); + } + return 0; } static void @@ -3250,151 +3254,151 @@ interpret_uri(struct mg_connection *conn, /* in: request */ int *is_websocket_request, /* out: websocket connetion? */ int *is_put_or_delete_request /* out: put/delete a file? */ ) { - if (conn && conn->ctx) { - const char *uri = conn->request_info.uri; - const char *root = conn->ctx->config[DOCUMENT_ROOT]; + if (conn && conn->ctx) { + const char *uri = conn->request_info.uri; + const char *root = conn->ctx->config[DOCUMENT_ROOT]; #if !defined(NO_FILES) - const char *rewrite; - struct vec a, b; - char *p; - int match_len; - char gz_path[PATH_MAX]; - char const *accept_encoding; + const char *rewrite; + struct vec a, b; + char *p; + int match_len; + char gz_path[PATH_MAX]; + char const *accept_encoding; #endif - memset(filep, 0, sizeof(*filep)); - *filename = 0; - *is_script_ressource = 0; - *is_put_or_delete_request = is_put_or_delete_method(conn); + memset(filep, 0, sizeof(*filep)); + *filename = 0; + *is_script_ressource = 0; + *is_put_or_delete_request = is_put_or_delete_method(conn); #if defined(USE_WEBSOCKET) - *is_websocket_request = is_websocket_protocol(conn); - if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) { - root = conn->ctx->config[WEBSOCKET_ROOT]; - } + *is_websocket_request = is_websocket_protocol(conn); + if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) { + root = conn->ctx->config[WEBSOCKET_ROOT]; + } #else - *is_websocket_request = 0; + *is_websocket_request = 0; #endif #if !defined(NO_FILES) - /* Note that root == NULL is a regular use case here. This occurs, - if all requests are handled by callbacks, so the WEBSOCKET_ROOT - config is not required. */ - if (root == NULL) { - /* all file related outputs have already been set to 0, just return - */ - return; - } + /* Note that root == NULL is a regular use case here. This occurs, + if all requests are handled by callbacks, so the WEBSOCKET_ROOT + config is not required. */ + if (root == NULL) { + /* all file related outputs have already been set to 0, just return + */ + return; + } - /* 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, filename, filename_buf_len - 1, "%s%s", root, uri); + /* 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, filename, filename_buf_len - 1, "%s%s", root, uri); - rewrite = conn->ctx->config[REWRITE]; - while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { - if ((match_len = match_prefix(a.ptr, (int)a.len, uri)) > 0) { - mg_snprintf(conn, filename, filename_buf_len - 1, "%.*s%s", - (int)b.len, b.ptr, uri + match_len); - break; - } - } + rewrite = conn->ctx->config[REWRITE]; + while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { + if ((match_len = match_prefix(a.ptr, (int)a.len, uri)) > 0) { + mg_snprintf(conn, filename, filename_buf_len - 1, "%.*s%s", + (int)b.len, b.ptr, uri + match_len); + break; + } + } - /* Local file path and name, corresponding to requested URI - is now stored in "filename" variable. */ - if (mg_stat(conn, filename, filep)) { - /* File exists. Check if it is a script type. */ - if (0 + /* Local file path and name, corresponding to requested URI + is now stored in "filename" variable. */ + if (mg_stat(conn, filename, filep)) { + /* File exists. Check if it is a script type. */ + if (0 #if !defined(NO_CGI) - || - match_prefix(conn->ctx->config[CGI_EXTENSIONS], - (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), - filename) > 0 + || + match_prefix(conn->ctx->config[CGI_EXTENSIONS], + (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), + filename) > 0 #endif #if defined(USE_LUA) - || - match_prefix( - conn->ctx->config[LUA_SCRIPT_EXTENSIONS], - (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), - filename) > 0 + || + match_prefix( + conn->ctx->config[LUA_SCRIPT_EXTENSIONS], + (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), + filename) > 0 #endif - ) { - /* The request addresses a CGI script or a Lua script. The URI - corresponds - to the script itself (like /path/script.cgi), and there is no - additional - resource path (like /path/script.cgi/something). - Requests that modify (replace or delete) a resource, like PUT - and DELETE - requests, should replace/delete the script file. - Requests that read or write from/to a resource, like GET and - POST - requests, - should call the script and return the generated response. */ - *is_script_ressource = !*is_put_or_delete_request; - } - return; - } + ) { + /* The request addresses a CGI script or a Lua script. The URI + corresponds + to the script itself (like /path/script.cgi), and there is no + additional + resource path (like /path/script.cgi/something). + Requests that modify (replace or delete) a resource, like PUT + and DELETE + requests, should replace/delete the script file. + Requests that read or write from/to a resource, like GET and + POST + requests, + should call the script and return the generated response. */ + *is_script_ressource = !*is_put_or_delete_request; + } + 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 ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != - NULL) { - if (strstr(accept_encoding, "gzip") != NULL) { - snprintf(gz_path, sizeof(gz_path), "%s.gz", filename); - if (mg_stat(conn, gz_path, filep)) { - if (filep) - filep->gzipped = 1; - /* Currently gz files can not be scripts. */ - 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 ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != + NULL) { + if (strstr(accept_encoding, "gzip") != NULL) { + snprintf(gz_path, sizeof(gz_path), "%s.gz", filename); + if (mg_stat(conn, gz_path, filep)) { + if (filep) + filep->gzipped = 1; + /* Currently gz files can not be scripts. */ + return; + } + } + } - /* Support PATH_INFO for CGI scripts. */ - for (p = filename + strlen(filename); p > filename + 1; p--) { - if (*p == '/') { - *p = '\0'; - if ((0 + /* Support PATH_INFO for CGI scripts. */ + for (p = filename + strlen(filename); p > filename + 1; p--) { + if (*p == '/') { + *p = '\0'; + if ((0 #if !defined(NO_CGI) - || - match_prefix( - conn->ctx->config[CGI_EXTENSIONS], - (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), - filename) > 0 + || + match_prefix( + conn->ctx->config[CGI_EXTENSIONS], + (int)strlen(conn->ctx->config[CGI_EXTENSIONS]), + filename) > 0 #endif #if defined(USE_LUA) - || - match_prefix( - conn->ctx->config[LUA_SCRIPT_EXTENSIONS], - (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), - filename) > 0 + || + match_prefix( + conn->ctx->config[LUA_SCRIPT_EXTENSIONS], + (int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), + filename) > 0 #endif - ) && - mg_stat(conn, filename, 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. */ - conn->path_info = p + 1; - memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for - trailing \0 */ - p[1] = '/'; - *is_script_ressource = 1; - break; - } else { - *p = '/'; - } - } - } + ) && + mg_stat(conn, filename, 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. */ + conn->path_info = p + 1; + memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for + trailing \0 */ + p[1] = '/'; + *is_script_ressource = 1; + break; + } else { + *p = '/'; + } + } + } #endif - } + } } /* Check whether full request is buffered. Return: @@ -3402,98 +3406,98 @@ interpret_uri(struct mg_connection *conn, /* in: request */ 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; + 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. */ - 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 */ - } else if (s[0] == '\n' && s[1] == '\n') { - len = (int)(s - buf) + 2; - } else if (s[0] == '\n' && &s[1] < e && s[1] == '\r' && s[2] == '\n') { - len = (int)(s - buf) + 3; - } + for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) + /* 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 */ + } else if (s[0] == '\n' && s[1] == '\n') { + len = (int)(s - buf) + 2; + } else if (s[0] == '\n' && &s[1] < e && s[1] == '\r' && s[2] == '\n') { + len = (int)(s - buf) + 3; + } - return len; + return len; } /* Convert month to the month number. Return -1 on error, or month number */ static int get_month_index(const char *s) { - size_t i; + size_t i; - for (i = 0; i < ARRAY_SIZE(month_names); i++) - if (!strcmp(s, month_names[i])) - return (int)i; + for (i = 0; i < ARRAY_SIZE(month_names); i++) + if (!strcmp(s, month_names[i])) + return (int)i; - return -1; + return -1; } static int num_leap_years(int year) { - return year / 4 - year / 100 + year / 400; + return year / 4 - year / 100 + year / 400; } /* 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[] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; - char month_str[32] = {0}; - int second, minute, hour, day, month, year, leap_days, days; - time_t result = (time_t)0; + static const unsigned short days_before_month[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + char month_str[32] = {0}; + int second, minute, hour, day, month, year, leap_days, days; + time_t result = (time_t)0; - if ((sscanf(datetime, "%d/%3s/%d %d:%d:%d", &day, month_str, &year, &hour, - &minute, &second) == 6) || - (sscanf(datetime, "%d %3s %d %d:%d:%d", &day, month_str, &year, &hour, - &minute, &second) == 6) || - (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", &day, month_str, &year, - &hour, &minute, &second) == 6) || - (sscanf(datetime, "%d-%3s-%d %d:%d:%d", &day, month_str, &year, &hour, - &minute, &second) == 6)) { - month = get_month_index(month_str); - if ((month >= 0) && (year > 1970)) { - leap_days = num_leap_years(year) - num_leap_years(1970); - year -= 1970; - days = - year * 365 + days_before_month[month] + (day - 1) + leap_days; - result = (time_t)days * 24 * 3600 + (time_t)hour * 3600 + - minute * 60 + second; - } - } + if ((sscanf(datetime, "%d/%3s/%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%d %3s %d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6) || + (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", &day, month_str, &year, + &hour, &minute, &second) == 6) || + (sscanf(datetime, "%d-%3s-%d %d:%d:%d", &day, month_str, &year, &hour, + &minute, &second) == 6)) { + month = get_month_index(month_str); + if ((month >= 0) && (year > 1970)) { + leap_days = num_leap_years(year) - num_leap_years(1970); + year -= 1970; + days = + year * 365 + days_before_month[month] + (day - 1) + leap_days; + result = (time_t)days * 24 * 3600 + (time_t)hour * 3600 + + minute * 60 + second; + } + } - return result; + return result; } /* Protect against directory disclosure attack by removing '..', excessive '/' and '\' characters */ static void remove_double_dots_and_double_slashes(char *s) { - char *p = s; + char *p = s; - while (*s != '\0') { - *p++ = *s++; - if (s[-1] == '/' || s[-1] == '\\') { - /* Skip all following slashes, backslashes and double-dots */ - while (s[0] != '\0') { - if (s[0] == '/' || s[0] == '\\') { - s++; - } else if (s[0] == '.' && s[1] == '.') { - s += 2; - } else { - break; - } - } - } - } - *p = '\0'; + while (*s != '\0') { + *p++ = *s++; + if (s[-1] == '/' || s[-1] == '\\') { + /* Skip all following slashes, backslashes and double-dots */ + while (s[0] != '\0') { + if (s[0] == '/' || s[0] == '\\') { + s++; + } else if (s[0] == '.' && s[1] == '.') { + s += 2; + } else { + break; + } + } + } + } + *p = '\0'; } static const struct { - const char *extension; - size_t ext_len; - const char *mime_type; + const char *extension; + size_t ext_len; + const char *mime_type; } builtin_mime_types[] = { /* IANA registered MIME types (http://www.iana.org/assignments/media-types) application types */ @@ -3581,665 +3585,667 @@ static const struct { {NULL, 0, NULL}}; const char *mg_get_builtin_mime_type(const char *path) { - const char *ext; - size_t i, path_len; + const char *ext; + size_t i, path_len; - path_len = strlen(path); + path_len = strlen(path); - for (i = 0; builtin_mime_types[i].extension != NULL; i++) { - ext = path + (path_len - builtin_mime_types[i].ext_len); - if (path_len > builtin_mime_types[i].ext_len && - mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) { - return builtin_mime_types[i].mime_type; - } - } + for (i = 0; builtin_mime_types[i].extension != NULL; i++) { + ext = path + (path_len - builtin_mime_types[i].ext_len); + if (path_len > builtin_mime_types[i].ext_len && + mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) { + return builtin_mime_types[i].mime_type; + } + } - return "text/plain"; + return "text/plain"; } /* 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) { - struct vec ext_vec, mime_vec; - const char *list, *ext; - size_t path_len; + struct vec ext_vec, mime_vec; + const char *list, *ext; + size_t path_len; - path_len = strlen(path); + path_len = strlen(path); - if (ctx == NULL || vec == NULL) { - return; - } + if (ctx == NULL || vec == NULL) { + return; + } - /* 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 = path + path_len - ext_vec.len; - if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { - *vec = mime_vec; - return; - } - } + /* 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 = path + path_len - ext_vec.len; + if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { + *vec = mime_vec; + return; + } + } - vec->ptr = mg_get_builtin_mime_type(path); - vec->len = strlen(vec->ptr); + vec->ptr = mg_get_builtin_mime_type(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 */ static void bin2str(char *to, const unsigned char *p, size_t len) { - static const char *hex = "0123456789abcdef"; + static const char *hex = "0123456789abcdef"; - for (; len--; p++) { - *to++ = hex[p[0] >> 4]; - *to++ = hex[p[0] & 0x0f]; - } - *to = '\0'; + for (; len--; p++) { + *to++ = hex[p[0] >> 4]; + *to++ = hex[p[0] & 0x0f]; + } + *to = '\0'; } /* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */ char *mg_md5(char buf[33], ...) { - md5_byte_t hash[16]; - const char *p; - va_list ap; - md5_state_t ctx; + md5_byte_t hash[16]; + const char *p; + va_list ap; + md5_state_t ctx; - md5_init(&ctx); + md5_init(&ctx); - va_start(ap, buf); - while ((p = va_arg(ap, const char *)) != NULL) { - md5_append(&ctx, (const md5_byte_t *)p, (int)strlen(p)); - } - va_end(ap); + va_start(ap, buf); + while ((p = va_arg(ap, const char *)) != NULL) { + md5_append(&ctx, (const md5_byte_t *)p, (int)strlen(p)); + } + va_end(ap); - md5_finish(&ctx, hash); - bin2str(buf, hash, sizeof(hash)); - return buf; + md5_finish(&ctx, hash); + bin2str(buf, hash, sizeof(hash)); + return buf; } /* 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]; + char ha2[32 + 1], expected_response[32 + 1]; - /* Some of the parameters may be NULL */ - if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL || - qop == NULL || response == NULL) { - return 0; - } + /* 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 || */ - strlen(response) != 32 - /* || now - strtoul(dig->nonce, NULL, 10) > 3600 */ - ) { - 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 || */ + strlen(response) != 32 + /* || now - strtoul(dig->nonce, NULL, 10) > 3600 */ + ) { + return 0; + } - mg_md5(ha2, method, ":", uri, NULL); - mg_md5(expected_response, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, - ":", ha2, NULL); + mg_md5(ha2, method, ":", uri, NULL); + mg_md5(expected_response, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, + ":", ha2, NULL); - return mg_strcasecmp(response, expected_response) == 0; + 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. */ static void open_auth_file(struct mg_connection *conn, const char *path, struct file *filep) { - if (conn != NULL && conn->ctx != NULL) { - char name[PATH_MAX]; - const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE]; - struct file file = STRUCT_FILE_INITIALIZER; + if (conn != NULL && conn->ctx != NULL) { + char name[PATH_MAX]; + const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE]; + struct file file = STRUCT_FILE_INITIALIZER; - if (gpass != NULL) { - /* Use global passwords file */ - if (!mg_fopen(conn, gpass, "r", filep)) { + if (gpass != NULL) { + /* Use global passwords file */ + if (!mg_fopen(conn, gpass, "r", filep)) { #ifdef DEBUG - mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO)); + 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. */ - } else if (mg_stat(conn, path, &file) && file.is_directory) { - mg_snprintf(conn, name, sizeof(name), "%s%c%s", path, '/', - PASSWORDS_FILE_NAME); - if (!mg_fopen(conn, name, "r", filep)) { + } + /* 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); + if (!mg_fopen(conn, name, "r", filep)) { #ifdef DEBUG - mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); + mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); #endif - } - } else { - /* Try to find .htpasswd in requested directory. */ - for (p = path, e = p + strlen(p) - 1; e > p; e--) { - if (e[0] == '/') { - break; - } - } - mg_snprintf(conn, name, sizeof(name), "%.*s%c%s", (int)(e - p), p, - '/', PASSWORDS_FILE_NAME); - if (!mg_fopen(conn, name, "r", filep)) { + } + } else { + /* Try to find .htpasswd in requested directory. */ + for (p = path, e = p + strlen(p) - 1; e > p; e--) { + if (e[0] == '/') { + break; + } + } + mg_snprintf(conn, name, sizeof(name), "%.*s%c%s", (int)(e - p), p, + '/', PASSWORDS_FILE_NAME); + if (!mg_fopen(conn, name, "r", filep)) { #ifdef DEBUG - mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); + mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); #endif - } - } - } + } + } + } } /* Parsed Authorization header */ struct ah { - char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; + char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; }; /* 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) { - char *name, *value, *s; - const char *auth_header; - unsigned long nonce; + char *name, *value, *s; + const char *auth_header; + unsigned long nonce; - if (!ah || !conn) - return 0; + if (!ah || !conn) + return 0; - (void)memset(ah, 0, sizeof(*ah)); - if ((auth_header = mg_get_header(conn, "Authorization")) == NULL || - mg_strncasecmp(auth_header, "Digest ", 7) != 0) { - return 0; - } + (void)memset(ah, 0, sizeof(*ah)); + if ((auth_header = mg_get_header(conn, "Authorization")) == NULL || + mg_strncasecmp(auth_header, "Digest ", 7) != 0) { + return 0; + } - /* Make modifiable copy of the auth header */ - (void)mg_strlcpy(buf, auth_header + 7, buf_size); - s = buf; + /* Make modifiable copy of the auth header */ + (void)mg_strlcpy(buf, auth_header + 7, buf_size); + s = buf; - /* Parse authorization header */ - for (;;) { - /* 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. */ - if (s[0] == '\"') { - s++; - value = skip_quoted(&s, "\"", " ", '\\'); - if (s[0] == ',') { - s++; - } - } else { - value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses - spaces */ - } - if (*name == '\0') { - break; - } + /* Parse authorization header */ + for (;;) { + /* 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. */ + if (s[0] == '\"') { + s++; + value = skip_quoted(&s, "\"", " ", '\\'); + if (s[0] == ',') { + s++; + } + } else { + value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses + spaces */ + } + if (*name == '\0') { + break; + } - if (!strcmp(name, "username")) { - ah->user = value; - } else if (!strcmp(name, "cnonce")) { - ah->cnonce = value; - } else if (!strcmp(name, "response")) { - ah->response = value; - } else if (!strcmp(name, "uri")) { - ah->uri = value; - } else if (!strcmp(name, "qop")) { - ah->qop = value; - } else if (!strcmp(name, "nc")) { - ah->nc = value; - } else if (!strcmp(name, "nonce")) { - ah->nonce = value; - } - } + if (!strcmp(name, "username")) { + ah->user = value; + } else if (!strcmp(name, "cnonce")) { + ah->cnonce = value; + } else if (!strcmp(name, "response")) { + ah->response = value; + } else if (!strcmp(name, "uri")) { + ah->uri = value; + } else if (!strcmp(name, "qop")) { + ah->qop = value; + } else if (!strcmp(name, "nc")) { + ah->nc = value; + } else if (!strcmp(name, "nonce")) { + ah->nonce = value; + } + } #ifndef NO_NONCE_CHECK - /* Convert the nonce from the client to a number and check it. */ - /* Server side nonce check is valuable in all situations but one: if the - server restarts frequently, - but the client should not see that, so the server should accept nonces from - previous starts. */ - if (ah->nonce == NULL) { - return 0; - } - nonce = strtoul(ah->nonce, &s, 10); - if ((s == NULL) || (*s != 0)) { - return 0; - } - nonce ^= (unsigned long)(conn->ctx); - if (nonce < conn->ctx->start_time) { - /* nonce is from a previous start of the server and no longer valid - * (replay - * attack?) */ - return 0; - } - if (nonce >= conn->ctx->start_time + conn->ctx->nonce_count) { - return 0; - } + /* Convert the nonce from the client to a number and check it. */ + /* Server side nonce check is valuable in all situations but one: if the + server restarts frequently, + but the client should not see that, so the server should accept nonces from + previous starts. */ + if (ah->nonce == NULL) { + return 0; + } + nonce = strtoul(ah->nonce, &s, 10); + if ((s == NULL) || (*s != 0)) { + return 0; + } + nonce ^= (unsigned long)(conn->ctx); + if (nonce < conn->ctx->start_time) { + /* nonce is from a previous start of the server and no longer valid + * (replay + * attack?) */ + return 0; + } + if (nonce >= conn->ctx->start_time + conn->ctx->nonce_count) { + return 0; + } #endif - /* CGI needs it as REMOTE_USER */ - if (ah->user != NULL) { - conn->request_info.remote_user = mg_strdup(ah->user); - } else { - return 0; - } + /* CGI needs it as REMOTE_USER */ + if (ah->user != NULL) { + conn->request_info.remote_user = mg_strdup(ah->user); + } else { + return 0; + } - return 1; + return 1; } static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p) { - char *eof; - size_t len; - char *memend; + char *eof; + size_t len; + char *memend; - if (!filep) - return NULL; + if (!filep) + return NULL; - 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 */ - if (eof != NULL) { - eof += 1; /* Include \n */ - } else { - eof = memend; /* Copy remaining data */ - } - len = (size_t)(eof - *p) > size - 1 ? size - 1 : (size_t)(eof - *p); - memcpy(buf, *p, len); - buf[len] = '\0'; - *p += len; - return len ? eof : NULL; - } else if (filep->fp != NULL) { - return fgets(buf, (int)size, filep->fp); - } else { - return NULL; - } + 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 */ + if (eof != NULL) { + eof += 1; /* Include \n */ + } else { + eof = memend; /* Copy remaining data */ + } + len = (size_t)(eof - *p) > size - 1 ? size - 1 : (size_t)(eof - *p); + memcpy(buf, *p, len); + buf[len] = '\0'; + *p += len; + return len ? eof : NULL; + } else if (filep->fp != NULL) { + return fgets(buf, (int)size, filep->fp); + } else { + return NULL; + } } struct read_auth_file_struct { - struct mg_connection *conn; - struct ah ah; - char *domain; - char buf[256 + 256 + 40]; - char *f_user; - char *f_domain; - char *f_ha1; + struct mg_connection *conn; + struct ah ah; + char *domain; + char buf[256 + 256 + 40]; + char *f_user; + char *f_domain; + char *f_ha1; }; static int read_auth_file(struct file *filep, struct read_auth_file_struct *workdata) { - char *p; - int is_authorized = 0; - struct file fp; - size_t l; + char *p; + int is_authorized = 0; + struct file fp; + size_t l; - if (!filep || !workdata) - return 0; + if (!filep || !workdata) + return 0; - /* Loop over passwords file */ - p = (char *)filep->membuf; - while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) { - l = strlen(workdata->buf); - while (l > 0) { - if (isspace(workdata->buf[l - 1]) || - iscntrl(workdata->buf[l - 1])) { - l--; - workdata->buf[l] = 0; - } else - break; - } - if (l < 1) - continue; + /* Loop over passwords file */ + p = (char *)filep->membuf; + while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) { + l = strlen(workdata->buf); + while (l > 0) { + if (isspace(workdata->buf[l - 1]) || + iscntrl(workdata->buf[l - 1])) { + l--; + workdata->buf[l] = 0; + } else + break; + } + if (l < 1) + continue; - workdata->f_user = workdata->buf; + workdata->f_user = workdata->buf; - if (workdata->f_user[0] == ':') { - /* user names may not contain a ':' and may not be empty, - so lines starting with ':' may be used for a special purpose */ - if (workdata->f_user[1] == '#') { - /* :# is a comment */ - continue; - } else if (!strncmp(workdata->f_user + 1, "include=", 8)) { - if (mg_fopen(workdata->conn, workdata->f_user + 9, "r", &fp)) { - is_authorized = read_auth_file(&fp, workdata); - mg_fclose(&fp); - } else { - mg_cry(workdata->conn, - "%s: cannot open authorization file: %s", __func__, - workdata->buf); - } - continue; - } - /* everything is invalid for the moment (might change in the future) - */ - mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", - __func__, workdata->buf); - continue; - } + if (workdata->f_user[0] == ':') { + /* user names may not contain a ':' and may not be empty, + so lines starting with ':' may be used for a special purpose */ + if (workdata->f_user[1] == '#') { + /* :# is a comment */ + continue; + } else if (!strncmp(workdata->f_user + 1, "include=", 8)) { + if (mg_fopen(workdata->conn, workdata->f_user + 9, "r", &fp)) { + is_authorized = read_auth_file(&fp, workdata); + mg_fclose(&fp); + } else { + mg_cry(workdata->conn, + "%s: cannot open authorization file: %s", __func__, + workdata->buf); + } + continue; + } + /* everything is invalid for the moment (might change in the + * future) + */ + mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", + __func__, workdata->buf); + continue; + } - workdata->f_domain = strchr(workdata->f_user, ':'); - if (workdata->f_domain == NULL) { - mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", - __func__, workdata->buf); - continue; - } - *(workdata->f_domain) = 0; - (workdata->f_domain)++; + workdata->f_domain = strchr(workdata->f_user, ':'); + if (workdata->f_domain == NULL) { + mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", + __func__, workdata->buf); + continue; + } + *(workdata->f_domain) = 0; + (workdata->f_domain)++; - workdata->f_ha1 = strchr(workdata->f_domain, ':'); - if (workdata->f_ha1 == NULL) { - mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", - __func__, workdata->buf); - continue; - } - *(workdata->f_ha1) = 0; - (workdata->f_ha1)++; + workdata->f_ha1 = strchr(workdata->f_domain, ':'); + if (workdata->f_ha1 == NULL) { + mg_cry(workdata->conn, "%s: syntax error in authorization file: %s", + __func__, workdata->buf); + continue; + } + *(workdata->f_ha1) = 0; + (workdata->f_ha1)++; - if (!strcmp(workdata->ah.user, workdata->f_user) && - !strcmp(workdata->domain, workdata->f_domain)) { - return check_password( - workdata->conn->request_info.request_method, workdata->f_ha1, - workdata->ah.uri, workdata->ah.nonce, workdata->ah.nc, - workdata->ah.cnonce, workdata->ah.qop, workdata->ah.response); - } - } + if (!strcmp(workdata->ah.user, workdata->f_user) && + !strcmp(workdata->domain, workdata->f_domain)) { + return check_password( + workdata->conn->request_info.request_method, workdata->f_ha1, + workdata->ah.uri, workdata->ah.nonce, workdata->ah.nc, + workdata->ah.cnonce, workdata->ah.qop, workdata->ah.response); + } + } - return is_authorized; + return is_authorized; } /* Authorize against the opened passwords file. Return 1 if authorized. */ static int authorize(struct mg_connection *conn, struct file *filep) { - struct read_auth_file_struct workdata; - char buf[MG_BUF_LEN]; + struct read_auth_file_struct workdata; + char buf[MG_BUF_LEN]; - if (!conn || !conn->ctx) - return 0; + if (!conn || !conn->ctx) + return 0; - memset(&workdata, 0, sizeof(workdata)); - workdata.conn = conn; + memset(&workdata, 0, sizeof(workdata)); + workdata.conn = conn; - if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) { - return 0; - } - workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; + if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) { + return 0; + } + workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; - return read_auth_file(filep, &workdata); + return read_auth_file(filep, &workdata); } /* Return 1 if request is authorised, 0 otherwise. */ static int check_authorization(struct mg_connection *conn, const char *path) { - char fname[PATH_MAX]; - struct vec uri_vec, filename_vec; - const char *list; - struct file file = STRUCT_FILE_INITIALIZER; - int authorized = 1; + char fname[PATH_MAX]; + struct vec uri_vec, filename_vec; + const char *list; + struct file file = STRUCT_FILE_INITIALIZER; + int authorized = 1; - if (!conn || !conn->ctx) - return 0; + if (!conn || !conn->ctx) + return 0; - list = conn->ctx->config[PROTECT_URI]; - while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { - if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) { - mg_snprintf(conn, fname, sizeof(fname), "%.*s", - (int)filename_vec.len, filename_vec.ptr); - if (!mg_fopen(conn, fname, "r", &file)) { - mg_cry(conn, "%s: cannot open %s: %s", __func__, fname, - strerror(errno)); - } - break; - } - } + list = conn->ctx->config[PROTECT_URI]; + while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { + if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) { + mg_snprintf(conn, fname, sizeof(fname), "%.*s", + (int)filename_vec.len, filename_vec.ptr); + if (!mg_fopen(conn, fname, "r", &file)) { + mg_cry(conn, "%s: cannot open %s: %s", __func__, fname, + strerror(errno)); + } + break; + } + } - if (!is_file_opened(&file)) { - open_auth_file(conn, path, &file); - } + if (!is_file_opened(&file)) { + open_auth_file(conn, path, &file); + } - if (is_file_opened(&file)) { - authorized = authorize(conn, &file); - mg_fclose(&file); - } + if (is_file_opened(&file)) { + authorized = authorize(conn, &file); + mg_fclose(&file); + } - return authorized; + return authorized; } static void send_authorization_request(struct mg_connection *conn) { - char date[64]; - time_t curtime = time(NULL); + char date[64]; + time_t curtime = time(NULL); - if (conn && conn->ctx) { - unsigned long nonce = (unsigned long)(conn->ctx->start_time); + if (conn && conn->ctx) { + unsigned long nonce = (unsigned long)(conn->ctx->start_time); - (void)pthread_mutex_lock(&conn->ctx->nonce_mutex); - nonce += conn->ctx->nonce_count; - ++conn->ctx->nonce_count; - (void)pthread_mutex_unlock(&conn->ctx->nonce_mutex); + (void)pthread_mutex_lock(&conn->ctx->nonce_mutex); + nonce += conn->ctx->nonce_count; + ++conn->ctx->nonce_count; + (void)pthread_mutex_unlock(&conn->ctx->nonce_mutex); - nonce ^= (unsigned long)(conn->ctx); - conn->status_code = 401; - conn->must_close = 1; + nonce ^= (unsigned long)(conn->ctx); + conn->status_code = 401; + conn->must_close = 1; - gmt_time_string(date, sizeof(date), &curtime); + gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n" - "Date: %s\r\n" - "Connection: %s\r\n" - "Content-Length: 0\r\n" - "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", " - "nonce=\"%lu\"\r\n\r\n", - date, suggest_connection_header(conn), - conn->ctx->config[AUTHENTICATION_DOMAIN], nonce); - } + mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n" + "Date: %s\r\n" + "Connection: %s\r\n" + "Content-Length: 0\r\n" + "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", " + "nonce=\"%lu\"\r\n\r\n", + date, suggest_connection_header(conn), + conn->ctx->config[AUTHENTICATION_DOMAIN], nonce); + } } static int is_authorized_for_put(struct mg_connection *conn) { - if (conn) { - struct file file = STRUCT_FILE_INITIALIZER; - const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; - int ret = 0; + if (conn) { + struct file file = STRUCT_FILE_INITIALIZER; + const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; + int ret = 0; - if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) { - ret = authorize(conn, &file); - mg_fclose(&file); - } + if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) { + ret = authorize(conn, &file); + mg_fclose(&file); + } - return ret; - } - return 0; + return ret; + } + return 0; } int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass) { - int found, i; - char line[512], u[512] = "", d[512] = "", ha1[33], tmp[PATH_MAX + 8]; - FILE *fp, *fp2; + int found, i; + char line[512], u[512] = "", d[512] = "", ha1[33], tmp[PATH_MAX + 8]; + FILE *fp, *fp2; - found = 0; - fp = fp2 = NULL; + found = 0; + fp = fp2 = NULL; - /* Regard empty password as no password - remove user record. */ - if (pass != NULL && pass[0] == '\0') { - pass = NULL; - } + /* Regard empty password as no password - remove user record. */ + if (pass != NULL && pass[0] == '\0') { + pass = NULL; + } - /* Other arguments must not be empty */ - if (fname == NULL || domain == NULL || user == NULL) - return 0; + /* Other arguments must not be empty */ + if (fname == NULL || domain == NULL || user == NULL) + return 0; - /* Using the given file format, user name and domain must not contain ':' */ - if (strchr(user, ':') != NULL) - return 0; - if (strchr(domain, ':') != NULL) - return 0; + /* Using the given file format, user name and domain must not contain ':' + */ + if (strchr(user, ':') != NULL) + return 0; + if (strchr(domain, ':') != NULL) + return 0; - /* Do not allow control characters like newline in user name and domain. - Do not allow excessively long names either. */ - for (i = 0; i < 255 && user[i] != 0; i++) { - if (iscntrl(user[i])) - return 0; - } - if (user[i]) - return 0; - for (i = 0; i < 255 && domain[i] != 0; i++) { - if (iscntrl(domain[i])) - return 0; - } - if (domain[i]) - return 0; + /* Do not allow control characters like newline in user name and domain. + Do not allow excessively long names either. */ + for (i = 0; i < 255 && user[i] != 0; i++) { + if (iscntrl(user[i])) + return 0; + } + if (user[i]) + return 0; + for (i = 0; i < 255 && domain[i] != 0; i++) { + if (iscntrl(domain[i])) + return 0; + } + if (domain[i]) + return 0; - /* Create a temporary file name */ - (void)snprintf(tmp, sizeof(tmp) - 1, "%s.tmp", fname); - tmp[sizeof(tmp) - 1] = 0; + /* Create a temporary file name */ + (void)snprintf(tmp, sizeof(tmp) - 1, "%s.tmp", fname); + tmp[sizeof(tmp) - 1] = 0; - /* Create the file if does not exist */ - if ((fp = fopen(fname, "a+")) != NULL) { - (void)fclose(fp); - } + /* Create the file if does not exist */ + if ((fp = fopen(fname, "a+")) != NULL) { + (void)fclose(fp); + } - /* Open the given file and temporary file */ - if ((fp = fopen(fname, "r")) == NULL) { - return 0; - } else if ((fp2 = fopen(tmp, "w+")) == NULL) { - fclose(fp); - return 0; - } + /* Open the given file and temporary file */ + if ((fp = fopen(fname, "r")) == NULL) { + return 0; + } else if ((fp2 = fopen(tmp, "w+")) == NULL) { + fclose(fp); + return 0; + } - /* Copy the stuff to temporary file */ - while (fgets(line, sizeof(line), fp) != NULL) { - if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) { - continue; - } - u[255] = 0; - d[255] = 0; + /* Copy the stuff to temporary file */ + while (fgets(line, sizeof(line), fp) != NULL) { + if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) { + continue; + } + u[255] = 0; + d[255] = 0; - if (!strcmp(u, user) && !strcmp(d, domain)) { - found++; - if (pass != NULL) { - mg_md5(ha1, user, ":", domain, ":", pass, NULL); - fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); - } - } else { - fprintf(fp2, "%s", line); - } - } + if (!strcmp(u, user) && !strcmp(d, domain)) { + found++; + if (pass != NULL) { + mg_md5(ha1, user, ":", domain, ":", pass, NULL); + fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); + } + } else { + fprintf(fp2, "%s", line); + } + } - /* 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); - } + /* 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 */ - fclose(fp); - fclose(fp2); + /* Close files */ + fclose(fp); + fclose(fp2); - /* Put the temp file in place of real file */ - IGNORE_UNUSED_RESULT(remove(fname)); - IGNORE_UNUSED_RESULT(rename(tmp, fname)); + /* Put the temp file in place of real file */ + IGNORE_UNUSED_RESULT(remove(fname)); + IGNORE_UNUSED_RESULT(rename(tmp, fname)); - return 1; + return 1; } static SOCKET conn2(struct mg_context *ctx /* may be null */, const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len) { - struct sockaddr_in sain; - struct hostent *he; - SOCKET sock = INVALID_SOCKET; + struct sockaddr_in sain; + struct hostent *he; + SOCKET sock = INVALID_SOCKET; - if (ebuf_len > 0) { - *ebuf = 0; - } + if (ebuf_len > 0) { + *ebuf = 0; + } - if (host == NULL) { - 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() */ - } 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) { - snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); - } else { - set_close_on_exec(sock, fc(ctx)); - memset(&sain, '\0', sizeof(sain)); - sain.sin_family = AF_INET; - sain.sin_port = htons((uint16_t)port); - sain.sin_addr = *(struct in_addr *)he->h_addr_list[0]; - if (connect(sock, (struct sockaddr *)&sain, sizeof(sain)) != 0) { - snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", host, port, - strerror(ERRNO)); - closesocket(sock); - sock = INVALID_SOCKET; - } - } - return sock; + if (host == NULL) { + 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() */ + } 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) { + snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); + } else { + set_close_on_exec(sock, fc(ctx)); + memset(&sain, '\0', sizeof(sain)); + sain.sin_family = AF_INET; + sain.sin_port = htons((uint16_t)port); + sain.sin_addr = *(struct in_addr *)he->h_addr_list[0]; + if (connect(sock, (struct sockaddr *)&sain, sizeof(sain)) != 0) { + snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", host, port, + strerror(ERRNO)); + closesocket(sock); + sock = INVALID_SOCKET; + } + } + return sock; } int mg_url_encode(const char *src, char *dst, size_t dst_len) { - static const char *dont_escape = "._-$,;~()"; - static const char *hex = "0123456789abcdef"; - char *pos = dst; - const char *end = dst + dst_len - 1; + static const char *dont_escape = "._-$,;~()"; + static const char *hex = "0123456789abcdef"; + char *pos = dst; + const char *end = dst + dst_len - 1; - for (; *src != '\0' && pos < end; src++, pos++) { - if (isalnum(*(const unsigned char *)src) || - strchr(dont_escape, *(const unsigned char *)src) != NULL) { - *pos = *src; - } else if (pos + 2 < end) { - pos[0] = '%'; - pos[1] = hex[(*(const unsigned char *)src) >> 4]; - pos[2] = hex[(*(const unsigned char *)src) & 0xf]; - pos += 2; - } else { - break; - } - } + for (; *src != '\0' && pos < end; src++, pos++) { + if (isalnum(*(const unsigned char *)src) || + strchr(dont_escape, *(const unsigned char *)src) != NULL) { + *pos = *src; + } else if (pos + 2 < end) { + pos[0] = '%'; + pos[1] = hex[(*(const unsigned char *)src) >> 4]; + pos[2] = hex[(*(const unsigned char *)src) & 0xf]; + pos += 2; + } else { + break; + } + } - *pos = '\0'; - return (*src == '\0') ? (int)(pos - dst) : -1; + *pos = '\0'; + return (*src == '\0') ? (int)(pos - dst) : -1; } static void print_dir_entry(struct de *de) { - char size[64], mod[64], href[PATH_MAX]; - struct tm *tm; + char size[64], mod[64], href[PATH_MAX]; + struct tm *tm; - 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. */ - if (de->file.size < 1024) { - mg_snprintf(de->conn, size, sizeof(size), "%d", (int)de->file.size); - } else if (de->file.size < 0x100000) { - mg_snprintf(de->conn, size, sizeof(size), "%.1fk", - (double)de->file.size / 1024.0); - } else if (de->file.size < 0x40000000) { - mg_snprintf(de->conn, size, sizeof(size), "%.1fM", - (double)de->file.size / 1048576); - } else { - mg_snprintf(de->conn, size, sizeof(size), "%.1fG", - (double)de->file.size / 1073741824); - } - } - tm = localtime(&de->file.modification_time); - if (tm != NULL) { - strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm); - } else { - mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod)); - mod[sizeof(mod) - 1] = '\0'; - } - mg_url_encode(de->file_name, href, sizeof(href)); - de->conn->num_bytes_sent += mg_printf( - de->conn, "%s%s" - " %s  %s\n", - de->conn->request_info.uri, href, de->file.is_directory ? "/" : "", - de->file_name, de->file.is_directory ? "/" : "", mod, size); + 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. */ + if (de->file.size < 1024) { + mg_snprintf(de->conn, size, sizeof(size), "%d", (int)de->file.size); + } else if (de->file.size < 0x100000) { + mg_snprintf(de->conn, size, sizeof(size), "%.1fk", + (double)de->file.size / 1024.0); + } else if (de->file.size < 0x40000000) { + mg_snprintf(de->conn, size, sizeof(size), "%.1fM", + (double)de->file.size / 1048576); + } else { + mg_snprintf(de->conn, size, sizeof(size), "%.1fG", + (double)de->file.size / 1073741824); + } + } + tm = localtime(&de->file.modification_time); + if (tm != NULL) { + strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm); + } else { + mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod)); + mod[sizeof(mod) - 1] = '\0'; + } + mg_url_encode(de->file_name, href, sizeof(href)); + de->conn->num_bytes_sent += mg_printf( + de->conn, "%s%s" + " %s  %s\n", + de->conn->request_info.uri, href, de->file.is_directory ? "/" : "", + de->file_name, de->file.is_directory ? "/" : "", mod, size); } /* This function is called from send_directory() and used for @@ -4247,490 +4253,490 @@ 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) { - if (p1 && p2) { - const struct de *a = (const struct de *)p1, *b = (const struct de *)p2; - const char *query_string = a->conn->request_info.query_string; - int cmp_result = 0; + if (p1 && p2) { + const struct de *a = (const struct de *)p1, *b = (const struct de *)p2; + const char *query_string = a->conn->request_info.query_string; + int cmp_result = 0; - if (query_string == NULL) { - query_string = "na"; - } + if (query_string == NULL) { + query_string = "na"; + } - if (a->file.is_directory && !b->file.is_directory) { - return -1; /* Always put directories on top */ - } else if (!a->file.is_directory && b->file.is_directory) { - 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') { - cmp_result = a->file.size == b->file.size - ? 0 - : a->file.size > b->file.size ? 1 : -1; - } else if (*query_string == 'd') { - cmp_result = - a->file.modification_time == b->file.modification_time - ? 0 - : a->file.modification_time > b->file.modification_time - ? 1 - : -1; - } + if (a->file.is_directory && !b->file.is_directory) { + return -1; /* Always put directories on top */ + } else if (!a->file.is_directory && b->file.is_directory) { + 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') { + cmp_result = a->file.size == b->file.size + ? 0 + : a->file.size > b->file.size ? 1 : -1; + } else if (*query_string == 'd') { + cmp_result = + a->file.modification_time == b->file.modification_time + ? 0 + : a->file.modification_time > b->file.modification_time + ? 1 + : -1; + } - return query_string[1] == 'd' ? -cmp_result : cmp_result; - } - return 0; + return query_string[1] == 'd' ? -cmp_result : cmp_result; + } + return 0; } static int must_hide_file(struct mg_connection *conn, const char *path) { - if (conn && conn->ctx) { - const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; - const char *pattern = conn->ctx->config[HIDE_FILES]; - return match_prefix(pw_pattern, (int)strlen(pw_pattern), path) > 0 || - (pattern != NULL && - match_prefix(pattern, (int)strlen(pattern), path) > 0); - } - return 0; + if (conn && conn->ctx) { + const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; + const char *pattern = conn->ctx->config[HIDE_FILES]; + return match_prefix(pw_pattern, (int)strlen(pw_pattern), path) > 0 || + (pattern != NULL && + match_prefix(pattern, (int)strlen(pattern), path) > 0); + } + return 0; } static int scan_directory(struct mg_connection *conn, const char *dir, void *data, void (*cb)(struct de *, void *)) { - char path[PATH_MAX]; - struct dirent *dp; - DIR *dirp; - struct de de; + char path[PATH_MAX]; + struct dirent *dp; + DIR *dirp; + struct de de; - if ((dirp = opendir(dir)) == NULL) { - return 0; - } else { - de.conn = conn; + if ((dirp = opendir(dir)) == NULL) { + return 0; + } else { + de.conn = conn; - while ((dp = readdir(dirp)) != NULL) { - /* Do not show current dir and hidden files */ - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") || - must_hide_file(conn, dp->d_name)) { - continue; - } + while ((dp = readdir(dirp)) != NULL) { + /* Do not show current dir and hidden files */ + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") || + must_hide_file(conn, dp->d_name)) { + continue; + } - mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', - dp->d_name); + 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 */ - memset(&de.file, 0, sizeof(de.file)); - if (!mg_stat(conn, path, &de.file)) { - mg_cry(conn, "%s: mg_stat(%s) failed: %s", __func__, path, - strerror(ERRNO)); - } + /* 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", __func__, path, + strerror(ERRNO)); + } - de.file_name = dp->d_name; - cb(&de, data); - } - (void)closedir(dirp); - } - return 1; + de.file_name = dp->d_name; + cb(&de, data); + } + (void)closedir(dirp); + } + return 1; } static int remove_directory(struct mg_connection *conn, const char *dir) { - char path[PATH_MAX]; - struct dirent *dp; - DIR *dirp; - struct de de; + char path[PATH_MAX]; + struct dirent *dp; + DIR *dirp; + struct de de; - if ((dirp = opendir(dir)) == NULL) { - return 0; - } else { - de.conn = conn; + if ((dirp = opendir(dir)) == NULL) { + return 0; + } else { + de.conn = conn; - while ((dp = readdir(dirp)) != NULL) { - /* 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; - } + while ((dp = readdir(dirp)) != NULL) { + /* 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; + } - mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', - dp->d_name); + 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 */ - memset(&de.file, 0, sizeof(de.file)); - if (!mg_stat(conn, path, &de.file)) { - mg_cry(conn, "%s: mg_stat(%s) failed: %s", __func__, path, - strerror(ERRNO)); - } - if (de.file.modification_time) { - if (de.file.is_directory) { - remove_directory(conn, path); - } else { - mg_remove(path); - } - } - } - (void)closedir(dirp); + /* 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", __func__, path, + strerror(ERRNO)); + } + if (de.file.modification_time) { + if (de.file.is_directory) { + remove_directory(conn, path); + } else { + mg_remove(path); + } + } + } + (void)closedir(dirp); - IGNORE_UNUSED_RESULT(rmdir(dir)); - } + IGNORE_UNUSED_RESULT(rmdir(dir)); + } - return 1; + return 1; } struct dir_scan_data { - struct de *entries; - int num_entries; - int arr_size; + struct de *entries; + int num_entries; + int arr_size; }; /* Behaves like realloc(), but frees original pointer on failure */ static void *realloc2(void *ptr, size_t size) { - void *new_ptr = mg_realloc(ptr, size); - if (new_ptr == NULL) { - mg_free(ptr); - } - return new_ptr; + void *new_ptr = mg_realloc(ptr, size); + if (new_ptr == NULL) { + mg_free(ptr); + } + return new_ptr; } static void dir_scan_callback(struct de *de, void *data) { - struct dir_scan_data *dsd = (struct dir_scan_data *)data; + struct dir_scan_data *dsd = (struct dir_scan_data *)data; - if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) { - dsd->arr_size *= 2; - dsd->entries = (struct de *)realloc2( - dsd->entries, dsd->arr_size * sizeof(dsd->entries[0])); - } - if (dsd->entries == NULL) { - /* 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); - dsd->entries[dsd->num_entries].file = de->file; - dsd->entries[dsd->num_entries].conn = de->conn; - dsd->num_entries++; - } + if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) { + dsd->arr_size *= 2; + dsd->entries = (struct de *)realloc2( + dsd->entries, dsd->arr_size * sizeof(dsd->entries[0])); + } + if (dsd->entries == NULL) { + /* 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); + dsd->entries[dsd->num_entries].file = de->file; + dsd->entries[dsd->num_entries].conn = de->conn; + dsd->num_entries++; + } } static void handle_directory_request(struct mg_connection *conn, const char *dir) { - int i, sort_direction; - struct dir_scan_data data = {NULL, 0, 128}; - char date[64]; - time_t curtime = time(NULL); + int i, sort_direction; + struct dir_scan_data data = {NULL, 0, 128}; + char date[64]; + time_t curtime = time(NULL); - if (!scan_directory(conn, dir, &data, dir_scan_callback)) { - send_http_error(conn, 500, - "Error: Cannot open directory\nopendir(%s): %s", dir, - strerror(ERRNO)); - return; - } + if (!scan_directory(conn, dir, &data, dir_scan_callback)) { + send_http_error(conn, 500, + "Error: Cannot open directory\nopendir(%s): %s", dir, + strerror(ERRNO)); + return; + } - gmt_time_string(date, sizeof(date), &curtime); + gmt_time_string(date, sizeof(date), &curtime); - if (!conn) - return; + if (!conn) + return; - sort_direction = conn->request_info.query_string != NULL && - conn->request_info.query_string[1] == 'd' - ? 'a' - : 'd'; + sort_direction = conn->request_info.query_string != NULL && + conn->request_info.query_string[1] == 'd' + ? 'a' + : 'd'; - conn->must_close = 1; - mg_printf(conn, "HTTP/1.1 200 OK\r\n" - "Date: %s\r\n" - "Connection: close\r\n" - "Content-Type: text/html; charset=utf-8\r\n\r\n", - date); + conn->must_close = 1; + mg_printf(conn, "HTTP/1.1 200 OK\r\n" + "Date: %s\r\n" + "Connection: close\r\n" + "Content-Type: text/html; charset=utf-8\r\n\r\n", + date); - conn->num_bytes_sent += mg_printf( - conn, "Index of %s" - "" - "

Index of %s

"
-              ""
-              ""
-              ""
-              "",
-        conn->request_info.uri, conn->request_info.uri, sort_direction,
-        sort_direction, sort_direction);
+	conn->num_bytes_sent += mg_printf(
+	    conn, "Index of %s"
+	          ""
+	          "

Index of %s

NameModifiedSize

" + "" + "" + "" + "", + conn->request_info.uri, conn->request_info.uri, sort_direction, + sort_direction, sort_direction); - /* Print first entry - link to a parent directory */ - conn->num_bytes_sent += - mg_printf(conn, "" - "\n", - conn->request_info.uri, "..", "Parent directory", "-", "-"); + /* Print first entry - link to a parent directory */ + conn->num_bytes_sent += + mg_printf(conn, "" + "\n", + conn->request_info.uri, "..", "Parent directory", "-", "-"); - /* Sort and print directory entries */ - if (data.entries != NULL) { - qsort(data.entries, (size_t)data.num_entries, sizeof(data.entries[0]), - compare_dir_entries); - for (i = 0; i < data.num_entries; i++) { - print_dir_entry(&data.entries[i]); - mg_free(data.entries[i].file_name); - } - mg_free(data.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); + for (i = 0; i < data.num_entries; i++) { + print_dir_entry(&data.entries[i]); + mg_free(data.entries[i].file_name); + } + mg_free(data.entries); + } - conn->num_bytes_sent += mg_printf(conn, "%s", "
NameModifiedSize

%s %s  %s
%s %s  %s
"); - conn->status_code = 200; + conn->num_bytes_sent += mg_printf(conn, "%s", ""); + conn->status_code = 200; } /* 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; + char buf[MG_BUF_LEN]; + int to_read, num_read, num_written; - /* Sanity check the offset */ - if (!filep) - return; - offset = offset < 0 ? 0 : offset > filep->size ? filep->size : offset; + /* Sanity check the offset */ + if (!filep) + return; + offset = offset < 0 ? 0 : offset > filep->size ? filep->size : offset; - if (len > 0 && filep->membuf != NULL && filep->size > 0) { - if (len > filep->size - offset) { - len = filep->size - offset; - } - mg_write(conn, filep->membuf + offset, (size_t)len); - } else if (len > 0 && filep->fp != NULL) { - if (offset > 0 && fseeko(filep->fp, offset, SEEK_SET) != 0) { - mg_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO)); - } - while (len > 0) { - /* 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; - } + if (len > 0 && filep->membuf != NULL && filep->size > 0) { + if (len > filep->size - offset) { + len = filep->size - offset; + } + mg_write(conn, filep->membuf + offset, (size_t)len); + } else if (len > 0 && filep->fp != NULL) { + if (offset > 0 && fseeko(filep->fp, offset, SEEK_SET) != 0) { + mg_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO)); + } + while (len > 0) { + /* 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 */ - if ((num_read = (int)fread(buf, 1, (size_t)to_read, filep->fp)) <= - 0) { - break; - } + /* 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 */ - if ((num_written = mg_write(conn, buf, (size_t)num_read)) != - num_read) { - break; - } + /* 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 */ - if (!conn) - break; - conn->num_bytes_sent += num_written; - len -= num_written; - } - } + /* Both read and were successful, adjust counters */ + if (!conn) + break; + conn->num_bytes_sent += num_written; + len -= num_written; + } + } } static int parse_range_header(const char *header, int64_t *a, int64_t *b) { - return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); + return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); } static void construct_etag(char *buf, size_t buf_len, const struct file *filep) { - if (filep) - snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", - (unsigned long)filep->modification_time, filep->size); + if (filep) + snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", + (unsigned long)filep->modification_time, filep->size); } static void fclose_on_exec(struct file *filep, struct mg_connection *conn) { - if (filep != NULL && filep->fp != NULL) { + if (filep != NULL && filep->fp != NULL) { #ifdef _WIN32 - (void)conn; /* Unused. */ + (void)conn; /* Unused. */ #else - if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) { - mg_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, - strerror(ERRNO)); - } + if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) { + mg_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, + strerror(ERRNO)); + } #endif - } + } } static void handle_static_file_request(struct mg_connection *conn, const char *path, struct file *filep) { - char date[64], lm[64], etag[64], range[64]; - const char *msg = "OK", *hdr; - time_t curtime = time(NULL); - int64_t cl, r1, r2; - struct vec mime_vec; - int n; - char gz_path[PATH_MAX]; - const char *encoding = ""; - const char *cors1, *cors2, *cors3; + char date[64], lm[64], etag[64], range[64]; + const char *msg = "OK", *hdr; + time_t curtime = time(NULL); + int64_t cl, r1, r2; + struct vec mime_vec; + int n; + char gz_path[PATH_MAX]; + const char *encoding = ""; + const char *cors1, *cors2, *cors3; - if (!conn || !conn->ctx || !filep) - return; + if (!conn || !conn->ctx || !filep) + return; - get_mime_type(conn->ctx, path, &mime_vec); - cl = filep->size; - conn->status_code = 200; - range[0] = '\0'; + get_mime_type(conn->ctx, path, &mime_vec); + cl = filep->size; + 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 (filep->gzipped) { - snprintf(gz_path, sizeof(gz_path), "%s.gz", path); - path = gz_path; - encoding = "Content-Encoding: gzip\r\n"; - } + /* 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; + encoding = "Content-Encoding: gzip\r\n"; + } - if (!mg_fopen(conn, path, "rb", filep)) { - send_http_error(conn, 500, "Error: Cannot open file\nfopen(%s): %s", - path, strerror(ERRNO)); - return; - } + if (!mg_fopen(conn, path, "rb", filep)) { + send_http_error(conn, 500, "Error: Cannot open file\nfopen(%s): %s", + path, strerror(ERRNO)); + return; + } - fclose_on_exec(filep, conn); + fclose_on_exec(filep, conn); - /* 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) */ - if (filep->gzipped) { - send_http_error( - conn, 501, "%s", - "Error: Range requests in gzipped files are not supported"); - mg_fclose(filep); - return; - } - conn->status_code = 206; - cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1 : cl - r1; - mg_snprintf(conn, range, sizeof(range), - "Content-Range: bytes " - "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", - r1, r1 + cl - 1, filep->size); - msg = "Partial Content"; - } + /* 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) */ + if (filep->gzipped) { + send_http_error( + conn, 501, "%s", + "Error: Range requests in gzipped files are not supported"); + mg_fclose(filep); + return; + } + conn->status_code = 206; + cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1 : cl - r1; + mg_snprintf(conn, range, sizeof(range), + "Content-Range: bytes " + "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", + r1, r1 + cl - 1, filep->size); + msg = "Partial Content"; + } - hdr = mg_get_header(conn, "Origin"); - if (hdr) { - /* Cross-origin resource sharing (CORS), see - http://www.html5rocks.com/en/tutorials/cors/, - http://www.html5rocks.com/static/images/cors_server_flowchart.png - - preflight is not supported for files. */ - cors1 = "Access-Control-Allow-Origin: "; - cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; - cors3 = "\r\n"; - } else { - cors1 = cors2 = cors3 = ""; - } + hdr = mg_get_header(conn, "Origin"); + if (hdr) { + /* Cross-origin resource sharing (CORS), see + http://www.html5rocks.com/en/tutorials/cors/, + http://www.html5rocks.com/static/images/cors_server_flowchart.png - + preflight is not supported for files. */ + cors1 = "Access-Control-Allow-Origin: "; + cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; + cors3 = "\r\n"; + } else { + cors1 = cors2 = cors3 = ""; + } - /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to - http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */ - gmt_time_string(date, sizeof(date), &curtime); - gmt_time_string(lm, sizeof(lm), &filep->modification_time); - construct_etag(etag, sizeof(etag), filep); + /* 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); - (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n" - "%s%s%s" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Etag: %s\r\n" - "Content-Type: %.*s\r\n" - "Content-Length: %" INT64_FMT "\r\n" - "Connection: %s\r\n" - "Accept-Ranges: bytes\r\n" - "%s%s\r\n", - conn->status_code, msg, cors1, cors2, cors3, date, lm, etag, - (int)mime_vec.len, mime_vec.ptr, cl, - suggest_connection_header(conn), range, encoding); + (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n" + "%s%s%s" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "Etag: %s\r\n" + "Content-Type: %.*s\r\n" + "Content-Length: %" INT64_FMT "\r\n" + "Connection: %s\r\n" + "Accept-Ranges: bytes\r\n" + "%s%s\r\n", + conn->status_code, msg, cors1, cors2, cors3, date, lm, etag, + (int)mime_vec.len, mime_vec.ptr, cl, + suggest_connection_header(conn), range, encoding); - if (strcmp(conn->request_info.request_method, "HEAD") != 0) { - send_file_data(conn, filep, r1, cl); - } - mg_fclose(filep); + if (strcmp(conn->request_info.request_method, "HEAD") != 0) { + send_file_data(conn, filep, r1, cl); + } + mg_fclose(filep); } void mg_send_file(struct mg_connection *conn, const char *path) { - struct file file = STRUCT_FILE_INITIALIZER; - if (mg_stat(conn, path, &file)) { - if (file.is_directory) { - if (!conn) - return; - if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], - "yes")) { - handle_directory_request(conn, path); - } else { - send_http_error(conn, 403, "%s", - "Error: Directory listing denied"); - } - } else { - handle_static_file_request(conn, path, &file); - } - } else { - send_http_error(conn, 404, "%s", "Error: File not found"); - } + struct file file = STRUCT_FILE_INITIALIZER; + if (mg_stat(conn, path, &file)) { + if (file.is_directory) { + if (!conn) + return; + if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], + "yes")) { + handle_directory_request(conn, path); + } else { + send_http_error(conn, 403, "%s", + "Error: Directory listing denied"); + } + } else { + handle_static_file_request(conn, path, &file); + } + } else { + send_http_error(conn, 404, "%s", "Error: File not found"); + } } /* 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; + int i; - if (!ri) - return; + if (!ri) + return; - for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) { - ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0); - ri->http_headers[i].value = skip(buf, "\r\n"); - if (ri->http_headers[i].name[0] == '\0') - break; - ri->num_headers = i + 1; - } + for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) { + ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0); + ri->http_headers[i].value = skip(buf, "\r\n"); + if (ri->http_headers[i].name[0] == '\0') + break; + ri->num_headers = i + 1; + } } static int is_valid_http_method(const char *method) { - return !strcmp(method, "GET") || !strcmp(method, "POST") || - !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") || - !strcmp(method, "PUT") || !strcmp(method, "DELETE") || - !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND") || - !strcmp(method, "MKCOL"); + return !strcmp(method, "GET") || !strcmp(method, "POST") || + !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") || + !strcmp(method, "PUT") || !strcmp(method, "DELETE") || + !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND") || + !strcmp(method, "MKCOL"); } /* 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) { - if (!ri) - return 0; - /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr, - * remote_port */ - ri->remote_user = ri->request_method = ri->uri = ri->http_version = - NULL; - ri->num_headers = 0; + int is_request, request_length = get_request_len(buf, len); + if (request_length > 0) { + if (!ri) + return 0; + /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr, + * remote_port */ + ri->remote_user = ri->request_method = ri->uri = ri->http_version = + NULL; + ri->num_headers = 0; - buf[request_length - 1] = '\0'; + buf[request_length - 1] = '\0'; - /* RFC says that all initial whitespaces should be ingored */ - while (*buf != '\0' && isspace(*(unsigned char *)buf)) { - buf++; - } - ri->request_method = skip(&buf, " "); - ri->uri = skip(&buf, " "); - ri->http_version = skip(&buf, "\r\n"); + /* RFC says that all initial whitespaces should be ingored */ + while (*buf != '\0' && isspace(*(unsigned char *)buf)) { + buf++; + } + ri->request_method = skip(&buf, " "); + 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 ..." */ - 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)) { - request_length = -1; - } else { - if (is_request) { - ri->http_version += 5; - } - parse_http_headers(&buf, ri); - } - } - return request_length; + /* 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)) { + request_length = -1; + } else { + if (is_request) { + ri->http_version += 5; + } + parse_http_headers(&buf, ri); + } + } + return request_length; } /* Keep reading the input (either opened file descriptor fd, or socket sock, @@ -4740,38 +4746,38 @@ 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) { - int request_len, n = 0; - struct timespec last_action_time = {0, 0}; - double request_timeout; + int request_len, n = 0; + struct timespec last_action_time = {0, 0}; + double request_timeout; - if (!conn) - return 0; + if (!conn) + return 0; - if (conn->ctx->config[REQUEST_TIMEOUT]) { - /* value of request_timeout is in seconds, config in milliseconds */ - request_timeout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; - } else { - request_timeout = -1.0; - } + if (conn->ctx->config[REQUEST_TIMEOUT]) { + /* value of request_timeout is in seconds, config in milliseconds */ + request_timeout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; + } else { + request_timeout = -1.0; + } - request_len = get_request_len(buf, *nread); - while ((conn->ctx->stop_flag == 0) && (*nread < bufsiz) && - (request_len == 0) && - ((mg_difftimespec(&last_action_time, &(conn->req_time)) <= - request_timeout) || - (request_timeout < 0)) && - ((n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0)) { - *nread += n; - /* assert(*nread <= bufsiz); */ - if (*nread > bufsiz) - return -2; - request_len = get_request_len(buf, *nread); - if (request_timeout > 0.0) { - clock_gettime(CLOCK_MONOTONIC, &last_action_time); - } - } + request_len = get_request_len(buf, *nread); + while ((conn->ctx->stop_flag == 0) && (*nread < bufsiz) && + (request_len == 0) && + ((mg_difftimespec(&last_action_time, &(conn->req_time)) <= + request_timeout) || + (request_timeout < 0)) && + ((n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0)) { + *nread += n; + /* assert(*nread <= bufsiz); */ + if (*nread > bufsiz) + return -2; + request_len = get_request_len(buf, *nread); + if (request_timeout > 0.0) { + clock_gettime(CLOCK_MONOTONIC, &last_action_time); + } + } - return (request_len <= 0 && n <= 0) ? -1 : request_len; + return (request_len <= 0 && n <= 0) ? -1 : request_len; } /* For given directory path, substitute it to valid index file. @@ -4779,142 +4785,142 @@ Return 1 if index file has been found, 0 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) { - if (conn && conn->ctx) { - const char *list = conn->ctx->config[INDEX_FILES]; - struct file file = STRUCT_FILE_INITIALIZER; - struct vec filename_vec; - size_t n = strlen(path); - int found = 0; + if (conn && conn->ctx) { + const char *list = conn->ctx->config[INDEX_FILES]; + struct file file = STRUCT_FILE_INITIALIZER; + struct vec filename_vec; + 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. */ - while (n > 0 && path[n - 1] == '/') { - n--; - } - path[n] = '/'; + /* 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 */ - while ((list = next_option(list, &filename_vec, NULL)) != NULL) { - /* Ignore too long entries that may overflow path buffer */ - if (filename_vec.len > path_len - (n + 2)) - continue; + /* 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 */ + if (filename_vec.len > path_len - (n + 2)) + continue; - /* Prepare full path to the index file */ - mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); + /* Prepare full path to the index file */ + mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); - /* Does it exist? */ - if (mg_stat(conn, path, &file)) { - /* Yes it does, break the loop */ - *filep = file; - found = 1; - break; - } - } + /* Does it exist? */ + if (mg_stat(conn, path, &file)) { + /* Yes it does, break the loop */ + *filep = file; + found = 1; + break; + } + } - /* If no index file exists, restore directory path */ - if (!found) { - path[n] = '\0'; - } + /* If no index file exists, restore directory path */ + if (!found) { + path[n] = '\0'; + } - return found; - } - return 0; + return found; + } + return 0; } /* Return True if we should reply 304 Not Modified. */ static int is_not_modified(const struct mg_connection *conn, const struct file *filep) { - char etag[64]; - const char *ims = mg_get_header(conn, "If-Modified-Since"); - const char *inm = mg_get_header(conn, "If-None-Match"); - construct_etag(etag, sizeof(etag), filep); - if (!filep) - return 0; - return (inm != NULL && !mg_strcasecmp(etag, inm)) || - (ims != NULL && filep->modification_time <= parse_date_string(ims)); + char etag[64]; + const char *ims = mg_get_header(conn, "If-Modified-Since"); + const char *inm = mg_get_header(conn, "If-None-Match"); + construct_etag(etag, sizeof(etag), filep); + if (!filep) + return 0; + return (inm != NULL && !mg_strcasecmp(etag, inm)) || + (ims != NULL && filep->modification_time <= parse_date_string(ims)); } static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl) { - const char *expect, *body; - char buf[MG_BUF_LEN]; - int to_read, nread, success = 0; - int64_t buffered_len; + const char *expect, *body; + char buf[MG_BUF_LEN]; + int to_read, nread, success = 0; + int64_t buffered_len; - if (!conn) - return 0; + if (!conn) + return 0; - expect = mg_get_header(conn, "Expect"); - /* assert(fp != NULL); */ - if (!fp) { - send_http_error(conn, 500, "%s", "Error: NULL File"); - return 0; - } + expect = mg_get_header(conn, "Expect"); + /* assert(fp != NULL); */ + if (!fp) { + send_http_error(conn, 500, "%s", "Error: NULL File"); + return 0; + } - if (conn->content_len == -1 && !conn->is_chunked) { - /* Content length is not specified by the client. */ - send_http_error(conn, 411, "%s", - "Error: Client did not specify content length"); - } else if ((expect != NULL) && - (mg_strcasecmp(expect, "100-continue") != 0)) { - /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */ - send_http_error(conn, 417, "Error: Can not fulfill expectation %s", - expect); - } else { - if (expect != NULL) { - (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); - conn->status_code = 100; - } else { - conn->status_code = 200; - } + if (conn->content_len == -1 && !conn->is_chunked) { + /* Content length is not specified by the client. */ + send_http_error(conn, 411, "%s", + "Error: Client did not specify content length"); + } else if ((expect != NULL) && + (mg_strcasecmp(expect, "100-continue") != 0)) { + /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */ + send_http_error(conn, 417, "Error: Can not fulfill expectation %s", + expect); + } else { + if (expect != NULL) { + (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); + conn->status_code = 100; + } else { + conn->status_code = 200; + } - buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len - - conn->consumed_content; - /* - assert(buffered_len >= 0); - assert(conn->consumed_content == 0); - */ - if ((buffered_len < 0) || (conn->consumed_content != 0)) { - send_http_error(conn, 500, "%s", "Error: Size mismatch"); - return 0; - } + buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len - + conn->consumed_content; + /* + assert(buffered_len >= 0); + assert(conn->consumed_content == 0); + */ + if ((buffered_len < 0) || (conn->consumed_content != 0)) { + send_http_error(conn, 500, "%s", "Error: Size mismatch"); + return 0; + } - if (buffered_len > 0) { - if ((int64_t)buffered_len > conn->content_len) { - buffered_len = (int)conn->content_len; - } - body = conn->buf + conn->request_len + conn->consumed_content; - push(fp, sock, ssl, body, (int64_t)buffered_len); - conn->consumed_content += buffered_len; - } + if (buffered_len > 0) { + if ((int64_t)buffered_len > conn->content_len) { + buffered_len = (int)conn->content_len; + } + body = conn->buf + conn->request_len + conn->consumed_content; + push(fp, sock, ssl, body, (int64_t)buffered_len); + conn->consumed_content += buffered_len; + } - nread = 0; - while (conn->consumed_content < conn->content_len) { - to_read = sizeof(buf); - if ((int64_t)to_read > conn->content_len - conn->consumed_content) { - to_read = (int)(conn->content_len - conn->consumed_content); - } - nread = pull(NULL, conn, buf, to_read); - if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) { - break; - } - conn->consumed_content += nread; - } + nread = 0; + while (conn->consumed_content < conn->content_len) { + to_read = sizeof(buf); + if ((int64_t)to_read > conn->content_len - conn->consumed_content) { + to_read = (int)(conn->content_len - conn->consumed_content); + } + nread = pull(NULL, conn, buf, to_read); + if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) { + break; + } + conn->consumed_content += nread; + } - if (conn->consumed_content == conn->content_len) { - success = nread >= 0; - } + if (conn->consumed_content == conn->content_len) { + success = nread >= 0; + } - /* Each error code path in this function must send an error */ - if (!success) { - /* TODO: Maybe some data has already been sent. */ - send_http_error(conn, 500, "%s", ""); - } - } + /* Each error code path in this function must send an error */ + if (!success) { + /* TODO: Maybe some data has already been sent. */ + send_http_error(conn, 500, "%s", ""); + } + } - return success; + return success; } #if !defined(NO_CGI) @@ -4927,11 +4933,11 @@ 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 */ + 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 */ }; static char *addenv(struct cgi_env_block *block, @@ -4941,350 +4947,350 @@ static char *addenv(struct cgi_env_block *block, /* 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; + int n, space; + char *added; + va_list ap; - if (!block) - return NULL; + if (!block) + return NULL; - /* Calculate how much space is left in the buffer */ - space = sizeof(block->buf) - block->len - 2; - /* assert(space >= 0); */ - if (space < 0) - return NULL; + /* Calculate how much space is left in the buffer */ + space = sizeof(block->buf) - block->len - 2; + /* assert(space >= 0); */ + if (space < 0) + return NULL; - /* Make a pointer to the free space int the buffer */ - added = block->buf + block->len; + /* Make a pointer to the free space int the buffer */ + added = block->buf + block->len; - /* 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); + /* 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 */ - 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 */ - block->vars[block->nvars++] = added; - /* 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); - } + /* 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 */ + block->vars[block->nvars++] = added; + /* 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); + } - return added; + return added; } static void prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_env_block *blk) { - const char *s; - struct vec var_vec; - char *p, src_addr[IP_ADDR_STR_LEN]; - int i; + const char *s; + struct vec var_vec; + char *p, src_addr[IP_ADDR_STR_LEN]; + int i; - if (conn == NULL || blk == NULL) - return; + if (conn == NULL || blk == NULL) + return; - blk->len = blk->nvars = 0; - blk->conn = conn; - sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); + blk->len = blk->nvars = 0; + blk->conn = conn; + sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]); - addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); - addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); - addenv(blk, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version()); + addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]); + addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); + addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); + addenv(blk, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version()); - /* 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 */ + /* 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 */ - /* TODO(lsm): fix this for IPv6 case */ - addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port)); + /* 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); - addenv(blk, "REMOTE_ADDR=%s", src_addr); - addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port); - addenv(blk, "REQUEST_URI=%s", conn->request_info.uri); + addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method); + addenv(blk, "REMOTE_ADDR=%s", src_addr); + addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port); + addenv(blk, "REQUEST_URI=%s", conn->request_info.uri); - /* SCRIPT_NAME */ - addenv(blk, "SCRIPT_NAME=%.*s", - (int)strlen(conn->request_info.uri) - - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)), - conn->request_info.uri); + /* SCRIPT_NAME */ + addenv(blk, "SCRIPT_NAME=%.*s", + (int)strlen(conn->request_info.uri) - + ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)), + conn->request_info.uri); - addenv(blk, "SCRIPT_FILENAME=%s", prog); - if (conn->path_info == NULL) { - addenv(blk, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT]); - } else { - addenv(blk, "PATH_TRANSLATED=%s%s", conn->ctx->config[DOCUMENT_ROOT], - conn->path_info); - } + addenv(blk, "SCRIPT_FILENAME=%s", prog); + if (conn->path_info == NULL) { + addenv(blk, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT]); + } else { + addenv(blk, "PATH_TRANSLATED=%s%s", conn->ctx->config[DOCUMENT_ROOT], + conn->path_info); + } - addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on"); + addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on"); - if ((s = mg_get_header(conn, "Content-Type")) != NULL) - addenv(blk, "CONTENT_TYPE=%s", s); + if ((s = mg_get_header(conn, "Content-Type")) != NULL) + addenv(blk, "CONTENT_TYPE=%s", s); - if (conn->request_info.query_string != NULL) - addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string); + if (conn->request_info.query_string != NULL) + addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string); - if ((s = mg_get_header(conn, "Content-Length")) != NULL) - addenv(blk, "CONTENT_LENGTH=%s", s); + if ((s = mg_get_header(conn, "Content-Length")) != NULL) + addenv(blk, "CONTENT_LENGTH=%s", s); - if ((s = getenv("PATH")) != NULL) - addenv(blk, "PATH=%s", s); + if ((s = getenv("PATH")) != NULL) + addenv(blk, "PATH=%s", s); - if (conn->path_info != NULL) { - addenv(blk, "PATH_INFO=%s", conn->path_info); - } + if (conn->path_info != NULL) { + addenv(blk, "PATH_INFO=%s", conn->path_info); + } - if (conn->status_code > 0) { - /* CGI error handler should show the status code */ - addenv(blk, "STATUS=%d", conn->status_code); - } + if (conn->status_code > 0) { + /* CGI error handler should show the status code */ + addenv(blk, "STATUS=%d", conn->status_code); + } #if defined(_WIN32) - if ((s = getenv("COMSPEC")) != NULL) { - addenv(blk, "COMSPEC=%s", s); - } - if ((s = getenv("SYSTEMROOT")) != NULL) { - addenv(blk, "SYSTEMROOT=%s", s); - } - if ((s = getenv("SystemDrive")) != NULL) { - addenv(blk, "SystemDrive=%s", s); - } - if ((s = getenv("ProgramFiles")) != NULL) { - addenv(blk, "ProgramFiles=%s", s); - } - if ((s = getenv("ProgramFiles(x86)")) != NULL) { - addenv(blk, "ProgramFiles(x86)=%s", s); - } + if ((s = getenv("COMSPEC")) != NULL) { + addenv(blk, "COMSPEC=%s", s); + } + if ((s = getenv("SYSTEMROOT")) != NULL) { + addenv(blk, "SYSTEMROOT=%s", s); + } + if ((s = getenv("SystemDrive")) != NULL) { + addenv(blk, "SystemDrive=%s", s); + } + if ((s = getenv("ProgramFiles")) != NULL) { + addenv(blk, "ProgramFiles=%s", s); + } + if ((s = getenv("ProgramFiles(x86)")) != NULL) { + addenv(blk, "ProgramFiles(x86)=%s", s); + } #else - if ((s = getenv("LD_LIBRARY_PATH")) != NULL) - addenv(blk, "LD_LIBRARY_PATH=%s", s); + if ((s = getenv("LD_LIBRARY_PATH")) != NULL) + addenv(blk, "LD_LIBRARY_PATH=%s", s); #endif /* _WIN32 */ - if ((s = getenv("PERLLIB")) != NULL) - addenv(blk, "PERLLIB=%s", s); + if ((s = getenv("PERLLIB")) != NULL) + addenv(blk, "PERLLIB=%s", s); - if (conn->request_info.remote_user != NULL) { - addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user); - addenv(blk, "%s", "AUTH_TYPE=Digest"); - } + if (conn->request_info.remote_user != NULL) { + addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user); + addenv(blk, "%s", "AUTH_TYPE=Digest"); + } - /* 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); + /* 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 _ */ - for (; *p != '=' && *p != '\0'; p++) { - if (*p == '-') - *p = '_'; - *p = (char)toupper(*(unsigned char *)p); - } - } + /* Convert variable name into uppercase, and change - to _ */ + for (; *p != '=' && *p != '\0'; p++) { + if (*p == '-') + *p = '_'; + *p = (char)toupper(*(unsigned char *)p); + } + } - /* 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); - } + /* 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); + } - blk->vars[blk->nvars++] = NULL; - blk->buf[blk->len++] = '\0'; + blk->vars[blk->nvars++] = NULL; + blk->buf[blk->len++] = '\0'; - /* - assert(blk->nvars < (int) ARRAY_SIZE(blk->vars)); - assert(blk->len > 0); - assert(blk->len < (int) sizeof(blk->buf)); - */ - if ((blk->nvars >= (int)ARRAY_SIZE(blk->vars)) || (blk->len <= 0) || - (blk->len >= (int)sizeof(blk->buf))) - return; + /* + assert(blk->nvars < (int) ARRAY_SIZE(blk->vars)); + assert(blk->len > 0); + assert(blk->len < (int) sizeof(blk->buf)); + */ + if ((blk->nvars >= (int)ARRAY_SIZE(blk->vars)) || (blk->len <= 0) || + (blk->len >= (int)sizeof(blk->buf))) + return; } 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, *connection_state; - char *pbuf, dir[PATH_MAX], *p; - struct mg_request_info ri; - struct cgi_env_block blk; - FILE *in = NULL, *out = NULL; - struct file fout = STRUCT_FILE_INITIALIZER; - pid_t pid = (pid_t)-1; + char *buf; + size_t buflen; + int headers_len, data_len, i, fdin[2] = {0, 0}, fdout[2] = {0, 0}; + const char *status, *status_text, *connection_state; + char *pbuf, dir[PATH_MAX], *p; + struct mg_request_info ri; + struct cgi_env_block blk; + FILE *in = NULL, *out = NULL; + struct file fout = STRUCT_FILE_INITIALIZER; + pid_t pid = (pid_t)-1; - if (conn == NULL) - return; + if (conn == NULL) + return; - buf = NULL; - buflen = 16384; - prepare_cgi_environment(conn, prog, &blk); + buf = NULL; + 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'. */ - (void)mg_snprintf(conn, dir, sizeof(dir), "%s", prog); - if ((p = strrchr(dir, '/')) != NULL) { - *p++ = '\0'; - } else { - dir[0] = '.', dir[1] = '\0'; - p = (char *)prog; - } + /* 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'; + } else { + dir[0] = '.', dir[1] = '\0'; + p = (char *)prog; + } - if (pipe(fdin) != 0 || pipe(fdout) != 0) { - send_http_error(conn, 500, "Error: Cannot create CGI pipe: %s", - strerror(ERRNO)); - goto done; - } + if (pipe(fdin) != 0 || pipe(fdout) != 0) { + send_http_error(conn, 500, "Error: Cannot create CGI pipe: %s", + strerror(ERRNO)); + goto done; + } - pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir); - if (pid == (pid_t)-1) { - send_http_error(conn, 500, "Error: Cannot spawn CGI process [%s]: %s", - prog, strerror(ERRNO)); - goto done; - } + pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir); + if (pid == (pid_t)-1) { + send_http_error(conn, 500, "Error: Cannot spawn CGI process [%s]: %s", + prog, strerror(ERRNO)); + goto done; + } - /* 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); + /* 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. */ - (void)close(fdin[0]); - (void)close(fdout[1]); - fdin[0] = fdout[1] = -1; + /* 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; - if ((in = fdopen(fdin[1], "wb")) == NULL) { - send_http_error(conn, 500, "Error: CGI can not open fdin\nfopen: %s", - strerror(ERRNO)); - goto done; - } - if ((out = fdopen(fdout[0], "rb")) == NULL) { - send_http_error(conn, 500, "Error: CGI can not open fdout\nfopen: %s", - strerror(ERRNO)); - goto done; - } + if ((in = fdopen(fdin[1], "wb")) == NULL) { + send_http_error(conn, 500, "Error: CGI can not open fdin\nfopen: %s", + strerror(ERRNO)); + goto done; + } + if ((out = fdopen(fdout[0], "rb")) == NULL) { + send_http_error(conn, 500, "Error: CGI can not open fdout\nfopen: %s", + strerror(ERRNO)); + goto done; + } - setbuf(in, NULL); - setbuf(out, NULL); - fout.fp = out; + setbuf(in, NULL); + setbuf(out, NULL); + fout.fp = out; - /* Send POST or PUT data to the CGI process if needed */ - if (!mg_strcasecmp(conn->request_info.request_method, "POST") || - !mg_strcasecmp(conn->request_info.request_method, "PUT")) { - /* This is a POST/PUT request */ - if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) { - /* Error sending the body data */ - goto done; - } - } + /* Send POST or PUT data to the CGI process if needed */ + if (!mg_strcasecmp(conn->request_info.request_method, "POST") || + !mg_strcasecmp(conn->request_info.request_method, "PUT")) { + /* This is a POST/PUT request */ + if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) { + /* Error sending the body data */ + goto done; + } + } - /* Close so child gets an EOF. */ - fclose(in); - in = NULL; - fdin[1] = -1; + /* 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. */ - data_len = 0; - buf = (char *)mg_malloc(buflen); - if (buf == NULL) { - send_http_error(conn, 500, - "Error: Not enough memory for CGI buffer (%u bytes)", - (unsigned int)buflen); - goto done; - } - headers_len = read_request(out, conn, buf, (int)buflen, &data_len); - if (headers_len <= 0) { - send_http_error(conn, 500, - "Error: CGI program sent malformed or too big " - "(>%u bytes) HTTP headers: [%.*s]", - (unsigned)buflen, data_len, buf); - goto done; - } - pbuf = buf; - buf[headers_len - 1] = '\0'; - parse_http_headers(&pbuf, &ri); + /* 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 = (char *)mg_malloc(buflen); + if (buf == NULL) { + send_http_error(conn, 500, + "Error: Not enough memory for CGI buffer (%u bytes)", + (unsigned int)buflen); + goto done; + } + headers_len = read_request(out, conn, buf, (int)buflen, &data_len); + if (headers_len <= 0) { + send_http_error(conn, 500, + "Error: CGI program sent malformed or too big " + "(>%u bytes) HTTP headers: [%.*s]", + (unsigned)buflen, data_len, buf); + goto done; + } + pbuf = buf; + buf[headers_len - 1] = '\0'; + parse_http_headers(&pbuf, &ri); - /* Make up and send the status line */ - status_text = "OK"; - if ((status = get_header(&ri, "Status")) != NULL) { - conn->status_code = atoi(status); - status_text = status; - while (isdigit(*(unsigned char *)status_text) || *status_text == ' ') { - status_text++; - } - } else if (get_header(&ri, "Location") != NULL) { - conn->status_code = 302; - } else { - conn->status_code = 200; - } - 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); + /* Make up and send the status line */ + status_text = "OK"; + if ((status = get_header(&ri, "Status")) != NULL) { + conn->status_code = atoi(status); + status_text = status; + while (isdigit(*(unsigned char *)status_text) || *status_text == ' ') { + status_text++; + } + } else if (get_header(&ri, "Location") != NULL) { + conn->status_code = 302; + } else { + conn->status_code = 200; + } + 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 */ - 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 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 */ - conn->num_bytes_sent += - mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len)); + /* 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 */ - send_file_data(conn, &fout, 0, INT64_MAX); + /* Read the rest of CGI output and send to the client */ + send_file_data(conn, &fout, 0, INT64_MAX); done: - if (pid != (pid_t)-1) { - kill(pid, SIGKILL); + if (pid != (pid_t)-1) { + kill(pid, SIGKILL); #if !defined(_WIN32) - { - int st; - while (waitpid(pid, &st, 0) != -1) - ; /* clean zombies */ - } + { + int st; + while (waitpid(pid, &st, 0) != -1) + ; /* clean zombies */ + } #endif - } - if (fdin[0] != -1) { - close(fdin[0]); - } - if (fdout[1] != -1) { - close(fdout[1]); - } + } + if (fdin[0] != -1) { + close(fdin[0]); + } + if (fdout[1] != -1) { + close(fdout[1]); + } - if (in != NULL) { - fclose(in); - } else if (fdin[1] != -1) { - close(fdin[1]); - } + if (in != NULL) { + fclose(in); + } else if (fdin[1] != -1) { + close(fdin[1]); + } - if (out != NULL) { - fclose(out); - } else if (fdout[0] != -1) { - close(fdout[0]); - } - if (buf != NULL) { - mg_free(buf); - } + if (out != NULL) { + fclose(out); + } else if (fdout[0] != -1) { + close(fdout[0]); + } + if (buf != NULL) { + mg_free(buf); + } } #endif /* !NO_CGI */ @@ -5295,255 +5301,255 @@ Return -1 for if the path is too long. Return -2 if path can not be created. */ static int put_dir(struct mg_connection *conn, const char *path) { - char buf[PATH_MAX]; - const char *s, *p; - struct file file = STRUCT_FILE_INITIALIZER; - int len, res = 1; + char buf[PATH_MAX]; + const char *s, *p; + struct file file = STRUCT_FILE_INITIALIZER; + int len, res = 1; - for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { - len = (int)(p - path); - if (len >= (int)sizeof(buf)) { - /* path too long */ - res = -1; - break; - } - memcpy(buf, path, len); - buf[len] = '\0'; + for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { + len = (int)(p - path); + if (len >= (int)sizeof(buf)) { + /* path too long */ + res = -1; + break; + } + memcpy(buf, path, len); + buf[len] = '\0'; - /* Try to create intermediate directory */ - DEBUG_TRACE("mkdir(%s)", buf); - if (!mg_stat(conn, buf, &file) && mg_mkdir(buf, 0755) != 0) { - /* path does not exixt and can not be created */ - res = -2; - break; - } + /* Try to create intermediate directory */ + DEBUG_TRACE("mkdir(%s)", buf); + if (!mg_stat(conn, buf, &file) && mg_mkdir(buf, 0755) != 0) { + /* path does not exixt and can not be created */ + res = -2; + break; + } - /* Is path itself a directory? */ - if (p[1] == '\0') { - res = 0; - } - } + /* Is path itself a directory? */ + if (p[1] == '\0') { + res = 0; + } + } - return res; + return res; } static void mkcol(struct mg_connection *conn, const char *path) { - int rc, body_len; - struct de de; - char date[64]; - time_t curtime = time(NULL); + int rc, body_len; + struct de de; + char date[64]; + time_t curtime = time(NULL); - if (conn == NULL) - return; + if (conn == NULL) + return; - /* TODO: Check the send_http_error situations in this function */ + /* TODO: Check the send_http_error situations in this function */ - memset(&de.file, 0, sizeof(de.file)); - if (!mg_stat(conn, path, &de.file)) { - mg_cry(conn, "%s: mg_stat(%s) failed: %s", __func__, path, - strerror(ERRNO)); - } + memset(&de.file, 0, sizeof(de.file)); + if (!mg_stat(conn, path, &de.file)) { + mg_cry(conn, "%s: mg_stat(%s) failed: %s", __func__, path, + strerror(ERRNO)); + } - if (de.file.modification_time) { - send_http_error(conn, 405, "Error: mkcol(%s): %s", path, - strerror(ERRNO)); - return; - } + if (de.file.modification_time) { + send_http_error(conn, 405, "Error: mkcol(%s): %s", path, + strerror(ERRNO)); + return; + } - body_len = conn->data_len - conn->request_len; - if (body_len > 0) { - send_http_error(conn, 415, "Error: mkcol(%s): %s", path, - strerror(ERRNO)); - return; - } + body_len = conn->data_len - conn->request_len; + if (body_len > 0) { + send_http_error(conn, 415, "Error: mkcol(%s): %s", path, + strerror(ERRNO)); + return; + } - rc = mg_mkdir(path, 0755); + rc = mg_mkdir(path, 0755); - if (rc == 0) { - conn->status_code = 201; - gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, "HTTP/1.1 %d Created\r\nDate: %s\r\nContent-Length: " - "0\r\nConnection: %s\r\n\r\n", - conn->status_code, date, suggest_connection_header(conn)); - } else if (rc == -1) { - if (errno == EEXIST) - send_http_error(conn, 405, "Error:mkcol(%s): %s", path, - strerror(ERRNO)); - else if (errno == EACCES) - send_http_error(conn, 403, "Error: mkcol(%s): %s", path, - strerror(ERRNO)); - else if (errno == ENOENT) - send_http_error(conn, 409, "Error: mkcol(%s): %s", path, - strerror(ERRNO)); - else - send_http_error(conn, 500, "fopen(%s): %s", path, strerror(ERRNO)); - } + if (rc == 0) { + conn->status_code = 201; + gmt_time_string(date, sizeof(date), &curtime); + mg_printf(conn, "HTTP/1.1 %d Created\r\nDate: %s\r\nContent-Length: " + "0\r\nConnection: %s\r\n\r\n", + conn->status_code, date, suggest_connection_header(conn)); + } else if (rc == -1) { + if (errno == EEXIST) + send_http_error(conn, 405, "Error:mkcol(%s): %s", path, + strerror(ERRNO)); + else if (errno == EACCES) + send_http_error(conn, 403, "Error: mkcol(%s): %s", path, + strerror(ERRNO)); + else if (errno == ENOENT) + send_http_error(conn, 409, "Error: mkcol(%s): %s", path, + strerror(ERRNO)); + else + send_http_error(conn, 500, "fopen(%s): %s", path, strerror(ERRNO)); + } } static void put_file(struct mg_connection *conn, const char *path) { - struct file file = STRUCT_FILE_INITIALIZER; - const char *range; - int64_t r1, r2; - int rc; - char date[64]; - time_t curtime = time(NULL); + struct file file = STRUCT_FILE_INITIALIZER; + const char *range; + int64_t r1, r2; + int rc; + char date[64]; + time_t curtime = time(NULL); - if (conn == NULL) - return; + if (conn == NULL) + return; - if (mg_stat(conn, path, &file)) { - /* File already exists */ - conn->status_code = 200; + if (mg_stat(conn, path, &file)) { + /* File already exists */ + conn->status_code = 200; - if (file.is_directory) { - /* This is an already existing directory, - so there is nothing to do for the server. */ - rc = 0; + if (file.is_directory) { + /* This is an already existing directory, + so there is nothing to do for the server. */ + rc = 0; - } else { - /* File exists and is not a directory. */ - /* Can it be replaced? */ + } else { + /* File exists and is not a directory. */ + /* Can it be replaced? */ - if (file.modification_time == 0) { - /* This is an "in-memory" file, that can not be replaced */ - send_http_error( - conn, 405, - "Error: Put not possible\nReplacing %s is not supported", - path); - return; - } + if (file.modification_time == 0) { + /* This is an "in-memory" file, that can not be replaced */ + send_http_error( + conn, 405, + "Error: Put not possible\nReplacing %s is not supported", + path); + return; + } - /* Check if the server may write this file */ - if (access(path, W_OK) == 0) { - /* Access granted */ - conn->status_code = 200; - rc = 1; - } else { - send_http_error( - conn, 403, - "Error: Put not possible\nReplacing %s is not allowed", - path); - return; - } - } - } else { - /* File should be created */ - conn->status_code = 201; - rc = put_dir(conn, path); - } + /* Check if the server may write this file */ + if (access(path, W_OK) == 0) { + /* Access granted */ + conn->status_code = 200; + rc = 1; + } else { + send_http_error( + conn, 403, + "Error: Put not possible\nReplacing %s is not allowed", + path); + return; + } + } + } else { + /* File should be created */ + conn->status_code = 201; + rc = put_dir(conn, path); + } - if (rc == 0) { - /* put_dir returns 0 if path is a directory */ - gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Content-Length: 0\r\n" - "Connection: %s\r\n\r\n", - conn->status_code, - mg_get_response_code_text(conn->status_code, NULL), date, - suggest_connection_header(conn)); + if (rc == 0) { + /* put_dir returns 0 if path is a directory */ + gmt_time_string(date, sizeof(date), &curtime); + mg_printf(conn, "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Content-Length: 0\r\n" + "Connection: %s\r\n\r\n", + conn->status_code, + mg_get_response_code_text(conn->status_code, NULL), date, + suggest_connection_header(conn)); - /* Request to create a directory has been fulfilled successfully. - No need to put a file. */ - return; - } + /* Request to create a directory has been fulfilled successfully. + No need to put a file. */ + return; + } - if (rc == -1) { - /* put_dir returns -1 if the path is too long */ - send_http_error(conn, 414, "Error: Path too long\nput_dir(%s): %s", - path, strerror(ERRNO)); - return; - } + if (rc == -1) { + /* put_dir returns -1 if the path is too long */ + send_http_error(conn, 414, "Error: Path too long\nput_dir(%s): %s", + path, strerror(ERRNO)); + return; + } - if (rc == -2) { - /* put_dir returns -2 if the directory can not be created */ - send_http_error(conn, 500, - "Error: Can not create directory\nput_dir(%s): %s", - path, strerror(ERRNO)); - return; - } + if (rc == -2) { + /* put_dir returns -2 if the directory can not be created */ + send_http_error(conn, 500, + "Error: Can not create directory\nput_dir(%s): %s", + path, strerror(ERRNO)); + return; + } - /* A file should be created or overwritten. */ - if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) { - mg_fclose(&file); - send_http_error(conn, 500, "Error: Can not create file\nfopen(%s): %s", - path, strerror(ERRNO)); - return; - } + /* A file should be created or overwritten. */ + if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) { + mg_fclose(&file); + send_http_error(conn, 500, "Error: Can not create file\nfopen(%s): %s", + path, strerror(ERRNO)); + return; + } - fclose_on_exec(&file, conn); - range = mg_get_header(conn, "Content-Range"); - r1 = r2 = 0; - if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { - conn->status_code = 206; /* Partial content */ - fseeko(file.fp, r1, SEEK_SET); - } + fclose_on_exec(&file, conn); + range = mg_get_header(conn, "Content-Range"); + r1 = r2 = 0; + if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { + conn->status_code = 206; /* Partial content */ + fseeko(file.fp, r1, SEEK_SET); + } - if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { - /* forward_body_data failed. - The error code has already been sent to the client, - and conn->status_code is already set. */ - return; - } + if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { + /* forward_body_data failed. + The error code has already been sent to the client, + and conn->status_code is already set. */ + return; + } - gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Content-Length: 0\r\n" - "Connection: %s\r\n\r\n", - conn->status_code, - mg_get_response_code_text(conn->status_code, NULL), date, - suggest_connection_header(conn)); + gmt_time_string(date, sizeof(date), &curtime); + mg_printf(conn, "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Content-Length: 0\r\n" + "Connection: %s\r\n\r\n", + conn->status_code, + mg_get_response_code_text(conn->status_code, NULL), date, + suggest_connection_header(conn)); - mg_fclose(&file); + mg_fclose(&file); } static void delete_file(struct mg_connection *conn, const char *path) { - struct de de; - memset(&de.file, 0, sizeof(de.file)); - if (!mg_stat(conn, path, &de.file)) { - /* mg_stat returns 0 if the file does not exist */ - send_http_error(conn, 404, - "Error: Cannot delete file\nFile %s not found", path); - return; - } + struct de de; + memset(&de.file, 0, sizeof(de.file)); + if (!mg_stat(conn, path, &de.file)) { + /* mg_stat returns 0 if the file does not exist */ + send_http_error(conn, 404, + "Error: Cannot delete file\nFile %s not found", path); + return; + } - if (de.file.modification_time == 0) { - /* mg_stat returns != 0 and modification_time == 0 - if the file is cached in memory */ - send_http_error( - conn, 405, - "Error: Delete not possible\nDeleting %s is not supported", path); - return; - } + if (de.file.modification_time == 0) { + /* mg_stat returns != 0 and modification_time == 0 + if the file is cached in memory */ + send_http_error( + conn, 405, + "Error: Delete not possible\nDeleting %s is not supported", path); + return; + } - if (de.file.is_directory) { - remove_directory(conn, path); - /* TODO: remove_dir does not return success of the operation */ - /* Assume delete is successful: Return 204 without content. */ - send_http_error(conn, 204, "%s", ""); - return; - } + if (de.file.is_directory) { + remove_directory(conn, path); + /* TODO: remove_dir does not return success of the operation */ + /* Assume delete is successful: Return 204 without content. */ + send_http_error(conn, 204, "%s", ""); + return; + } - /* This is an existing file (not a directory). - Check if write permission is granted. */ - if (access(path, W_OK) != 0) { - /* File is read only */ - send_http_error( - conn, 403, "Error: Delete not possible\nDeleting %s is not allowed", - path); - return; - } + /* This is an existing file (not a directory). + Check if write permission is granted. */ + if (access(path, W_OK) != 0) { + /* File is read only */ + send_http_error( + conn, 403, "Error: Delete not possible\nDeleting %s is not allowed", + path); + return; + } - /* Try to delete it. */ - if (mg_remove(path) == 0) { - /* Delete was successful: Return 204 without content. */ - send_http_error(conn, 204, "%s", ""); - } else { - /* Delete not successful (file locked). */ - send_http_error(conn, 423, "Error: Cannot delete file\nremove(%s): %s", - path, strerror(ERRNO)); - } + /* Try to delete it. */ + if (mg_remove(path) == 0) { + /* Delete was successful: Return 204 without content. */ + send_http_error(conn, 204, "%s", ""); + } else { + /* Delete not successful (file locked). */ + send_http_error(conn, 423, "Error: Cannot delete file\nremove(%s): %s", + path, strerror(ERRNO)); + } } static void send_ssi_file(struct mg_connection *, const char *, struct file *, @@ -5551,304 +5557,304 @@ static void send_ssi_file(struct mg_connection *, const char *, struct file *, static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level) { - char file_name[MG_BUF_LEN], path[512], *p; - struct file file = STRUCT_FILE_INITIALIZER; - size_t len; + char file_name[MG_BUF_LEN], path[512], *p; + struct file file = STRUCT_FILE_INITIALIZER; + size_t len; - if (conn == NULL) - return; + if (conn == NULL) + return; - /* 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=\"%511[^\"]\"", file_name) == 1) { - /* File name is relative to the webserver root */ - file_name[511] = 0; - (void)mg_snprintf(conn, path, sizeof(path), "%s%c%s", - conn->ctx->config[DOCUMENT_ROOT], '/', file_name); - } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) { - /* File name is relative to the webserver working directory - or it is absolute system path */ - file_name[511] = 0; - (void)mg_snprintf(conn, path, sizeof(path), "%s", file_name); - } else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1 || - sscanf(tag, " \"%511[^\"]\"", file_name) == 1) { - /* File name is relative to the currect document */ - file_name[511] = 0; - (void)mg_snprintf(conn, path, sizeof(path), "%s", ssi); - if ((p = strrchr(path, '/')) != NULL) { - p[1] = '\0'; - } - len = strlen(path); - (void)mg_snprintf(conn, path + len, sizeof(path) - len, "%s", - file_name); - } else { - mg_cry(conn, "Bad SSI #include: [%s]", tag); - return; - } + /* 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=\"%511[^\"]\"", file_name) == 1) { + /* File name is relative to the webserver root */ + file_name[511] = 0; + (void)mg_snprintf(conn, path, sizeof(path), "%s%c%s", + conn->ctx->config[DOCUMENT_ROOT], '/', file_name); + } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) { + /* File name is relative to the webserver working directory + or it is absolute system path */ + file_name[511] = 0; + (void)mg_snprintf(conn, path, sizeof(path), "%s", file_name); + } else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1 || + sscanf(tag, " \"%511[^\"]\"", file_name) == 1) { + /* File name is relative to the currect document */ + file_name[511] = 0; + (void)mg_snprintf(conn, path, sizeof(path), "%s", ssi); + if ((p = strrchr(path, '/')) != NULL) { + p[1] = '\0'; + } + len = strlen(path); + (void)mg_snprintf(conn, path + len, sizeof(path) - len, "%s", + file_name); + } else { + mg_cry(conn, "Bad SSI #include: [%s]", tag); + return; + } - if (!mg_fopen(conn, path, "rb", &file)) { - mg_cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", tag, path, - strerror(ERRNO)); - } else { - fclose_on_exec(&file, conn); - if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], - (int)strlen(conn->ctx->config[SSI_EXTENSIONS]), - path) > 0) { - send_ssi_file(conn, path, &file, include_level + 1); - } else { - send_file_data(conn, &file, 0, INT64_MAX); - } - mg_fclose(&file); - } + if (!mg_fopen(conn, path, "rb", &file)) { + mg_cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", tag, path, + strerror(ERRNO)); + } else { + fclose_on_exec(&file, conn); + if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], + (int)strlen(conn->ctx->config[SSI_EXTENSIONS]), + path) > 0) { + send_ssi_file(conn, path, &file, include_level + 1); + } else { + send_file_data(conn, &file, 0, INT64_MAX); + } + mg_fclose(&file); + } } #if !defined(NO_POPEN) static void do_ssi_exec(struct mg_connection *conn, char *tag) { - char cmd[1024] = ""; - struct file file = STRUCT_FILE_INITIALIZER; + char cmd[1024] = ""; + struct file file = STRUCT_FILE_INITIALIZER; - if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) { - mg_cry(conn, "Bad SSI #exec: [%s]", tag); - } else { - cmd[1023] = 0; - if ((file.fp = popen(cmd, "r")) == NULL) { - mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); - } else { - send_file_data(conn, &file, 0, INT64_MAX); - pclose(file.fp); - } - } + if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) { + mg_cry(conn, "Bad SSI #exec: [%s]", tag); + } else { + cmd[1023] = 0; + if ((file.fp = popen(cmd, "r")) == NULL) { + mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); + } else { + send_file_data(conn, &file, 0, INT64_MAX); + pclose(file.fp); + } + } } #endif /* !NO_POPEN */ static int mg_fgetc(struct file *filep, int offset) { - if (filep == NULL) - return EOF; - if (filep->membuf != NULL && offset >= 0 && offset < filep->size) { - return ((unsigned char *)filep->membuf)[offset]; - } else if (filep->fp != NULL) { - return fgetc(filep->fp); - } else { - return EOF; - } + if (filep == NULL) + return EOF; + if (filep->membuf != NULL && offset >= 0 && offset < filep->size) { + return ((unsigned char *)filep->membuf)[offset]; + } else if (filep->fp != NULL) { + return fgetc(filep->fp); + } else { + return EOF; + } } static void send_ssi_file(struct mg_connection *conn, const char *path, struct file *filep, int include_level) { - char buf[MG_BUF_LEN]; - int ch, offset, len, in_ssi_tag; + char buf[MG_BUF_LEN]; + int ch, offset, len, in_ssi_tag; - if (include_level > 10) { - mg_cry(conn, "SSI #include level is too deep (%s)", path); - return; - } + if (include_level > 10) { + mg_cry(conn, "SSI #include level is too deep (%s)", path); + return; + } - in_ssi_tag = len = offset = 0; - while ((ch = mg_fgetc(filep, offset)) != EOF) { - if (in_ssi_tag && ch == '>') { - in_ssi_tag = 0; - buf[len++] = (char)ch; - buf[len] = '\0'; - /* assert(len <= (int) sizeof(buf)); */ - if (len > (int)sizeof(buf)) - break; - if (len < 6 || memcmp(buf, "