From 4c00463505692a73ff715222295e159cad922a18 Mon Sep 17 00:00:00 2001 From: Lammert Bies Date: Thu, 22 Dec 2016 01:04:08 +0100 Subject: [PATCH] Code cleanup --- src/httplib_authorize.c | 8 +- src/httplib_check_authorization.c | 33 +- src/httplib_check_password.c | 24 +- src/httplib_close_all_listening_sockets.c | 9 +- src/httplib_close_socket_gracefully.c | 54 +- src/httplib_compare_dir_entries.c | 54 +- src/httplib_connect_client.c | 2 +- src/httplib_connect_websocket_client.c | 89 ++- src/httplib_dir_scan_callback.c | 29 +- src/httplib_forward_body_data.c | 111 +-- src/httplib_get_cookie.c | 21 +- src/httplib_get_system_name.c | 2 +- src/httplib_handle_directory_request.c | 66 +- src/httplib_handle_file_based_request.c | 37 +- src/httplib_handle_form_request.c | 664 +++++++++++------- ..._handle_not_modified_static_file_request.c | 20 +- src/httplib_handle_propfind.c | 70 +- src/httplib_handle_request.c | 582 ++++++++++----- src/httplib_handle_static_file_request.c | 148 ++-- src/httplib_handle_websocket_request.c | 162 +++-- src/httplib_header_has_option.c | 17 +- src/httplib_inet_pton.c | 39 +- src/httplib_initialize_ssl.c | 30 +- src/httplib_interpret_uri.c | 172 +++-- src/httplib_is_authorized_for_put.c | 22 +- src/httplib_is_put_or_delete_method.c | 17 +- src/httplib_is_valid_port.c | 13 +- src/httplib_is_websocket_protocol.c | 18 +- src/httplib_load_dll.c | 78 +- src/httplib_lock_unlock_connection.c | 2 +- src/httplib_lock_unlock_context.c | 2 +- src/httplib_log_access.c | 66 +- src/httplib_lowercase.c | 6 +- src/httplib_main.h | 12 +- src/httplib_master_thread.c | 121 ++-- src/httplib_match_prefix.c | 54 +- src/httplib_md5.c | 30 +- src/httplib_mkcol.c | 40 +- src/httplib_modify_passwords_file.c | 135 ++-- src/httplib_must_hide_file.c | 8 +- src/httplib_next_option.c | 53 +- src/httplib_parse_auth_header.c | 122 ++-- src/httplib_parse_http_headers.c | 63 +- src/httplib_parse_http_message.c | 53 +- src/httplib_path_to_unicode.c | 86 ++- src/httplib_prepare_cgi_environment.c | 82 ++- src/httplib_print_dir_entry.c | 12 +- src/httplib_process_new_connection.c | 172 +++-- src/httplib_produce_socket.c | 49 +- src/httplib_pull.c | 79 ++- src/httplib_pull_all.c | 40 +- src/httplib_put_file.c | 163 +++-- src/httplib_read.c | 2 +- src/httplib_read_auth_file.c | 8 +- src/httplib_read_request.c | 55 +- src/httplib_redirect_to_https_port.c | 54 +- src/httplib_send_authorization_request.c | 17 +- src/httplib_send_file_data.c | 93 ++- src/httplib_send_http_error.c | 153 ++-- src/httplib_send_websocket_handshake.c | 67 +- src/httplib_set_close_on_exec.c | 22 +- src/httplib_set_handler_type.c | 158 +++-- src/httplib_set_websocket_handler.c | 6 +- src/httplib_should_decode_url.c | 9 +- src/httplib_should_keep_alive.c | 47 +- src/httplib_skip_quoted.c | 62 +- src/httplib_sockaddr_to_string.c | 16 +- src/httplib_spawn_process.c | 174 +++-- src/httplib_ssi.c | 256 ++++--- src/httplib_ssl_get_client_cert_info.c | 106 +-- src/httplib_ssl_id_callback.c | 42 +- src/httplib_ssl_use_pem_file.c | 28 +- src/httplib_start.c | 170 +++-- src/httplib_start_thread_with_id.c | 42 +- src/httplib_stat.c | 85 ++- src/main.c | 25 +- src/osx_clock_gettime.c | 47 +- src/win32_clock_gettime.c | 62 +- 78 files changed, 3696 insertions(+), 2151 deletions(-) diff --git a/src/httplib_authorize.c b/src/httplib_authorize.c index 66516763..0b7efbb3 100644 --- a/src/httplib_authorize.c +++ b/src/httplib_authorize.c @@ -28,23 +28,23 @@ #include "httplib_main.h" /* - * int XX_httplib_authorize( struct httplib_connection *conn, struct file *filep ); + * bool XX_httplib_authorize( struct httplib_connection *conn, struct file *filep ); * * The function XX_httplib_authorize() authorizes agains the open passwords * file. It returns 1 if authorized. */ -int XX_httplib_authorize( struct httplib_connection *conn, struct file *filep ) { +bool XX_httplib_authorize( struct httplib_connection *conn, struct file *filep ) { struct read_auth_file_struct workdata; char buf[MG_BUF_LEN]; - if ( conn == NULL || conn->ctx == NULL ) return 0; + if ( conn == NULL || conn->ctx == NULL ) return false; memset( & workdata, 0, sizeof(workdata) ); workdata.conn = conn; - if ( ! XX_httplib_parse_auth_header( conn, buf, sizeof(buf), &workdata.ah ) ) return 0; + if ( ! XX_httplib_parse_auth_header( conn, buf, sizeof(buf), &workdata.ah ) ) return false; workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; return XX_httplib_read_auth_file( filep, &workdata ); diff --git a/src/httplib_check_authorization.c b/src/httplib_check_authorization.c index a099aa12..eedf3df5 100644 --- a/src/httplib_check_authorization.c +++ b/src/httplib_check_authorization.c @@ -22,42 +22,49 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_string.h" /* Return 1 if request is authorised, 0 otherwise. */ -int XX_httplib_check_authorization( struct httplib_connection *conn, const char *path ) { +bool XX_httplib_check_authorization( struct httplib_connection *conn, const char *path ) { char fname[PATH_MAX]; struct vec uri_vec; struct vec filename_vec; const char *list; struct file file = STRUCT_FILE_INITIALIZER; - int authorized = 1; + bool authorized; int truncated; - if (!conn || !conn->ctx) return 0; + if ( conn == NULL || conn->ctx == NULL ) return false; + + authorized = true; list = conn->ctx->config[PROTECT_URI]; - while ((list = XX_httplib_next_option(list, &uri_vec, &filename_vec)) != NULL) { - if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) { - XX_httplib_snprintf(conn, &truncated, fname, sizeof(fname), "%.*s", (int)filename_vec.len, filename_vec.ptr); - if (truncated || !XX_httplib_fopen(conn, fname, "r", &file)) { - httplib_cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno)); + while ( (list = XX_httplib_next_option( list, &uri_vec, &filename_vec )) != NULL ) { + + if ( ! memcmp( conn->request_info.local_uri, uri_vec.ptr, uri_vec.len ) ) { + + XX_httplib_snprintf( conn, &truncated, fname, sizeof(fname), "%.*s", (int)filename_vec.len, filename_vec.ptr ); + + if ( truncated || ! XX_httplib_fopen( conn, fname, "r", &file ) ) { + + httplib_cry( conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno) ); } break; } } - if (!XX_httplib_is_file_opened(&file)) XX_httplib_open_auth_file(conn, path, &file); + if ( ! XX_httplib_is_file_opened( & file ) ) XX_httplib_open_auth_file( conn, path, & file ); - if (XX_httplib_is_file_opened(&file)) { - authorized = XX_httplib_authorize(conn, &file); - XX_httplib_fclose(&file); + if ( XX_httplib_is_file_opened( & file ) ) { + + authorized = XX_httplib_authorize( conn, & file ); + XX_httplib_fclose( & file ); } return authorized; diff --git a/src/httplib_check_password.c b/src/httplib_check_password.c index d7ec4b00..cd02426c 100644 --- a/src/httplib_check_password.c +++ b/src/httplib_check_password.c @@ -22,26 +22,32 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" /* Check the user's password, return 1 if OK */ -int XX_httplib_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 ) { +bool XX_httplib_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]; char 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 + */ - /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */ - if (strlen(response) != 32) return 0; + if ( method == NULL || nonce == NULL || nc == NULL || cnonce == NULL || qop == NULL || response == NULL ) return false; - httplib_md5(ha2, method, ":", uri, NULL); - httplib_md5(expected_response, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, ":", ha2, NULL); + /* + * NOTE(lsm): due to a bug in MSIE, we do not compare the URI + */ - return httplib_strcasecmp(response, expected_response) == 0; + if ( strlen(response) != 32 ) return false; + + httplib_md5( ha2, method, ":", uri, NULL ); + httplib_md5( expected_response, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, ":", ha2, NULL ); + + return ( httplib_strcasecmp( response, expected_response ) == 0 ); } /* XX_httplib_check_password */ diff --git a/src/httplib_close_all_listening_sockets.c b/src/httplib_close_all_listening_sockets.c index 6efcb515..bdbaac18 100644 --- a/src/httplib_close_all_listening_sockets.c +++ b/src/httplib_close_all_listening_sockets.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -41,12 +41,15 @@ void XX_httplib_close_all_listening_sockets( struct httplib_context *ctx ) { if ( ctx == NULL ) return; - for (i = 0; i < ctx->num_listening_sockets; i++) { - closesocket(ctx->listening_sockets[i].sock); + for (i=0; inum_listening_sockets; i++) { + + closesocket( ctx->listening_sockets[i].sock ); ctx->listening_sockets[i].sock = INVALID_SOCKET; } + httplib_free( ctx->listening_sockets ); httplib_free( ctx->listening_socket_fds ); + ctx->listening_sockets = NULL; ctx->listening_socket_fds = NULL; diff --git a/src/httplib_close_socket_gracefully.c b/src/httplib_close_socket_gracefully.c index 68b2b4f0..9fb03c08 100644 --- a/src/httplib_close_socket_gracefully.c +++ b/src/httplib_close_socket_gracefully.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -41,13 +41,19 @@ void XX_httplib_close_socket_gracefully( struct httplib_connection *conn ) { int n; #endif struct linger linger; - int error_code = 0; - socklen_t opt_len = sizeof(error_code); + int error_code; + socklen_t opt_len; if ( conn == NULL ) return; - /* Set linger option to avoid socket hanging out after close. This - * prevent ephemeral port exhaust problem under high QPS. */ + error_code = 0; + opt_len = sizeof(error_code); + + /* + * Set linger option to avoid socket hanging out after close. This + * prevent ephemeral port exhaust problem under high QPS. + */ + linger.l_onoff = 1; linger.l_linger = 1; @@ -55,30 +61,42 @@ void XX_httplib_close_socket_gracefully( struct httplib_connection *conn ) { if (error_code == ECONNRESET) { /* Socket already closed by client/peer, close socket without linger */ - } else { - if (setsockopt(conn->client.sock, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger)) != 0) { - httplib_cry(conn, "%s: setsockopt(SOL_SOCKET SO_LINGER) failed: %s", __func__, strerror(ERRNO)); + } + + else { + if ( setsockopt( conn->client.sock, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger) ) != 0 ) { + + httplib_cry( conn, "%s: setsockopt(SOL_SOCKET SO_LINGER) failed: %s", __func__, strerror(ERRNO) ); } } - /* Send FIN to the client */ - shutdown(conn->client.sock, SHUTDOWN_WR); - XX_httplib_set_non_blocking_mode(conn->client.sock); + /* + * Send FIN to the client + */ + + shutdown( conn->client.sock, SHUTDOWN_WR ); + XX_httplib_set_non_blocking_mode( conn->client.sock ); #if defined(_WIN32) - /* Read and discard pending incoming data. If we do not do that and - * close + /* + * Read and discard pending incoming data. If we do not do that and close * the socket, the data in the send buffer may be discarded. This * behaviour is seen on Windows, when client keeps sending data * when server decides to close the connection; then when client - * does recv() it gets no data back. */ + * does recv() it gets no data back. + */ + do { - n = XX_httplib_pull( NULL, conn, buf, sizeof(buf), 1E-10 /* TODO: allow 0 as timeout */); - } while (n > 0); + n = XX_httplib_pull( NULL, conn, buf, sizeof(buf), 1E-10 /* TODO: allow 0 as timeout */ ); + } while ( n > 0 ); #endif - /* Now we know that our FIN is ACK-ed, safe to close */ - closesocket(conn->client.sock); + /* + * Now we know that our FIN is ACK-ed, safe to close + */ + + closesocket( conn->client.sock ); + conn->client.sock = INVALID_SOCKET; } /* XX_httplib_close_socket_gracefully */ diff --git a/src/httplib_compare_dir_entries.c b/src/httplib_compare_dir_entries.c index 3c5f51f3..ef2d6395 100644 --- a/src/httplib_compare_dir_entries.c +++ b/src/httplib_compare_dir_entries.c @@ -22,34 +22,64 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* This function is called from send_directory() and used for +/* + * This function is called from send_directory() and used for * sorting directory entries by size, or name, or modification time. * On windows, __cdecl specification is needed in case if project is built - * with __stdcall convention. qsort always requires __cdels callback. */ + * with __stdcall convention. qsort always requires __cdels callback. + */ + int WINCDECL XX_httplib_compare_dir_entries( const void *p1, const void *p2 ) { + int cmp_result; + const struct de *a; + const struct de *b; + const char *query_string; + bool do_desc; + bool do_name; + bool do_size; + bool do_date; + if ( p1 == NULL || p2 == NULL ) return 0; - int cmp_result; - const struct de *a = (const struct de *)p1; - const struct de *b = (const struct de *)p2; - const char *query_string = a->conn->request_info.query_string; + do_desc = false; + do_name = true; + do_size = false; + do_date = false; - if ( query_string == NULL ) query_string = "na"; + a = p1; + b = p2; + query_string = a->conn->request_info.query_string; + + if ( query_string != NULL ) { + + switch ( query_string[0] ) { + + case 'n' : do_name = true; do_size = false; do_date = false; break; + case 's' : do_name = false; do_size = true; do_date = false; break; + case 'd' : do_name = false; do_size = false; do_date = true; break; + } + + switch ( query_string[1] ) { + + case 'a' : do_desc = false; break; + case 'd' : do_desc = true; break; + } + } if ( a->file.is_directory && ! b->file.is_directory ) return -1; if ( ! a->file.is_directory && b->file.is_directory ) return 1; cmp_result = 0; - if (query_string[0] == 'n') cmp_result = strcmp( a->file_name, b->file_name ); - else if (query_string[0] == 's') cmp_result = (a->file.size == b->file.size) ? 0 : ((a->file.size > b->file.size) ? 1 : -1); - else if (query_string[0] == 'd') cmp_result = (a->file.last_modified == b->file.last_modified) ? 0 : ((a->file.last_modified > b->file.last_modified) ? 1 : -1); + if ( do_name ) cmp_result = strcmp( a->file_name, b->file_name ); + else if ( do_size ) cmp_result = (a->file.size == b->file.size) ? 0 : ((a->file.size > b->file.size) ? 1 : -1); + else if ( do_date ) cmp_result = (a->file.last_modified == b->file.last_modified) ? 0 : ((a->file.last_modified > b->file.last_modified) ? 1 : -1); - return (query_string[1] == 'd') ? -cmp_result : cmp_result; + return ( do_desc ) ? -cmp_result : cmp_result; } /* XX_httplib_compare_dir_entries */ diff --git a/src/httplib_connect_client.c b/src/httplib_connect_client.c index 4afaa27f..4cd65d05 100644 --- a/src/httplib_connect_client.c +++ b/src/httplib_connect_client.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 1.9 */ #include "httplib_main.h" diff --git a/src/httplib_connect_websocket_client.c b/src/httplib_connect_websocket_client.c index ec63b908..2a20c5cf 100644 --- a/src/httplib_connect_websocket_client.c +++ b/src/httplib_connect_websocket_client.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -46,8 +46,8 @@ struct httplib_connection *httplib_connect_websocket_client(const char *host, const char *origin, httplib_websocket_data_handler data_func, httplib_websocket_close_handler close_func, - void *user_data) -{ + void *user_data ) { + struct httplib_connection *conn = NULL; #if defined(USE_WEBSOCKET) @@ -56,7 +56,7 @@ struct httplib_connection *httplib_connect_websocket_client(const char *host, static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw=="; static const char *handshake_req; - if (origin != NULL) { + if ( origin != NULL ) { handshake_req = "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Upgrade: websocket\r\n" @@ -65,7 +65,9 @@ struct httplib_connection *httplib_connect_websocket_client(const char *host, "Sec-WebSocket-Version: 13\r\n" "Origin: %s\r\n" "\r\n"; - } else { + } + + else { handshake_req = "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Upgrade: websocket\r\n" @@ -75,43 +77,61 @@ struct httplib_connection *httplib_connect_websocket_client(const char *host, "\r\n"; } - /* Establish the client connection and request upgrade */ + /* + * Establish the client connection and request upgrade + */ + conn = httplib_download(host, port, use_ssl, error_buffer, error_buffer_size, handshake_req, path, host, magic, origin); - /* Connection object will be null if something goes wrong */ - if (conn == NULL || (strcmp(conn->request_info.request_uri, "101") != 0)) { - if (!*error_buffer) { - /* if there is a connection, but it did not return 101, - * error_buffer is not yet set */ - XX_httplib_snprintf(conn, NULL, error_buffer, error_buffer_size, "Unexpected server reply"); + /* + * Connection object will be null if something goes wrong + */ + + if ( conn == NULL || strcmp(conn->request_info.request_uri, "101") ) { + + if ( ! *error_buffer ) { + + /* + * if there is a connection, but it did not return 101, + * error_buffer is not yet set + */ + + XX_httplib_snprintf( conn, NULL, error_buffer, error_buffer_size, "Unexpected server reply" ); } - if (conn != NULL) { + if ( conn != NULL ) { + httplib_free( conn ); conn = NULL; } return conn; } - /* For client connections, httplib_context is fake. Since we need to set a - * callback function, we need to create a copy and modify it. */ - newctx = httplib_malloc(sizeof(struct httplib_context)); - memcpy(newctx, conn->ctx, sizeof(struct httplib_context)); + /* + * For client connections, httplib_context is fake. Since we need to set a + * callback function, we need to create a copy and modify it. + */ + + newctx = httplib_malloc( sizeof(struct httplib_context) ); + memcpy( newctx, conn->ctx, sizeof(struct httplib_context) ); newctx->user_data = user_data; - newctx->context_type = 2; /* client context type */ - newctx->cfg_worker_threads = 1; /* one worker thread will be created */ - newctx->workerthreadids = httplib_calloc(newctx->cfg_worker_threads, sizeof(pthread_t)); + newctx->context_type = 2; /* client context type */ + newctx->cfg_worker_threads = 1; /* one worker thread will be created */ + newctx->workerthreadids = httplib_calloc( newctx->cfg_worker_threads, sizeof(pthread_t) ); conn->ctx = newctx; - thread_data = httplib_calloc(sizeof(struct websocket_client_thread_data), 1); + thread_data = httplib_calloc( sizeof(struct websocket_client_thread_data), 1 ); thread_data->conn = conn; thread_data->data_handler = data_func; thread_data->close_handler = close_func; thread_data->callback_data = NULL; - /* Start a thread to read the websocket client connection + /* + * Start a thread to read the websocket client connection * This thread will automatically stop when httplib_disconnect is - * called on the client connection */ - if (XX_httplib_start_thread_with_id( XX_httplib_websocket_client_thread, (void *)thread_data, newctx->workerthreadids) != 0) { + * called on the client connection + */ + + if ( XX_httplib_start_thread_with_id( XX_httplib_websocket_client_thread, (void *)thread_data, newctx->workerthreadids) != 0 ) { httplib_free( thread_data ); httplib_free( newctx->workerthreadids ); @@ -121,17 +141,16 @@ struct httplib_connection *httplib_connect_websocket_client(const char *host, conn = NULL; } #else - /* Appease "unused parameter" warnings */ - (void)host; - (void)port; - (void)use_ssl; - (void)error_buffer; - (void)error_buffer_size; - (void)path; - (void)origin; - (void)user_data; - (void)data_func; - (void)close_func; + UNUSED_PARAMETER(host); + UNUSED_PARAMETER(port); + UNUSED_PARAMETER(use_ssl); + UNUSED_PARAMETER(error_buffer); + UNUSED_PARAMETER(error_buffer_size); + UNUSED_PARAMETER(path); + UNUSED_PARAMETER(origin); + UNUSED_PARAMETER(user_data); + UNUSED_PARAMETER(data_func); + UNUSED_PARAMETER(close_func); #endif return conn; diff --git a/src/httplib_dir_scan_callback.c b/src/httplib_dir_scan_callback.c index f3a9441c..5f4f94b4 100644 --- a/src/httplib_dir_scan_callback.c +++ b/src/httplib_dir_scan_callback.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -31,19 +31,28 @@ void XX_httplib_dir_scan_callback( struct de *de, void *data ) { - struct dir_scan_data *dsd = (struct dir_scan_data *)data; + struct dir_scan_data *dsd; + + dsd = data; + + if ( dsd->entries == NULL || dsd->num_entries >= dsd->arr_size ) { - if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) { dsd->arr_size *= 2; - dsd->entries = XX_httplib_realloc2(dsd->entries, dsd->arr_size * sizeof(dsd->entries[0])); + dsd->entries = XX_httplib_realloc2( dsd->entries, dsd->arr_size * sizeof(dsd->entries[0]) ); } - if (dsd->entries == NULL) { - /* TODO(lsm, low): propagate an error to the caller */ + if ( dsd->entries == NULL ) { + + /* + * TODO(lsm, low): propagate an error to the caller + */ + dsd->num_entries = 0; - } else { - dsd->entries[dsd->num_entries].file_name = XX_httplib_strdup(de->file_name); - dsd->entries[dsd->num_entries].file = de->file; - dsd->entries[dsd->num_entries].conn = de->conn; + } + + else { + dsd->entries[dsd->num_entries].file_name = XX_httplib_strdup( de->file_name ); + dsd->entries[dsd->num_entries].file = de->file; + dsd->entries[dsd->num_entries].conn = de->conn; dsd->num_entries++; } diff --git a/src/httplib_forward_body_data.c b/src/httplib_forward_body_data.c index 32759067..53cd9f32 100644 --- a/src/httplib_forward_body_data.c +++ b/src/httplib_forward_body_data.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -42,74 +42,97 @@ int XX_httplib_forward_body_data( struct httplib_connection *conn, FILE *fp, SOC char buf[MG_BUF_LEN]; int to_read; int nread; - int success = 0; + int success; int64_t buffered_len; - double timeout = -1.0; + double timeout; - if ( conn == NULL ) return 0; - if (conn->ctx->config[REQUEST_TIMEOUT]) { - timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; - } + if ( conn == NULL || conn->ctx == NULL ) return 0; + + success = 0; + timeout = -1.0; + if ( conn->ctx->config[REQUEST_TIMEOUT] != NULL ) timeout = atof( conn->ctx->config[REQUEST_TIMEOUT] ) / 1000.0; expect = httplib_get_header(conn, "Expect"); - /* assert(fp != NULL); */ - if (!fp) { - XX_httplib_send_http_error(conn, 500, "%s", "Error: NULL File"); + + if ( fp == NULL ) { + + XX_httplib_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. */ - XX_httplib_send_http_error(conn, 411, "%s", "Error: Client did not specify content length"); - } else if ((expect != NULL) - && (httplib_strcasecmp(expect, "100-continue") != 0)) { - /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */ - XX_httplib_send_http_error(conn, 417, "Error: Can not fulfill expectation %s", expect); - } else { - if (expect != NULL) { - (void)httplib_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); + if ( conn->content_len == -1 && ! conn->is_chunked ) { + + /* + * Content length is not specified by the client. + */ + + XX_httplib_send_http_error( conn, 411, "%s", "Error: Client did not specify content length" ); + + } + + else if ( expect != NULL && httplib_strcasecmp( expect, "100-continue" ) != 0 ) { + + /* + * Client sent an "Expect: xyz" header and xyz is not 100-continue. + */ + + XX_httplib_send_http_error( conn, 417, "Error: Can not fulfill expectation %s", expect ); + + } + + else { + if ( expect != NULL ) { + + httplib_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); conn->status_code = 100; - } else conn->status_code = 200; + } + + else conn->status_code = 200; - buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len - - conn->consumed_content; + 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 ) { - if ((buffered_len < 0) || (conn->consumed_content != 0)) { - XX_httplib_send_http_error(conn, 500, "%s", "Error: Size mismatch"); + XX_httplib_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; - } + 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; - XX_httplib_push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len); + XX_httplib_push_all( conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len ); conn->consumed_content += buffered_len; } nread = 0; - while (conn->consumed_content < conn->content_len) { + + 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 = XX_httplib_pull(NULL, conn, buf, to_read, timeout); - if (nread <= 0 || XX_httplib_push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) break; + if ( (int64_t)to_read > conn->content_len - conn->consumed_content ) to_read = (int)(conn->content_len - conn->consumed_content); + + nread = XX_httplib_pull( NULL, conn, buf, to_read, timeout ); + if ( nread <= 0 || XX_httplib_push_all( conn->ctx, 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) { - /* NOTE: Maybe some data has already been sent. */ + /* + * Each error code path in this function must send an error + */ + + if ( ! success ) { + + /* + * NOTE: Maybe some data has already been sent. */ /* TODO (low): If some data has been sent, a correct error - * reply can no longer be sent, so just close the connection */ - XX_httplib_send_http_error(conn, 500, "%s", ""); + * reply can no longer be sent, so just close the connection + */ + + XX_httplib_send_http_error( conn, 500, "%s", "" ); } } diff --git a/src/httplib_get_cookie.c b/src/httplib_get_cookie.c index d0e8bb9e..58e9172d 100644 --- a/src/httplib_get_cookie.c +++ b/src/httplib_get_cookie.c @@ -22,13 +22,16 @@ * THE SOFTWARE. * * ============ - * Release: 1.9 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_string.h" -/* HCP24: some changes to compare hole var_name */ +/* + * HCP24: some changes to compare hole var_name + */ + int httplib_get_cookie( const char *cookie_header, const char *var_name, char *dst, size_t dst_size ) { const char *s; @@ -48,14 +51,16 @@ int httplib_get_cookie( const char *cookie_header, const char *var_name, char *d name_len = (int)strlen( var_name ); end = s + strlen( s ); - for (; (s = httplib_strcasestr( s, var_name )) != NULL; s += name_len) { + while ( (s = httplib_strcasestr( s, var_name )) != NULL ) { + if (s[name_len] == '=') { /* * HCP24: now check is it a substring or a full cookie name */ - if ((s == cookie_header) || (s[-1] == ' ')) { + if ( s == cookie_header || s[-1] == ' ' ) { + s += name_len + 1; if ( (p = strchr(s, ' ')) == NULL ) p = end; if ( p[-1] == ';' ) p--; @@ -63,14 +68,18 @@ int httplib_get_cookie( const char *cookie_header, const char *var_name, char *d s++; p--; } - if ((size_t)(p - s) < dst_size) { + if ( (size_t)(p - s) < dst_size ) { len = (int)(p - s); httplib_strlcpy( dst, s, (size_t)len+1 ); - } else len = -3; + } + else len = -3; + break; } } + + s += name_len; } return len; diff --git a/src/httplib_get_system_name.c b/src/httplib_get_system_name.c index d9bb1770..0b1df02e 100644 --- a/src/httplib_get_system_name.c +++ b/src/httplib_get_system_name.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.9 + * Release: 2.0 */ #include "httplib_main.h" diff --git a/src/httplib_handle_directory_request.c b/src/httplib_handle_directory_request.c index 84d2ba76..1d559dff 100644 --- a/src/httplib_handle_directory_request.c +++ b/src/httplib_handle_directory_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -33,28 +33,30 @@ void XX_httplib_handle_directory_request( struct httplib_connection *conn, const unsigned int i; int sort_direction; - struct dir_scan_data data = {NULL, 0, 128}; + struct dir_scan_data data = { NULL, 0, 128 }; char date[64]; - time_t curtime = time(NULL); + time_t curtime; - if (!XX_httplib_scan_directory(conn, dir, &data, XX_httplib_dir_scan_callback)) { - XX_httplib_send_http_error(conn, 500, "Error: Cannot open directory\nopendir(%s): %s", dir, strerror(ERRNO)); + if ( conn == NULL ) return; + if ( dir == NULL ) { XX_httplib_send_http_error( conn, 500, "Internal server error\nOpening NULL directory" ); return; } + + if ( ! XX_httplib_scan_directory( conn, dir, & data, XX_httplib_dir_scan_callback ) ) { + + XX_httplib_send_http_error( conn, 500, "Error: Cannot open directory\nopendir(%s): %s", dir, strerror(ERRNO) ); return; } - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); + curtime = time( NULL ); + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); - 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; - httplib_printf(conn, "HTTP/1.1 200 OK\r\n"); - XX_httplib_send_static_cache_header(conn); - httplib_printf(conn, "Date: %s\r\n" "Connection: close\r\n" "Content-Type: text/html; charset=utf-8\r\n\r\n", date); + httplib_printf( conn, "HTTP/1.1 200 OK\r\n" ); + XX_httplib_send_static_cache_header( conn ); + httplib_printf( conn, "Date: %s\r\n" "Connection: close\r\n" "Content-Type: text/html; charset=utf-8\r\n\r\n", date ); - conn->num_bytes_sent += - httplib_printf(conn, + conn->num_bytes_sent += httplib_printf( conn, "Index of %s" "" "

Index of %s

"
@@ -66,33 +68,39 @@ void XX_httplib_handle_directory_request( struct httplib_connection *conn, const
 	              conn->request_info.local_uri,
 	              sort_direction,
 	              sort_direction,
-	              sort_direction);
+	              sort_direction );
 
-	/* Print first entry - link to a parent directory */
-	conn->num_bytes_sent +=
-	    httplib_printf(conn,
+	/*
+	 * Print first entry - link to a parent directory
+	 */
+
+	conn->num_bytes_sent += httplib_printf(conn,
 	              ""
 	              "\n",
 	              conn->request_info.local_uri,
 	              "..",
 	              "Parent directory",
 	              "-",
-	              "-");
+	              "-" );
 
-	/* Sort and print directory entries */
-	if (data.entries != NULL) {
-		qsort(data.entries,
-		      (size_t)data.num_entries,
-		      sizeof(data.entries[0]),
-		      XX_httplib_compare_dir_entries);
-		for (i = 0; i < data.num_entries; i++) {
-			XX_httplib_print_dir_entry(&data.entries[i]);
+	/*
+	 * Sort and print directory entries
+	 */
+
+	if ( data.entries != NULL ) {
+
+		qsort( data.entries, (size_t)data.num_entries, sizeof(data.entries[0]), XX_httplib_compare_dir_entries );
+
+		for (i=0; inum_bytes_sent += httplib_printf(conn, "%s", "
%s %s  %s
"); - conn->status_code = 200; + conn->num_bytes_sent += httplib_printf( conn, "%s", "" ); + conn->status_code = 200; } /* XX_httplib_handle_directory_request */ diff --git a/src/httplib_handle_file_based_request.c b/src/httplib_handle_file_based_request.c index e1593766..97483189 100644 --- a/src/httplib_handle_file_based_request.c +++ b/src/httplib_handle_file_based_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -41,17 +41,34 @@ void XX_httplib_handle_file_based_request( struct httplib_connection *conn, cons if (0) { #if !defined(NO_CGI) - } else if (XX_httplib_match_prefix(conn->ctx->config[CGI_EXTENSIONS], strlen(conn->ctx->config[CGI_EXTENSIONS]), path) > 0) { - /* CGI scripts may support all HTTP methods */ - XX_httplib_handle_cgi_request(conn, path); + } + + else if ( XX_httplib_match_prefix( conn->ctx->config[CGI_EXTENSIONS], strlen(conn->ctx->config[CGI_EXTENSIONS]), path) > 0 ) { + + /* + * CGI scripts may support all HTTP methods + */ + + XX_httplib_handle_cgi_request( conn, path ); #endif /* !NO_CGI */ - } else if (XX_httplib_match_prefix(conn->ctx->config[SSI_EXTENSIONS], strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) { - XX_httplib_handle_ssi_file_request(conn, path, file); + } + + else if ( XX_httplib_match_prefix( conn->ctx->config[SSI_EXTENSIONS], strlen( conn->ctx->config[SSI_EXTENSIONS] ), path ) > 0 ) { + + XX_httplib_handle_ssi_file_request( conn, path, file ); #if !defined(NO_CACHING) - } else if ((!conn->in_error_handler) && XX_httplib_is_not_modified(conn, file)) { - /* Send 304 "Not Modified" - this must not send any body data */ - XX_httplib_handle_not_modified_static_file_request(conn, file); + } + + else if ( ! conn->in_error_handler && XX_httplib_is_not_modified( conn, file ) ) { + + /* + * Send 304 "Not Modified" - this must not send any body data + */ + + XX_httplib_handle_not_modified_static_file_request( conn, file ); #endif /* !NO_CACHING */ - } else XX_httplib_handle_static_file_request(conn, path, file, NULL, NULL); + } + + else XX_httplib_handle_static_file_request( conn, path, file, NULL, NULL ); } /* XX_httplib_handle_file_based_request */ diff --git a/src/httplib_handle_form_request.c b/src/httplib_handle_form_request.c index e56f5556..89760ce3 100644 --- a/src/httplib_handle_form_request.c +++ b/src/httplib_handle_form_request.c @@ -21,7 +21,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -46,33 +46,37 @@ static int url_encoded_field_found(const struct httplib_connection *conn, if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) return FORM_FIELD_STORAGE_SKIP; - if (filename) { - filename_dec_len = httplib_url_decode(filename, (int)filename_len, filename_dec, (int)sizeof(filename_dec), 1); + if ( filename != NULL ) { - if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec)) - || (filename_dec_len < 0)) { - /* Log error message and skip this field. */ - httplib_cry(conn, "%s: Cannot decode filename", __func__); + filename_dec_len = httplib_url_decode( filename, (int)filename_len, filename_dec, (int)sizeof(filename_dec), 1 ); + + if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec)) || (filename_dec_len < 0)) { + + /* + * Log error message and skip this field. + */ + + httplib_cry( conn, "%s: Cannot decode filename", __func__ ); return FORM_FIELD_STORAGE_SKIP; } } else filename_dec[0] = 0; - ret = fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data); + ret = fdh->field_found( key_dec, filename_dec, path, path_len, fdh->user_data ); - if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) { + if ( (ret & 0xF) == FORM_FIELD_STORAGE_GET ) { - if (fdh->field_get == NULL) { + if ( fdh->field_get == NULL ) { - httplib_cry(conn, "%s: Function \"Get\" not available", __func__); + httplib_cry( conn, "%s: Function \"Get\" not available", __func__ ); return FORM_FIELD_STORAGE_SKIP; } } - if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) { + if ( (ret & 0xF) == FORM_FIELD_STORAGE_STORE ) { - if (fdh->field_store == NULL) { + if ( fdh->field_store == NULL ) { - httplib_cry(conn, "%s: Function \"Store\" not available", __func__); + httplib_cry( conn, "%s: Function \"Store\" not available", __func__ ); return FORM_FIELD_STORAGE_SKIP; } } @@ -104,15 +108,16 @@ static int url_encoded_field_get(const struct httplib_connection *conn, return FORM_FIELD_STORAGE_ABORT; } - httplib_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); + httplib_url_decode( key, (int)key_len, key_dec, (int)sizeof(key_dec), 1 ); - value_dec_len = httplib_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1); + value_dec_len = httplib_url_decode( value, (int)value_len, value_dec, (int)value_len + 1, 1 ); - ret = fdh->field_get(key_dec, value_dec, (size_t)value_dec_len, fdh->user_data); + ret = fdh->field_get( key_dec, value_dec, (size_t)value_dec_len, fdh->user_data ); httplib_free( value_dec ); return ret; -} + +} /* url_encoded_field_get */ static int unencoded_field_get(const struct httplib_connection *conn, @@ -123,36 +128,41 @@ static int unencoded_field_get(const struct httplib_connection *conn, struct httplib_form_data_handler *fdh) { char key_dec[1024]; - (void)conn; - httplib_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); + UNUSED_PARAMETER(conn); - return fdh->field_get(key_dec, value, value_len, fdh->user_data); -} + httplib_url_decode( key, (int)key_len, key_dec, (int)sizeof(key_dec), 1 ); + + return fdh->field_get( key_dec, value, value_len, fdh->user_data ); + +} /* unencoded_field_get */ -static int field_stored(const struct httplib_connection *conn, const char *path, int64_t file_size, struct httplib_form_data_handler *fdh) { +static int field_stored( const struct httplib_connection *conn, const char *path, int64_t file_size, struct httplib_form_data_handler *fdh ) { - /* Equivalent to "upload" callback of "httplib_upload". */ + /* + * Equivalent to "upload" callback of "httplib_upload". + */ - (void)conn; /* we do not need httplib_cry here, so conn is currently unused */ + UNUSED_PARAMETER(conn); /* we do not need httplib_cry here, so conn is currently unused */ - return fdh->field_store(path, file_size, fdh->user_data); + return fdh->field_store( path, file_size, fdh->user_data ); } static const char * search_boundary(const char *buf, size_t buf_len, const char *boundary, size_t boundary_len) { - /* We must do a binary search here, not a string search, since the buffer - * may contain '\x00' bytes, if binary data is transferred. */ + /* + * We must do a binary search here, not a string search, since the buffer + * may contain '\x00' bytes, if binary data is transferred. + */ + int clen = (int)buf_len - (int)boundary_len - 4; int i; for (i = 0; i <= clen; i++) { if (!memcmp(buf + i, "\r\n--", 4)) { - if (!memcmp(buf + i + 4, boundary, boundary_len)) { - return buf + i; - } + if (!memcmp(buf + i + 4, boundary, boundary_len)) return buf+i; } } return NULL; @@ -186,34 +196,40 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ * This is the typical way to handle file upload from a form. */ - if (!has_body_data) { + if ( ! has_body_data ) { + const char *data; - if (strcmp(conn->request_info.request_method, "GET")) { - /* No body data, but not a GET request. - * This is not a valid form request. */ + if ( strcmp( conn->request_info.request_method, "GET" ) ) { + /* + * No body data, but not a GET request. + * This is not a valid form request. + */ + return -1; } - /* GET request: form data is in the query string. */ - /* The entire data has already been loaded, so there is no nead to + /* + * GET request: form data is in the query string. + * The entire data has already been loaded, so there is no nead to * call httplib_read. We just need to split the query string into key-value - * pairs. */ - data = conn->request_info.query_string; - if ( data == NULL ) { - /* No query string. */ - return -1; - } + * pairs. + */ + + data = conn->request_info.query_string; + if ( data == NULL ) return -1; /* No query string. */ + + /* + * Split data in a=1&b=xy&c=3&c=4 ... + */ + + while ( *data ) { - /* Split data in a=1&b=xy&c=3&c=4 ... */ - while (*data) { const char *val = strchr(data, '='); const char *next; ptrdiff_t keylen, vallen; - if (!val) { - break; - } + if ( val == NULL ) break; keylen = val - data; /* @@ -229,55 +245,78 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing */ - memset(path, 0, sizeof(path)); + memset( path, 0, sizeof(path) ); field_count++; - field_storage = url_encoded_field_found(conn, data, (size_t)keylen, NULL, 0, path, sizeof(path) - 1, fdh); + field_storage = url_encoded_field_found( conn, data, (size_t)keylen, NULL, 0, path, sizeof(path) - 1, fdh ); val++; - next = strchr(val, '&'); - if (next) { + next = strchr( val, '&' ); + if ( next != NULL ) { + vallen = next - val; next++; - } else { + } + + else { vallen = (ptrdiff_t)strlen(val); next = val + vallen; } - if (field_storage == FORM_FIELD_STORAGE_GET) { - /* Call callback */ - url_encoded_field_get( conn, data, (size_t)keylen, val, (size_t)vallen, fdh); + if ( field_storage == FORM_FIELD_STORAGE_GET ) { + + /* + * Call callback + */ + + url_encoded_field_get( conn, data, (size_t)keylen, val, (size_t)vallen, fdh ); } - if (field_storage == FORM_FIELD_STORAGE_STORE) { - /* Store the content to a file */ - if (XX_httplib_fopen(conn, path, "wb", &fstore) == 0) fstore.fp = NULL; + + if ( field_storage == FORM_FIELD_STORAGE_STORE ) { + + /* + * Store the content to a file + */ + + if ( XX_httplib_fopen( conn, path, "wb", &fstore ) == 0 ) fstore.fp = NULL; file_size = 0; - if (fstore.fp != NULL) { + + if ( fstore.fp != NULL ) { + size_t n = (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp); if ((n != (size_t)vallen) || (ferror(fstore.fp))) { - httplib_cry(conn, "%s: Cannot write file %s", __func__, path); - fclose(fstore.fp); + httplib_cry( conn, "%s: Cannot write file %s", __func__, path ); + fclose( fstore.fp ); fstore.fp = NULL; - XX_httplib_remove_bad_file(conn, path); + XX_httplib_remove_bad_file( conn, path ); } file_size += (int64_t)n; - if (fstore.fp) { - r = fclose(fstore.fp); + if ( fstore.fp ) { + + r = fclose( fstore.fp ); + if (r == 0) { - /* stored successfully */ - field_stored(conn, path, file_size, fdh); - } else { - httplib_cry(conn, "%s: Error saving file %s", __func__, path); - XX_httplib_remove_bad_file(conn, path); + /* + * stored successfully + */ + + field_stored( conn, path, file_size, fdh ); } + + else { + httplib_cry( conn, "%s: Error saving file %s", __func__, path ); + XX_httplib_remove_bad_file( conn, path ); + } + fstore.fp = NULL; } - } else httplib_cry(conn, "%s: Cannot create file %s", __func__, path); + } else httplib_cry( conn, "%s: Cannot create file %s", __func__, path ); } - /* if (field_storage == FORM_FIELD_STORAGE_READ) { */ - /* The idea of "field_storage=read" is to let the API user read + /* + * if (field_storage == FORM_FIELD_STORAGE_READ) { + * The idea of "field_storage=read" is to let the API user read * data chunk by chunk and to some data processing on the fly. * This should avoid the need to store data in the server: * It should neither be stored in memory, like @@ -286,36 +325,44 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ * However, for a "GET" request this does not make any much * sense, since the data is already stored in memory, as it is * part of the query string. + * + * } */ + + if ((field_storage & FORM_FIELD_STORAGE_ABORT) == FORM_FIELD_STORAGE_ABORT) break; /* Stop parsing the request */ + + /* + * Proceed to next entry */ - /* } */ - if ((field_storage & FORM_FIELD_STORAGE_ABORT) == FORM_FIELD_STORAGE_ABORT) { - /* Stop parsing the request */ - break; - } - - /* Proceed to next entry */ data = next; } return field_count; } - content_type = httplib_get_header(conn, "Content-Type"); + content_type = httplib_get_header( conn, "Content-Type" ); + + if ( content_type == NULL + || ! httplib_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED") + || ! httplib_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) { + + /* + * The form data is in the request body data, encoded in key/value + * pairs. + */ - if (!content_type - || !httplib_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED") - || !httplib_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) { - /* The form data is in the request body data, encoded in key/value - * pairs. */ int all_data_read = 0; - /* Read body data and split it in keys and values. + /* + * Read body data and split it in keys and values. * The encoding is like in the "GET" case above: a=1&b&c=3&c=4. * Here we use "POST", and read the data from the request body. * The data read on the fly, so it is not required to buffer the - * entire request in memory before processing it. */ + * entire request in memory before processing it. + */ + for (;;) { + const char *val; const char *next; ptrdiff_t keylen, vallen; @@ -326,43 +373,42 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ if ((size_t)buf_fill < (sizeof(buf) - 1)) { size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; - r = httplib_read(conn, buf + (size_t)buf_fill, to_read); - if (r < 0) { - /* read error */ - return -1; - } - if (r != (int)to_read) { - /* TODO: Create a function to get "all_data_read" from + r = httplib_read( conn, buf + (size_t)buf_fill, to_read ); + if ( r < 0 ) return -1; /* read error */ + + if ( r != (int)to_read ) { + /* + * TODO: Create a function to get "all_data_read" from * the conn object. All data is read if the Content-Length * has been reached, or if chunked encoding is used and * the end marker has been read, or if the connection has - * been closed. */ + * been closed. + */ + all_data_read = 1; } buf_fill += r; buf[buf_fill] = 0; - if (buf_fill < 1) break; + if ( buf_fill < 1 ) break; } val = strchr(buf, '='); - if (!val) { - break; - } + if ( val == NULL ) break; keylen = val - buf; val++; - /* Call callback */ - memset(path, 0, sizeof(path)); + /* + * Call callback + */ + + memset( path, 0, sizeof(path) ); field_count++; - field_storage = url_encoded_field_found(conn, buf, (size_t)keylen, NULL, 0, path, sizeof(path) - 1, fdh); + field_storage = url_encoded_field_found( conn, buf, (size_t)keylen, NULL, 0, path, sizeof(path) - 1, fdh ); - if ((field_storage & FORM_FIELD_STORAGE_ABORT) == FORM_FIELD_STORAGE_ABORT) { - /* Stop parsing the request */ - break; - } + if ( (field_storage & FORM_FIELD_STORAGE_ABORT) == FORM_FIELD_STORAGE_ABORT ) break; /* Stop parsing the request */ - if (field_storage == FORM_FIELD_STORAGE_STORE) { + if ( field_storage == FORM_FIELD_STORAGE_STORE ) { if (XX_httplib_fopen(conn, path, "wb", &fstore) == 0) fstore.fp = NULL; file_size = 0; @@ -370,19 +416,27 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ } get_block = 0; - /* Loop to read values larger than sizeof(buf)-keylen-2 */ + + /* + * Loop to read values larger than sizeof(buf)-keylen-2 + */ + do { next = strchr(val, '&'); - if (next) { + + if (next != NULL ) { + vallen = next - val; next++; end_of_key_value_pair_found = 1; - } else { + } + + else { vallen = (ptrdiff_t)strlen(val); next = val + vallen; } - if (field_storage == FORM_FIELD_STORAGE_GET) { + if ( field_storage == FORM_FIELD_STORAGE_GET ) { #if 0 if (!end_of_key_value_pair_found && !all_data_read) { /* This callback will deliver partial contents */ @@ -400,7 +454,7 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ fdh); get_block++; } - if (fstore.fp) { + if ( fstore.fp ) { size_t n = (size_t)fwrite(val, 1, (size_t)vallen, fstore.fp); if ((n != (size_t)vallen) || (ferror(fstore.fp))) { httplib_cry(conn, "%s: Cannot write file %s", __func__, path); @@ -411,24 +465,27 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ file_size += (int64_t)n; } - if (!end_of_key_value_pair_found) { + if ( ! end_of_key_value_pair_found ) { + used = next - buf; - memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); + memmove( buf, buf + (size_t)used, sizeof(buf) - (size_t)used ); buf_fill -= (int)used; if ((size_t)buf_fill < (sizeof(buf) - 1)) { size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; r = httplib_read(conn, buf + (size_t)buf_fill, to_read); - if (r < 0) { - /* read error */ - return -1; - } + if ( r < 0 ) return -1; /* read error */ + if (r != (int)to_read) { - /* TODO: Create a function to get "all_data_read" + + /* + * TODO: Create a function to get "all_data_read" * from the conn object. All data is read if the * Content-Length has been reached, or if chunked * encoding is used and the end marker has been - * read, or if the connection has been closed. */ + * read, or if the connection has been closed. + */ + all_data_read = 1; } buf_fill += r; @@ -438,30 +495,40 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ } } - } while (!end_of_key_value_pair_found); + } while ( ! end_of_key_value_pair_found ); - if (fstore.fp) { + if ( fstore.fp ) { r = fclose(fstore.fp); - if (r == 0) { - /* stored successfully */ - field_stored(conn, path, file_size, fdh); - } else { - httplib_cry(conn, "%s: Error saving file %s", __func__, path); - XX_httplib_remove_bad_file(conn, path); + if ( r == 0 ) { + + /* + * stored successfully + */ + field_stored( conn, path, file_size, fdh ); } + + else { + httplib_cry( conn, "%s: Error saving file %s", __func__, path ); + XX_httplib_remove_bad_file( conn, path ); + } + fstore.fp = NULL; } - /* Proceed to next entry */ + /* + * Proceed to next entry + */ + used = next - buf; - memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); + memmove( buf, buf + (size_t)used, sizeof(buf) - (size_t)used ); buf_fill -= (int)used; } return field_count; } - if (!httplib_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) { + if ( ! httplib_strncasecmp( content_type, "MULTIPART/FORM-DATA;", 20) ) { + /* * The form data is in the request body data, encoded as multipart * content (see https://www.ietf.org/rfc/rfc1867.txt, @@ -481,9 +548,12 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ const char *content_disp; const char *next; - memset(&part_header, 0, sizeof(part_header)); + memset( & part_header, 0, sizeof(part_header) ); + + /* + * Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= + */ - /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */ bl = 20; while (content_type[bl] == ' ') bl++; @@ -491,142 +561,175 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ * There has to be a BOUNDARY definition in the Content-Type header */ - if (httplib_strncasecmp(content_type + bl, "BOUNDARY=", 9)) { - /* Malformed request */ + if ( httplib_strncasecmp( content_type + bl, "BOUNDARY=", 9 ) ) { + + /* + * Malformed request + */ + return -1; } boundary = content_type + bl + 9; - bl = strlen(boundary); + bl = strlen( boundary ); - if (bl + 800 > sizeof(buf)) { - /* Sanity check: The algorithm can not work if bl >= sizeof(buf), + if ( bl + 800 > sizeof(buf) ) { + + /* + * Sanity check: The algorithm can not work if bl >= sizeof(buf), * and it will not work effectively, if the buf is only a few byte * larger than bl, or it buf can not hold the multipart header * plus the boundary. * Check some reasonable number here, that should be fulfilled by * any reasonable request from every browser. If it is not * fulfilled, it might be a hand-made request, intended to - * interfere with the algorithm. */ + * interfere with the algorithm. + */ + return -1; } for (;;) { - size_t towrite, n; + + size_t towrite; + size_t n; int get_block; - r = httplib_read(conn, buf + (size_t)buf_fill, sizeof(buf) - 1 - (size_t)buf_fill); - if (r < 0) { - /* read error */ - return -1; - } - buf_fill += r; - buf[buf_fill] = 0; - if (buf_fill < 1) { - /* No data */ - return -1; - } + r = httplib_read( conn, buf + (size_t)buf_fill, sizeof(buf) - 1 - (size_t)buf_fill ); + if ( r < 0 ) return -1; /* read error */ + + buf_fill += r; + buf[buf_fill] = 0; + + if ( buf_fill < 1 ) return -1; /* No data */ + + if ( buf[0] != '-' || buf[1] != '-' ) return -1; /* Malformed request */ + + if ( strncmp( buf + 2, boundary, bl ) ) return -1; /* Malformed request */ + + if ( buf[bl + 2] != '\r' || buf[bl + 3] != '\n' ) { + + /* + * Every part must end with \r\n, if there is another part. + * The end of the request has an extra -- + */ + + if (((size_t)buf_fill != (size_t)(bl + 6)) || (strncmp(buf + bl + 2, "--\r\n", 4))) return -1; /* Malformed request */ + + /* + * End of the request + */ - if (buf[0] != '-' || buf[1] != '-') { - /* Malformed request */ - return -1; - } - if (strncmp(buf + 2, boundary, bl)) { - /* Malformed request */ - return -1; - } - if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') { - /* Every part must end with \r\n, if there is another part. - * The end of the request has an extra -- */ - if (((size_t)buf_fill != (size_t)(bl + 6)) - || (strncmp(buf + bl + 2, "--\r\n", 4))) { - /* Malformed request */ - return -1; - } - /* End of the request */ break; } - /* Next, we need to get the part header: Read until \r\n\r\n */ + /* + * Next, we need to get the part header: Read until \r\n\r\n + */ + hbuf = buf + bl + 4; hend = strstr(hbuf, "\r\n\r\n"); - if (!hend) { - /* Malformed request */ - return -1; - } + if ( hend == NULL ) return -1; /* Malformed request */ - XX_httplib_parse_http_headers(&hbuf, &part_header); - if ((hend + 2) != hbuf) { - /* Malformed request */ - return -1; - } + XX_httplib_parse_http_headers( &hbuf, &part_header ); + if ( (hend + 2) != hbuf ) return -1; /* Malformed request */ + + /* + * Skip \r\n\r\n + */ - /* Skip \r\n\r\n */ hend += 4; - /* According to the RFC, every part has to have a header field like: - * Content-Disposition: form-data; name="..." */ - content_disp = XX_httplib_get_header(&part_header, "Content-Disposition"); - if (!content_disp) { - /* Malformed request */ - return -1; - } + /* + * According to the RFC, every part has to have a header field like: + * Content-Disposition: form-data; name="..." + */ + + content_disp = XX_httplib_get_header( & part_header, "Content-Disposition" ); + if ( ! content_disp ) return -1; /* Malformed request */ + + /* + * Get the mandatory name="..." part of the Content-Disposition + * header. + */ + + nbeg = strstr( content_disp, "name=\"" ); + if ( nbeg == NULL ) return -1; /* Malformed request */ - /* Get the mandatory name="..." part of the Content-Disposition - * header. */ - nbeg = strstr(content_disp, "name=\""); - if (!nbeg) { - /* Malformed request */ - return -1; - } nbeg += 6; nend = strchr(nbeg, '\"'); - if (!nend) { - /* Malformed request */ - return -1; - } - /* Get the optional filename="..." part of the Content-Disposition - * header. */ - fbeg = strstr(content_disp, "filename=\""); - if (fbeg) { + if ( nend == NULL ) return -1; /* Malformed request */ + + /* + * Get the optional filename="..." part of the Content-Disposition + * header. + */ + + fbeg = strstr( content_disp, "filename=\"" ); + + if ( fbeg != NULL ) { + fbeg += 10; - fend = strchr(fbeg, '\"'); - if (!fend) { - /* Malformed request (the filename field is optional, but if - * it exists, it needs to be terminated correctly). */ + fend = strchr(fbeg, '\"'); + + if ( fend == NULL ) { + + /* + * Malformed request (the filename field is optional, but if + * it exists, it needs to be terminated correctly). + */ + return -1; } - /* TODO: check Content-Type */ - /* Content-Type: application/octet-stream */ + /* + * TODO: check Content-Type + * Content-Type: application/octet-stream + */ } else fend = fbeg; - memset(path, 0, sizeof(path)); + memset( path, 0, sizeof(path) ); field_count++; - field_storage = url_encoded_field_found(conn, nbeg, (size_t)(nend - nbeg), fbeg, (size_t)(fend - fbeg), path, sizeof(path) - 1, fdh); + field_storage = url_encoded_field_found( conn, nbeg, (size_t)(nend - nbeg), fbeg, (size_t)(fend - fbeg), path, sizeof(path) - 1, fdh ); - /* If the boundary is already in the buffer, get the address, - * otherwise next will be NULL. */ - next = search_boundary(hbuf, (size_t)((buf - hbuf) + buf_fill), boundary, bl); + /* + * If the boundary is already in the buffer, get the address, + * otherwise next will be NULL. + */ - if (field_storage == FORM_FIELD_STORAGE_STORE) { - /* Store the content to a file */ - if (XX_httplib_fopen(conn, path, "wb", &fstore) == 0) fstore.fp = NULL; + next = search_boundary( hbuf, (size_t)((buf - hbuf) + buf_fill), boundary, bl ); + + if ( field_storage == FORM_FIELD_STORAGE_STORE ) { + + /* + * Store the content to a file + */ + + if ( XX_httplib_fopen( conn, path, "wb", &fstore ) == 0 ) fstore.fp = NULL; file_size = 0; - if (!fstore.fp) httplib_cry(conn, "%s: Cannot create file %s", __func__, path); + if ( ! fstore.fp ) httplib_cry( conn, "%s: Cannot create file %s", __func__, path ); } get_block = 0; - while (!next) { - /* Set "towrite" to the number of bytes available - * in the buffer */ + + while ( ! next ) { + + /* + * Set "towrite" to the number of bytes available + * in the buffer + */ + towrite = (size_t)(buf - hend + buf_fill); - /* Subtract the boundary length, to deal with + + /* + * Subtract the boundary length, to deal with * cases the boundary is only partially stored - * in the buffer. */ + * in the buffer. + */ + towrite -= bl + 4; if (field_storage == FORM_FIELD_STORAGE_GET) { @@ -639,46 +742,58 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ get_block++; } - if (field_storage == FORM_FIELD_STORAGE_STORE) { + if ( field_storage == FORM_FIELD_STORAGE_STORE ) { + if (fstore.fp) { - /* Store the content of the buffer. */ - n = (size_t)fwrite(hend, 1, towrite, fstore.fp); - if ((n != towrite) || (ferror(fstore.fp))) { - httplib_cry(conn, "%s: Cannot write file %s", __func__, path); - fclose(fstore.fp); + /* + * Store the content of the buffer. + */ + + n = (size_t)fwrite( hend, 1, towrite, fstore.fp ); + + if ( n != towrite || ferror( fstore.fp ) ) { + + httplib_cry( conn, "%s: Cannot write file %s", __func__, path ); + fclose( fstore.fp ); fstore.fp = NULL; - XX_httplib_remove_bad_file(conn, path); + XX_httplib_remove_bad_file( conn, path ); } + file_size += (int64_t)n; } } - memmove(buf, hend + towrite, bl + 4); + memmove( buf, hend + towrite, bl + 4 ); buf_fill = (int)(bl + 4); hend = buf; - /* Read new data */ - r = httplib_read(conn, buf + (size_t)buf_fill, sizeof(buf) - 1 - (size_t)buf_fill); - if (r < 0) { - /* read error */ - return -1; - } - buf_fill += r; - buf[buf_fill] = 0; - if (buf_fill < 1) { - /* No data */ - return -1; - } + /* + * Read new data + */ - /* Find boundary */ - next = search_boundary(buf, (size_t)buf_fill, boundary, bl); + r = httplib_read( conn, buf + (size_t)buf_fill, sizeof(buf) - 1 - (size_t)buf_fill ); + if ( r < 0 ) return -1; /* read error */ + + buf_fill += r; + buf[buf_fill] = 0; + if (buf_fill < 1) return -1; /* No data */ + + /* + * Find boundary + */ + + next = search_boundary( buf, (size_t)buf_fill, boundary, bl ); } towrite = (size_t)(next - hend); - if (field_storage == FORM_FIELD_STORAGE_GET) { - /* Call callback */ + if ( field_storage == FORM_FIELD_STORAGE_GET ) { + + /* + * Call callback + */ + unencoded_field_get(conn, ((get_block > 0) ? NULL : nbeg), ((get_block > 0) ? 0 : (size_t)(nend - nbeg)), @@ -687,48 +802,59 @@ int httplib_handle_form_request(struct httplib_connection *conn, struct httplib_ fdh); } - if (field_storage == FORM_FIELD_STORAGE_STORE) { + if ( field_storage == FORM_FIELD_STORAGE_STORE ) { - if (fstore.fp) { - n = (size_t)fwrite(hend, 1, towrite, fstore.fp); - if ((n != towrite) || (ferror(fstore.fp))) { - httplib_cry(conn, "%s: Cannot write file %s", __func__, path); - fclose(fstore.fp); + if ( fstore.fp ) { + + n = (size_t)fwrite( hend, 1, towrite, fstore.fp ); + + if ( n != towrite || ferror( fstore.fp ) ) { + + httplib_cry( conn, "%s: Cannot write file %s", __func__, path ); + fclose( fstore.fp ); fstore.fp = NULL; - XX_httplib_remove_bad_file(conn, path); + XX_httplib_remove_bad_file( conn, path ); } file_size += (int64_t)n; } } - if (field_storage == FORM_FIELD_STORAGE_STORE) { + if ( field_storage == FORM_FIELD_STORAGE_STORE ) { - if (fstore.fp) { - r = fclose(fstore.fp); - if (r == 0) { - /* stored successfully */ - field_stored(conn, path, file_size, fdh); - } else { - httplib_cry(conn, "%s: Error saving file %s", __func__, path); - XX_httplib_remove_bad_file(conn, path); + if ( fstore.fp ) { + + r = fclose( fstore.fp ); + if ( r == 0 ) { + + /* + * stored successfully + */ + + field_stored( conn, path, file_size, fdh ); + } + + else { + httplib_cry( conn, "%s: Error saving file %s", __func__, path ); + XX_httplib_remove_bad_file( conn, path ); } fstore.fp = NULL; } } - if ((field_storage & FORM_FIELD_STORAGE_ABORT) - == FORM_FIELD_STORAGE_ABORT) { - /* Stop parsing the request */ - break; - } + if ( (field_storage & FORM_FIELD_STORAGE_ABORT) == FORM_FIELD_STORAGE_ABORT ) break; /* Stop parsing the request */ + + /* + * Remove from the buffer + */ - /* Remove from the buffer */ used = next - buf + 2; memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); buf_fill -= (int)used; } - /* All parts handled */ + /* + * All parts handled + */ return field_count; } diff --git a/src/httplib_handle_not_modified_static_file_request.c b/src/httplib_handle_not_modified_static_file_request.c index 4064e020..e6f88300 100644 --- a/src/httplib_handle_not_modified_static_file_request.c +++ b/src/httplib_handle_not_modified_static_file_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -44,18 +44,18 @@ void XX_httplib_handle_not_modified_static_file_request( struct httplib_connecti char etag[64]; time_t curtime; - if (conn == NULL || filep == NULL) return; - - curtime = time( NULL ); + if ( conn == NULL || filep == NULL ) return; + curtime = time( NULL ); conn->status_code = 304; - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - XX_httplib_gmt_time_string(lm, sizeof(lm), &filep->last_modified); - XX_httplib_construct_etag(etag, sizeof(etag), filep); - httplib_printf(conn, "HTTP/1.1 %d %s\r\n" "Date: %s\r\n", conn->status_code, httplib_get_response_code_text(conn, conn->status_code), date); - XX_httplib_send_static_cache_header(conn); - httplib_printf(conn, "Last-Modified: %s\r\n" "Etag: %s\r\n" "Connection: %s\r\n" "\r\n", lm, etag, XX_httplib_suggest_connection_header(conn)); + XX_httplib_gmt_time_string( date, sizeof(date), & curtime ); + XX_httplib_gmt_time_string( lm, sizeof(lm), & filep->last_modified ); + XX_httplib_construct_etag( etag, sizeof(etag), filep ); + + httplib_printf( conn, "HTTP/1.1 %d %s\r\n" "Date: %s\r\n", conn->status_code, httplib_get_response_code_text( conn, conn->status_code ), date ); + XX_httplib_send_static_cache_header( conn ); + httplib_printf( conn, "Last-Modified: %s\r\n" "Etag: %s\r\n" "Connection: %s\r\n" "\r\n", lm, etag, XX_httplib_suggest_connection_header( conn ) ); } /* XX_httplib_handle_not_modified_static_file_request */ diff --git a/src/httplib_handle_propfind.c b/src/httplib_handle_propfind.c index 39afea20..4ea51ab1 100644 --- a/src/httplib_handle_propfind.c +++ b/src/httplib_handle_propfind.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -44,9 +44,8 @@ static void print_props( struct httplib_connection *conn, const char *uri, struc if ( conn == NULL || uri == NULL || filep == NULL ) return; - XX_httplib_gmt_time_string(mtime, sizeof(mtime), &filep->last_modified); - conn->num_bytes_sent += - httplib_printf(conn, + XX_httplib_gmt_time_string( mtime, sizeof(mtime), &filep->last_modified ); + conn->num_bytes_sent += httplib_printf(conn, "" "%s" "" @@ -61,12 +60,10 @@ static void print_props( struct httplib_connection *conn, const char *uri, struc uri, filep->is_directory ? "" : "", filep->size, - mtime); + mtime ); } /* print_props */ - - /* * static void print_dav_dir_entry( struct de *de, void *data ); * @@ -79,21 +76,22 @@ static void print_dav_dir_entry( struct de *de, void *data ) { char href[PATH_MAX]; char href_encoded[PATH_MAX * 3 /* worst case */]; int truncated; + struct httplib_connection *conn; - struct httplib_connection *conn = (struct httplib_connection *)data; - if (!de || !conn) return; + conn = data; - XX_httplib_snprintf(conn, &truncated, href, sizeof(href), "%s%s", conn->request_info.local_uri, de->file_name); + if ( de == NULL || conn == NULL ) return; - if (!truncated) { - httplib_url_encode(href, href_encoded, PATH_MAX * 3); - print_props(conn, href_encoded, &de->file); + XX_httplib_snprintf( conn, &truncated, href, sizeof(href), "%s%s", conn->request_info.local_uri, de->file_name ); + + if ( ! truncated ) { + + httplib_url_encode( href, href_encoded, PATH_MAX * 3 ); + print_props( conn, href_encoded, &de->file ); } } /* print_dav_dir_entry */ - - /* * void XX_httplib_handle_propfind( struct httplib_connection *conn, const char *path, struct file *filep ); * @@ -102,33 +100,41 @@ static void print_dav_dir_entry( struct de *de, void *data ) { void XX_httplib_handle_propfind( struct httplib_connection *conn, const char *path, struct file *filep ) { - const char *depth = httplib_get_header(conn, "Depth"); + const char *depth; char date[64]; - time_t curtime = time(NULL); + time_t curtime; - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); + if ( conn == NULL || conn->ctx == NULL || path == NULL || filep == NULL ) return; - if (!conn || !path || !filep || !conn->ctx) return; + depth = httplib_get_header( conn, "Depth" ); + curtime = time( NULL ); - conn->must_close = 1; + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); + + conn->must_close = 1; conn->status_code = 207; - httplib_printf(conn, "HTTP/1.1 207 Multi-Status\r\n" "Date: %s\r\n", date); - XX_httplib_send_static_cache_header(conn); - httplib_printf(conn, "Connection: %s\r\n" "Content-Type: text/xml; charset=utf-8\r\n\r\n", XX_httplib_suggest_connection_header(conn)); + httplib_printf( conn, "HTTP/1.1 207 Multi-Status\r\n" "Date: %s\r\n", date ); + XX_httplib_send_static_cache_header( conn ); + httplib_printf( conn, "Connection: %s\r\n" "Content-Type: text/xml; charset=utf-8\r\n\r\n", XX_httplib_suggest_connection_header( conn ) ); - conn->num_bytes_sent += httplib_printf(conn, "" "\n"); + conn->num_bytes_sent += httplib_printf( conn, "" "\n" ); - /* Print properties for the requested resource itself */ - print_props(conn, conn->request_info.local_uri, filep); + /* + * Print properties for the requested resource itself + */ - /* If it is a directory, print directory entries too if Depth is not 0 */ - if (filep && filep->is_directory - && !httplib_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") - && (depth == NULL || strcmp(depth, "0") != 0)) { - XX_httplib_scan_directory(conn, path, conn, &print_dav_dir_entry); + print_props( conn, conn->request_info.local_uri, filep ); + + /* + * If it is a directory, print directory entries too if Depth is not 0 + */ + + if ( filep && filep->is_directory && !httplib_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") && (depth == NULL || strcmp(depth, "0") != 0)) { + + XX_httplib_scan_directory( conn, path, conn, &print_dav_dir_entry ); } - conn->num_bytes_sent += httplib_printf(conn, "%s\n", ""); + conn->num_bytes_sent += httplib_printf( conn, "%s\n", "" ); } /* XX_httplib_handle_propfind */ diff --git a/src/httplib_handle_request.c b/src/httplib_handle_request.c index 2dbe46c8..4e7b87c6 100644 --- a/src/httplib_handle_request.c +++ b/src/httplib_handle_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -40,294 +40,456 @@ void XX_httplib_handle_request( struct httplib_connection *conn ) { - if ( conn == NULL ) return; - - struct httplib_request_info *ri = &conn->request_info; + struct httplib_request_info *ri; char path[PATH_MAX]; int uri_len; int ssl_index; - bool is_found = false; - bool is_script_resource = false; - bool is_websocket_request = false; - bool is_put_or_delete_request = false; - bool is_callback_resource = false; + bool is_found; + bool is_script_resource; + bool is_websocket_request; + bool is_put_or_delete_request; + bool is_callback_resource; int i; struct file file = STRUCT_FILE_INITIALIZER; - httplib_request_handler callback_handler = NULL; - httplib_websocket_connect_handler ws_connect_handler = NULL; - httplib_websocket_ready_handler ws_ready_handler = NULL; - httplib_websocket_data_handler ws_data_handler = NULL; - httplib_websocket_close_handler ws_close_handler = NULL; - void *callback_data = NULL; - httplib_authorization_handler auth_handler = NULL; - void *auth_callback_data = NULL; + httplib_request_handler callback_handler; + httplib_websocket_connect_handler ws_connect_handler; + httplib_websocket_ready_handler ws_ready_handler; + httplib_websocket_data_handler ws_data_handler; + httplib_websocket_close_handler ws_close_handler; + void *callback_data; + httplib_authorization_handler auth_handler; + void *auth_callback_data; #if !defined(NO_FILES) - time_t curtime = time(NULL); + time_t curtime = time( NULL ); char date[64]; #endif - path[0] = 0; + if ( conn == NULL ) return; - if (!ri) return; + ri = & conn->request_info; + is_found = false; + is_script_resource = false; + is_websocket_request = false; + is_put_or_delete_request = false; + is_callback_resource = false; + callback_handler = NULL; + ws_connect_handler = NULL; + ws_ready_handler = NULL; + ws_data_handler = NULL; + ws_close_handler = NULL; + callback_data = NULL; + auth_handler = NULL; + auth_callback_data = NULL; + path[0] = 0; - /* 1. get the request url */ - /* 1.1. split into url and query string */ - if ((conn->request_info.query_string = strchr(ri->request_uri, '?')) - != NULL) { - *((char *)conn->request_info.query_string++) = '\0'; - } + if ( ri == NULL ) return; - /* 1.2. do a https redirect, if required. Do not decode URIs yet. */ - if (!conn->client.is_ssl && conn->client.ssl_redir) { - ssl_index = XX_httplib_get_first_ssl_listener_index(conn->ctx); - if (ssl_index >= 0) { - XX_httplib_redirect_to_https_port(conn, ssl_index); - } else { - /* A http to https forward port has been specified, - * but no https port to forward to. */ - XX_httplib_send_http_error(conn, 503, "%s", "Error: SSL forward not configured properly"); - httplib_cry(conn, "Can not redirect to SSL, no SSL port available"); + /* + * 1. get the request url + */ + + /* + * 1.1. split into url and query string + */ + + conn->request_info.query_string = strchr( ri->request_uri, '?' ); + if ( conn->request_info.query_string != NULL ) *((char *)conn->request_info.query_string++) = '\0'; + + /* + * 1.2. do a https redirect, if required. Do not decode URIs yet. + */ + + if ( ! conn->client.is_ssl && conn->client.ssl_redir ) { + + ssl_index = XX_httplib_get_first_ssl_listener_index( conn->ctx ); + + if ( ssl_index >= 0 ) XX_httplib_redirect_to_https_port( conn, ssl_index ); + + else { + /* + * A http to https forward port has been specified, + * but no https port to forward to. + */ + + XX_httplib_send_http_error( conn, 503, "%s", "Error: SSL forward not configured properly" ); + httplib_cry( conn, "Can not redirect to SSL, no SSL port available" ); } + return; } - uri_len = (int)strlen(ri->local_uri); - /* 1.3. decode url (if config says so) */ - if (XX_httplib_should_decode_url(conn)) httplib_url_decode( ri->local_uri, uri_len, (char *)ri->local_uri, uri_len + 1, 0); + uri_len = (int)strlen( ri->local_uri ); - /* 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is - * not possible */ - XX_httplib_remove_double_dots_and_double_slashes((char *)ri->local_uri); + /* + * 1.3. decode url (if config says so) + */ - /* step 1. completed, the url is known now */ - uri_len = (int)strlen(ri->local_uri); + if ( XX_httplib_should_decode_url( conn ) ) httplib_url_decode( ri->local_uri, uri_len, (char *)ri->local_uri, uri_len + 1, 0 ); - /* 3. if this ip has limited speed, set it for this connection */ - conn->throttle = XX_httplib_set_throttle(conn->ctx->config[THROTTLE], XX_httplib_get_remote_ip(conn), ri->local_uri); + /* + * 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is + * not possible + */ - /* 4. call a "handle everything" callback, if registered */ - if (conn->ctx->callbacks.begin_request != NULL) { - /* Note that since V1.7 the "begin_request" function is called + XX_httplib_remove_double_dots_and_double_slashes( (char *)ri->local_uri ); + + /* + * step 1. completed, the url is known now + */ + + uri_len = (int)strlen( ri->local_uri ); + + /* + * 3. if this ip has limited speed, set it for this connection + */ + + conn->throttle = XX_httplib_set_throttle( conn->ctx->config[THROTTLE], XX_httplib_get_remote_ip( conn ), ri->local_uri ); + + /* + * 4. call a "handle everything" callback, if registered + */ + + if ( conn->ctx->callbacks.begin_request != NULL ) { + + /* + * 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. */ - i = conn->ctx->callbacks.begin_request(conn); - if (i > 0) { - /* callback already processed the request. Store the - return value as a status code for the access log. */ + * required, use a request_handler instead. + */ + + i = conn->ctx->callbacks.begin_request( conn ); + + if ( i > 0 ) { + + /* + * callback already processed the request. Store the + * return value as a status code for the access log. + */ + conn->status_code = i; return; - } else if (i == 0) { - /* LibHTTP should process the request */ - } else { - /* unspecified - may change with the next version */ + + } + + else if ( i == 0 ) { + /* + * LibHTTP should process the request + */ + } + + else { + /* + * unspecified - may change with the next version + */ + return; } } - /* request not yet handled by a handler or redirect, so the request - * is processed here */ - - /* 5. interpret the url to find out how the request must be handled + /* + * request not yet handled by a handler or redirect, so the request + * is processed here */ - /* 5.1. first test, if the request targets the regular http(s):// + + /* + * 5. interpret the url to find out how the request must be handled + * + * 5.1. first test, if the request targets the regular http(s):// * protocol namespace or the websocket ws(s):// protocol namespace. */ - is_websocket_request = XX_httplib_is_websocket_protocol(conn); - /* 5.2. check if the request will be handled by a callback */ - if (XX_httplib_get_request_handler(conn, - is_websocket_request ? WEBSOCKET_HANDLER - : REQUEST_HANDLER, + is_websocket_request = XX_httplib_is_websocket_protocol( conn ); + + /* + * 5.2. check if the request will be handled by a callback + */ + + if ( XX_httplib_get_request_handler( conn, + is_websocket_request ? WEBSOCKET_HANDLER : REQUEST_HANDLER, &callback_handler, &ws_connect_handler, &ws_ready_handler, &ws_data_handler, &ws_close_handler, NULL, - &callback_data)) { - /* 5.2.1. A callback will handle this request. All requests + &callback_data ) ) { + /* + * 5.2.1. A callback will handle this request. All requests * handled * by a callback have to be considered as requests to a script - * resource. */ - is_callback_resource = true; - is_script_resource = true; - is_put_or_delete_request = XX_httplib_is_put_or_delete_method(conn); - } else { - no_callback_resource: - /* 5.2.2. No callback is responsible for this request. The URI + * resource. + */ + + is_callback_resource = true; + is_script_resource = true; + is_put_or_delete_request = XX_httplib_is_put_or_delete_method( conn ); + } + + else { + +no_callback_resource: + + /* + * 5.2.2. No callback is responsible for this request. The URI * addresses a file based resource (static content or Lua/cgi - * scripts in the file system). */ + * scripts in the file system). + */ + is_callback_resource = false; - XX_httplib_interpret_uri(conn, + XX_httplib_interpret_uri( conn, path, sizeof(path), &file, &is_found, &is_script_resource, &is_websocket_request, - &is_put_or_delete_request); + &is_put_or_delete_request ); } - /* 6. authorization check */ - /* 6.1. a custom authorization handler is installed */ - if (XX_httplib_get_request_handler(conn, AUTH_HANDLER, NULL, NULL, NULL, NULL, NULL, &auth_handler, &auth_callback_data)) { - if (!auth_handler(conn, auth_callback_data)) return; - } else if (is_put_or_delete_request && !is_script_resource && !is_callback_resource) { -/* 6.2. this request is a PUT/DELETE to a real file */ -/* 6.2.1. thus, the server must have real files */ + /* + * 6. authorization check + * + * 6.1. a custom authorization handler is installed + */ + + if ( XX_httplib_get_request_handler( conn, AUTH_HANDLER, NULL, NULL, NULL, NULL, NULL, &auth_handler, &auth_callback_data ) ) { + + if ( ! auth_handler( conn, auth_callback_data ) ) return; + } + + else if ( is_put_or_delete_request && ! is_script_resource && ! is_callback_resource ) { + +/* + * 6.2. this request is a PUT/DELETE to a real file + * 6.2.1. thus, the server must have real files + */ + #if defined(NO_FILES) if (1) { #else - if (conn->ctx->config[DOCUMENT_ROOT] == NULL) { + if ( conn->ctx->config[DOCUMENT_ROOT] == NULL ) { #endif - /* This server does not have any real files, thus the - * PUT/DELETE methods are not valid. */ - XX_httplib_send_http_error(conn, 405, "%s method not allowed", conn->request_info.request_method); + /* + * This server does not have any real files, thus the + * PUT/DELETE methods are not valid. + */ + + XX_httplib_send_http_error( conn, 405, "%s method not allowed", conn->request_info.request_method ); return; } #if !defined(NO_FILES) - /* 6.2.2. Check if put authorization for static files is + /* + * 6.2.2. Check if put authorization for static files is * available. */ - if (!XX_httplib_is_authorized_for_put(conn)) { - XX_httplib_send_authorization_request(conn); + if ( ! XX_httplib_is_authorized_for_put( conn ) ) { + + XX_httplib_send_authorization_request( conn ); return; } #endif - } else { - /* 6.3. This is either a OPTIONS, GET, HEAD or POST request, + } + + else { + /* + * 6.3. This is either a OPTIONS, GET, HEAD or POST request, * or it is a PUT or DELETE request to a resource that does not - * correspond to a file. Check authorization. */ - if (!XX_httplib_check_authorization(conn, path)) { - XX_httplib_send_authorization_request(conn); + * correspond to a file. Check authorization. + */ + + if ( ! XX_httplib_check_authorization( conn, path ) ) { + + XX_httplib_send_authorization_request( conn ); return; } } - /* request is authorized or does not need authorization */ + /* + * request is authorized or does not need authorization + */ + + /* + * 7. check if there are request handlers for this uri + */ + + if ( is_callback_resource ) { + + if ( ! is_websocket_request ) { + + i = callback_handler( conn, callback_data ); + if ( i > 0 ) { + + /* + * Do nothing, callback has served the request. Store the + * return value as status code for the log and discard all + * data from the client not used by the callback. + */ - /* 7. check if there are request handlers for this uri */ - if (is_callback_resource) { - if (!is_websocket_request) { - i = callback_handler(conn, callback_data); - if (i > 0) { - /* Do nothing, callback has served the request. Store - * the - * return value as status code for the log and discard - * all - * data from the client not used by the callback. */ conn->status_code = i; - XX_httplib_discard_unread_request_data(conn); - } else { - /* TODO (high): what if the handler did NOT handle the - * request */ - /* The last version did handle this as a file request, - * but + XX_httplib_discard_unread_request_data( conn ); + } + + else { + /* TODO (high): what if the handler did NOT handle the request + * The last version did handle this as a file request, but * since a file request is not always a script resource, - * the authorization check might be different */ - XX_httplib_interpret_uri(conn, + * the authorization check might be different + */ + + XX_httplib_interpret_uri( conn, path, sizeof(path), &file, &is_found, &is_script_resource, &is_websocket_request, - &is_put_or_delete_request); + &is_put_or_delete_request ); callback_handler = NULL; - /* TODO (very low): goto is deprecated but for the - * moment, - * a goto is simpler than some curious loop. */ - /* The situation "callback does not handle the request" - * needs to be reconsidered anyway. */ goto no_callback_resource; } - } else { + } + + else { #if defined(USE_WEBSOCKET) - XX_httplib_handle_websocket_request(conn, + XX_httplib_handle_websocket_request( conn, path, is_callback_resource, ws_connect_handler, ws_ready_handler, ws_data_handler, ws_close_handler, - callback_data); + callback_data ); #endif } + return; } -/* 8. handle websocket requests */ +/* + * 8. handle websocket requests + */ + #if defined(USE_WEBSOCKET) - if (is_websocket_request) { - if (is_script_resource) { - /* Websocket Lua script, the 0 in the third parameter indicates Lua */ - XX_httplib_handle_websocket_request(conn, path, 0, NULL, NULL, NULL, NULL, &conn->ctx->callbacks); - } else { - XX_httplib_send_http_error(conn, 404, "%s", "Not found"); + + if ( is_websocket_request ) { + + if ( is_script_resource ) { + + /* + * Websocket Lua script, the 0 in the third parameter indicates Lua + */ + + XX_httplib_handle_websocket_request( conn, path, 0, NULL, NULL, NULL, NULL, &conn->ctx->callbacks ); } + + else XX_httplib_send_http_error( conn, 404, "%s", "Not found" ); + return; - } else + } + + else #endif #if defined(NO_FILES) - /* 9a. In case the server uses only callbacks, this uri is + + /* + * 9a. In case the server uses only callbacks, this uri is * unknown. - * Then, all request handling ends here. */ - XX_httplib_send_http_error(conn, 404, "%s", "Not Found"); + * Then, all request handling ends here. + */ + + XX_httplib_send_http_error( conn, 404, "%s", "Not Found" ); #else - /* 9b. This request is either for a static file or resource handled - * by a script file. Thus, a DOCUMENT_ROOT must exist. */ - if (conn->ctx->config[DOCUMENT_ROOT] == NULL) { - XX_httplib_send_http_error(conn, 404, "%s", "Not Found"); + /* + * 9b. This request is either for a static file or resource handled + * by a script file. Thus, a DOCUMENT_ROOT must exist. + */ + + if ( conn->ctx->config[DOCUMENT_ROOT] == NULL ) { + + XX_httplib_send_http_error( conn, 404, "%s", "Not Found" ); return; } - /* 10. File is handled by a script. */ - if (is_script_resource) { - XX_httplib_handle_file_based_request(conn, path, &file); + /* + * 10. File is handled by a script. + */ + + if ( is_script_resource ) { + + XX_httplib_handle_file_based_request( conn, path, &file ); return; } - /* 11. Handle put/delete/mkcol requests */ - if (is_put_or_delete_request) { - /* 11.1. PUT method */ - if (!strcmp(ri->request_method, "PUT")) { - XX_httplib_put_file(conn, path); + /* + * 11. Handle put/delete/mkcol requests + */ + + if ( is_put_or_delete_request ) { + + /* + * 11.1. PUT method + */ + + if ( ! strcmp( ri->request_method, "PUT" ) ) { + + XX_httplib_put_file( conn, path ); return; } - /* 11.2. DELETE method */ - if (!strcmp(ri->request_method, "DELETE")) { - XX_httplib_delete_file(conn, path); + + /* + * 11.2. DELETE method + */ + + if ( ! strcmp( ri->request_method, "DELETE" ) ) { + + XX_httplib_delete_file( conn, path ); return; } - /* 11.3. MKCOL method */ - if (!strcmp(ri->request_method, "MKCOL")) { - XX_httplib_mkcol(conn, path); + + /* + * 11.3. MKCOL method + */ + + if ( ! strcmp( ri->request_method, "MKCOL" ) ) { + + XX_httplib_mkcol( conn, path ); return; } - /* 11.4. PATCH method + + /* + * 11.4. PATCH method * This method is not supported for static resources, - * only for scripts (Lua, CGI) and callbacks. */ - XX_httplib_send_http_error(conn, 405, "%s method not allowed", conn->request_info.request_method); + * only for scripts (Lua, CGI) and callbacks. + */ + + XX_httplib_send_http_error( conn, 405, "%s method not allowed", conn->request_info.request_method ); return; } - /* 11. File does not exist, or it was configured that it should be - * hidden */ - if (!is_found || (XX_httplib_must_hide_file(conn, path))) { - XX_httplib_send_http_error(conn, 404, "%s", "Not found"); + /* + * 11. File does not exist, or it was configured that it should be + * hidden + */ + + if ( ! is_found || XX_httplib_must_hide_file( conn, path ) ) { + + XX_httplib_send_http_error( conn, 404, "%s", "Not found" ); return; } - /* 12. Directory uris should end with a slash */ - if (file.is_directory && (uri_len > 0) - && (ri->local_uri[uri_len - 1] != '/')) { - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - httplib_printf(conn, + /* + * 12. Directory uris should end with a slash + */ + + if ( file.is_directory && uri_len > 0 && ri->local_uri[uri_len - 1] != '/' ) { + + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); + httplib_printf( conn, "HTTP/1.1 301 Moved Permanently\r\n" "Location: %s/\r\n" "Date: %s\r\n" @@ -336,48 +498,78 @@ void XX_httplib_handle_request( struct httplib_connection *conn ) { "Connection: %s\r\n\r\n", ri->request_uri, date, - XX_httplib_suggest_connection_header(conn)); + XX_httplib_suggest_connection_header( conn ) ); return; } - /* 13. Handle other methods than GET/HEAD */ - /* 13.1. Handle PROPFIND */ - if (!strcmp(ri->request_method, "PROPFIND")) { - XX_httplib_handle_propfind(conn, path, &file); + /* + * 13. Handle other methods than GET/HEAD + * 13.1. Handle PROPFIND + */ + + if ( ! strcmp( ri->request_method, "PROPFIND" ) ) { + + XX_httplib_handle_propfind( conn, path, & file ); return; } - /* 13.2. Handle OPTIONS for files */ - if (!strcmp(ri->request_method, "OPTIONS")) { - /* This standard handler is only used for real files. + + /* + * 13.2. Handle OPTIONS for files + */ + + if ( ! strcmp( ri->request_method, "OPTIONS" ) ) { + + /* + * This standard handler is only used for real files. * Scripts should support the OPTIONS method themselves, to allow a * maximum flexibility. * Lua and CGI scripts may fully support CORS this way (including - * preflights). */ - XX_httplib_send_options(conn); - return; - } - /* 13.3. everything but GET and HEAD (e.g. POST) */ - if (0 != strcmp(ri->request_method, "GET") - && 0 != strcmp(ri->request_method, "HEAD")) { - XX_httplib_send_http_error(conn, 405, "%s method not allowed", conn->request_info.request_method); + * preflights). + */ + + XX_httplib_send_options( conn ); return; } - /* 14. directories */ - if (file.is_directory) { - if (XX_httplib_substitute_index_file(conn, path, sizeof(path), &file)) { - /* 14.1. use a substitute file */ - /* TODO (high): substitute index may be a script resource. - * define what should be possible in this case. */ - } else { - /* 14.2. no substitute file */ - if (!httplib_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) XX_httplib_handle_directory_request(conn, path); - else XX_httplib_send_http_error(conn, 403, "%s", "Error: Directory listing denied"); + /* + * 13.3. everything but GET and HEAD (e.g. POST) + */ + + if ( strcmp( ri->request_method, "GET" ) && strcmp( ri->request_method, "HEAD" ) ) { + + XX_httplib_send_http_error( conn, 405, "%s method not allowed", conn->request_info.request_method ); + return; + } + + /* + * 14. directories + */ + + if ( file.is_directory ) { + + if ( XX_httplib_substitute_index_file( conn, path, sizeof(path), &file ) ) { + + /* + * 14.1. use a substitute file + * TODO (high): substitute index may be a script resource. + * define what should be possible in this case. + */ + } + + else { + /* + * 14.2. no substitute file + */ + + if ( ! httplib_strcasecmp( conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes" ) ) XX_httplib_handle_directory_request( conn, path ); + else XX_httplib_send_http_error( conn, 403, "%s", "Error: Directory listing denied" ); + return; } } - XX_httplib_handle_file_based_request(conn, path, &file); + XX_httplib_handle_file_based_request( conn, path, &file ); + #endif /* !defined(NO_FILES) */ #if 0 diff --git a/src/httplib_handle_static_file_request.c b/src/httplib_handle_static_file_request.c index 7d1a42b4..05edd6c7 100644 --- a/src/httplib_handle_static_file_request.c +++ b/src/httplib_handle_static_file_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -42,8 +42,9 @@ void XX_httplib_handle_static_file_request( struct httplib_connection *conn, con char lm[64]; char etag[64]; char range[128]; /* large enough, so there will be no overflow */ - const char *msg = "OK", *hdr; - time_t curtime = time(NULL); + const char *msg; + const char *hdr; + time_t curtime; int64_t cl; int64_t r1; int64_t r2; @@ -51,62 +52,82 @@ void XX_httplib_handle_static_file_request( struct httplib_connection *conn, con int n; int truncated; char gz_path[PATH_MAX]; - const char *encoding = ""; + const char *encoding; const char *cors1; const char *cors2; const char *cors3; - if (conn == NULL || conn->ctx == NULL || filep == NULL) return; + if ( conn == NULL || conn->ctx == NULL || filep == NULL ) return; - if (mime_type == NULL) { - XX_httplib_get_mime_type(conn->ctx, path, &mime_vec); - } else { + msg = "OK"; + curtime = time( NULL ); + encoding = ""; + + if ( mime_type == NULL ) XX_httplib_get_mime_type( conn->ctx, path, &mime_vec ); + + else { mime_vec.ptr = mime_type; - mime_vec.len = strlen(mime_type); + mime_vec.len = strlen( mime_type ); } - if (filep->size > INT64_MAX) { - XX_httplib_send_http_error(conn, 500, "Error: File size is too large to send\n%" INT64_FMT, filep->size); - } - cl = (int64_t)filep->size; + + if ( filep->size > INT64_MAX ) XX_httplib_send_http_error( conn, 500, "Error: File size is too large to send\n%" INT64_FMT, filep->size ); + + cl = (int64_t)filep->size; conn->status_code = 200; - range[0] = '\0'; + range[0] = '\0'; - /* if this file is in fact a pre-gzipped file, rewrite its filename + /* + * 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) { - XX_httplib_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path); + * the mime type from it, to preserve the actual file's type + */ - if (truncated) { - XX_httplib_send_http_error(conn, 500, "Error: Path of zipped file too long (%s)", path); + if ( filep->gzipped ) { + + XX_httplib_snprintf( conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path ); + + if ( truncated ) { + + XX_httplib_send_http_error( conn, 500, "Error: Path of zipped file too long (%s)", path ); return; } - path = gz_path; + path = gz_path; encoding = "Content-Encoding: gzip\r\n"; } - if (!XX_httplib_fopen(conn, path, "rb", filep)) { - XX_httplib_send_http_error(conn, 500, "Error: Cannot open file\nfopen(%s): %s", path, strerror(ERRNO)); + if ( ! XX_httplib_fopen( conn, path, "rb", filep ) ) { + + XX_httplib_send_http_error( conn, 500, "Error: Cannot open file\nfopen(%s): %s", path, strerror(ERRNO) ); return; } - XX_httplib_fclose_on_exec(filep, conn); + XX_httplib_fclose_on_exec( filep, conn ); + + /* + * If Range: header specified, act accordingly + */ + + r1 = 0; + r2 = 0; + hdr = httplib_get_header( conn, "Range" ); + + if ( hdr != NULL && (n = XX_httplib_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 ) { - /* If Range: header specified, act accordingly */ - r1 = r2 = 0; - hdr = httplib_get_header(conn, "Range"); - if (hdr != NULL && (n = XX_httplib_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) { XX_httplib_send_http_error( conn, 501, "%s", "Error: Range requests in gzipped files are not supported"); XX_httplib_fclose(filep); return; } conn->status_code = 206; - cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1); - XX_httplib_snprintf(conn, + cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1); + + XX_httplib_snprintf( conn, NULL, /* range buffer is big enough */ range, sizeof(range), @@ -114,32 +135,43 @@ void XX_httplib_handle_static_file_request( struct httplib_connection *conn, con "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", r1, r1 + cl - 1, - filep->size); + filep->size ); + msg = "Partial Content"; } - hdr = httplib_get_header(conn, "Origin"); - if (hdr) { - /* Cross-origin resource sharing (CORS), see + hdr = httplib_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. */ + * 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 = ""; + } + 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 */ - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - XX_httplib_gmt_time_string(lm, sizeof(lm), &filep->last_modified); - XX_httplib_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 + */ - httplib_printf(conn, "HTTP/1.1 %d %s\r\n" "%s%s%s" "Date: %s\r\n", conn->status_code, msg, cors1, cors2, cors3, date); - XX_httplib_send_static_cache_header(conn); - httplib_printf(conn, + XX_httplib_gmt_time_string( date, sizeof(date), & curtime ); + XX_httplib_gmt_time_string( lm, sizeof(lm), & filep->last_modified ); + XX_httplib_construct_etag( etag, sizeof(etag), filep ); + + httplib_printf( conn, "HTTP/1.1 %d %s\r\n" "%s%s%s" "Date: %s\r\n", conn->status_code, msg, cors1, cors2, cors3, date ); + XX_httplib_send_static_cache_header( conn ); + httplib_printf( conn, "Last-Modified: %s\r\n" "Etag: %s\r\n" "Content-Type: %.*s\r\n" @@ -154,16 +186,18 @@ void XX_httplib_handle_static_file_request( struct httplib_connection *conn, con cl, XX_httplib_suggest_connection_header(conn), range, - encoding); + encoding ); - /* The previous code must not add any header starting with X- to make - * sure no one of the additional_headers is included twice */ + /* + * The previous code must not add any header starting with X- to make + * sure no one of the additional_headers is included twice + */ - if (additional_headers != NULL) { - httplib_printf(conn, "%.*s\r\n\r\n", (int)strlen(additional_headers), additional_headers); - } else httplib_printf(conn, "\r\n"); + if ( additional_headers != NULL ) httplib_printf( conn, "%.*s\r\n\r\n", (int)strlen( additional_headers ), additional_headers ); + else httplib_printf( conn, "\r\n" ); - if (strcmp(conn->request_info.request_method, "HEAD") != 0) XX_httplib_send_file_data(conn, filep, r1, cl); - XX_httplib_fclose(filep); + if ( strcmp( conn->request_info.request_method, "HEAD" ) != 0 ) XX_httplib_send_file_data( conn, filep, r1, cl ); + + XX_httplib_fclose( filep ); } /* XX_handle_static_file_request */ diff --git a/src/httplib_handle_websocket_request.c b/src/httplib_handle_websocket_request.c index 6065868d..92436e26 100644 --- a/src/httplib_handle_websocket_request.c +++ b/src/httplib_handle_websocket_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -38,89 +38,153 @@ void XX_httplib_handle_websocket_request( struct httplib_connection *conn, const char *path, int is_callback_resource, httplib_websocket_connect_handler ws_connect_handler, httplib_websocket_ready_handler ws_ready_handler, httplib_websocket_data_handler ws_data_handler, httplib_websocket_close_handler ws_close_handler, void *cbData ) { - const char *websock_key = httplib_get_header(conn, "Sec-WebSocket-Key"); - const char *version = httplib_get_header(conn, "Sec-WebSocket-Version"); - int lua_websock = 0; + const char *websock_key; + const char *version; + int lua_websock; + const char *key1; + const char *key2; + char key3[8]; - (void)path; + UNUSED_PARAMETER(path); - /* Step 1: Check websocket protocol version. */ - /* Step 1.1: Check Sec-WebSocket-Key. */ - if (!websock_key) { - /* The RFC standard version (https://tools.ietf.org/html/rfc6455) + if ( conn == NULL ) return; + + websock_key = httplib_get_header( conn, "Sec-WebSocket-Key" ); + version = httplib_get_header( conn, "Sec-WebSocket-Version" ); + lua_websock = 0; + + /* + * Step 1: Check websocket protocol version. + * Step 1.1: Check Sec-WebSocket-Key. + */ + + if ( websock_key == NULL ) { + + /* + * The RFC standard version (https://tools.ietf.org/html/rfc6455) * requires a Sec-WebSocket-Key header. - */ - /* It could be the hixie draft version + * + * It could be the hixie draft version * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76). */ - const char *key1 = httplib_get_header(conn, "Sec-WebSocket-Key1"); - const char *key2 = httplib_get_header(conn, "Sec-WebSocket-Key2"); - char key3[8]; - if ((key1 != NULL) && (key2 != NULL)) { - /* This version uses 8 byte body data in a GET request */ + key1 = httplib_get_header( conn, "Sec-WebSocket-Key1" ); + key2 = httplib_get_header( conn, "Sec-WebSocket-Key2" ); + + if ( key1 != NULL && key2 != NULL ) { + + /* + * This version uses 8 byte body data in a GET request + */ + conn->content_len = 8; - if (8 == httplib_read(conn, key3, 8)) { - /* This is the hixie version */ - XX_httplib_send_http_error(conn, 426, "%s", "Protocol upgrade to RFC 6455 required"); + + if ( httplib_read( conn, key3, 8 ) == 8 ) { + + /* + * This is the hixie version + */ + + XX_httplib_send_http_error( conn, 426, "%s", "Protocol upgrade to RFC 6455 required" ); return; } } - /* This is an unknown version */ - XX_httplib_send_http_error(conn, 400, "%s", "Malformed websocket request"); + + /* + * This is an unknown version + */ + + XX_httplib_send_http_error( conn, 400, "%s", "Malformed websocket request" ); return; } - /* Step 1.2: Check websocket protocol version. */ - /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */ - if (version == NULL || strcmp(version, "13") != 0) { - /* Reject wrong versions */ - XX_httplib_send_http_error(conn, 426, "%s", "Protocol upgrade required"); + /* + * Step 1.2: Check websocket protocol version. + * The RFC version (https://tools.ietf.org/html/rfc6455) is 13. + */ + + if ( version == NULL || strcmp( version, "13" ) != 0 ) { + + /* + * Reject wrong versions + */ + + XX_httplib_send_http_error( conn, 426, "%s", "Protocol upgrade required" ); return; } - /* Step 1.3: Could check for "Host", but we do not really nead this - * value for anything, so just ignore it. */ + /* + * Step 1.3: Could check for "Host", but we do not really nead this + * value for anything, so just ignore it. + */ - /* Step 2: If a callback is responsible, call it. */ - if (is_callback_resource) { - if (ws_connect_handler != NULL - && ws_connect_handler(conn, cbData) != 0) { - /* C callback has returned non-zero, do not proceed with + /* + * Step 2: If a callback is responsible, call it. + */ + + if ( is_callback_resource ) { + + if ( ws_connect_handler != NULL && ws_connect_handler( conn, cbData ) != 0 ) { + + /* + * C callback has returned non-zero, do not proceed with * handshake. + * + * Note that C callbacks are no longer called when Lua is + * responsible, so C can no longer filter callbacks for Lua. */ - /* Note that C callbacks are no longer called when Lua is - * responsible, so C can no longer filter callbacks for Lua. */ + return; } } - /* Step 4: Check if there is a responsible websocket handler. */ - if (!is_callback_resource && !lua_websock) { - /* There is no callback, an Lua is not responsible either. */ + /* + * Step 4: Check if there is a responsible websocket handler. + */ + + if ( ! is_callback_resource && ! lua_websock ) { + + /* + * There is no callback, an Lua is not responsible either. */ /* Reply with a 404 Not Found or with nothing at all? * TODO (mid): check the websocket standards, how to reply to - * requests to invalid websocket addresses. */ - XX_httplib_send_http_error(conn, 404, "%s", "Not found"); + * requests to invalid websocket addresses. + */ + + XX_httplib_send_http_error( conn, 404, "%s", "Not found" ); return; } - /* Step 5: The websocket connection has been accepted */ - if (!XX_httplib_send_websocket_handshake(conn, websock_key)) { - XX_httplib_send_http_error(conn, 500, "%s", "Websocket handshake failed"); + /* + * Step 5: The websocket connection has been accepted + */ + + if ( ! XX_httplib_send_websocket_handshake( conn, websock_key ) ) { + + XX_httplib_send_http_error( conn, 500, "%s", "Websocket handshake failed" ); return; } - /* Step 6: Call the ready handler */ - if (is_callback_resource) { - if (ws_ready_handler != NULL) ws_ready_handler(conn, cbData); + /* + * Step 6: Call the ready handler + */ + + if ( is_callback_resource ) { + + if ( ws_ready_handler != NULL ) ws_ready_handler( conn, cbData ); } - /* Step 7: Enter the read loop */ + /* + * Step 7: Enter the read loop + */ + if (is_callback_resource) XX_httplib_read_websocket(conn, ws_data_handler, cbData); - /* Step 8: Call the close handler */ - if (ws_close_handler) ws_close_handler(conn, cbData); + /* + * Step 8: Call the close handler + */ + + if ( ws_close_handler ) ws_close_handler( conn, cbData ); } /* XX_httplib_handle_websocket_request */ diff --git a/src/httplib_header_has_option.c b/src/httplib_header_has_option.c index 88e4ecd2..9d1a2f73 100644 --- a/src/httplib_header_has_option.c +++ b/src/httplib_header_has_option.c @@ -22,24 +22,27 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* A helper function for checking if a comma separated list of values contains +/* + * A helper function for checking if a comma separated list of values contains * the given option (case insensitvely). - * 'header' can be NULL, in which case false is returned. */ + * 'header' can be NULL, in which case false is returned. + */ + int XX_httplib_header_has_option( const char *header, const char *option ) { struct vec opt_vec; struct vec eq_vec; - assert(option != NULL); - assert(option[0] != '\0'); + if ( option == NULL || option[0] == '\0' ) return false; - while ((header = XX_httplib_next_option(header, &opt_vec, &eq_vec)) != NULL) { - if (httplib_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0) return 1; + while ( (header = XX_httplib_next_option( header, &opt_vec, &eq_vec )) != NULL ) { + + if ( httplib_strncasecmp( option, opt_vec.ptr, opt_vec.len ) == 0 ) return 1; } return 0; diff --git a/src/httplib_inet_pton.c b/src/httplib_inet_pton.c index f4647abd..85788626 100644 --- a/src/httplib_inet_pton.c +++ b/src/httplib_inet_pton.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -30,36 +30,47 @@ int XX_httplib_inet_pton( int af, const char *src, void *dst, size_t dstlen ) { - struct addrinfo hints, *res, *ressave; - int func_ret = 0; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *ressave; + int func_ret; int gai_ret; - memset(&hints, 0, sizeof(struct addrinfo)); + func_ret = 0; + + memset( & hints, 0, sizeof(struct addrinfo) ); hints.ai_family = af; - gai_ret = getaddrinfo(src, NULL, &hints, &res); - if (gai_ret != 0) { - /* gai_strerror could be used to convert gai_ret to a string */ - /* POSIX return values: see + gai_ret = getaddrinfo( src, NULL, &hints, &res ); + + if ( gai_ret != 0 ) { + + /* + * gai_strerror could be used to convert gai_ret to a string + * POSIX return values: see * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html - */ - /* Windows return values: see + * + * Windows return values: see * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx */ + return 0; } ressave = res; - while (res) { - if (dstlen >= res->ai_addrlen) { - memcpy(dst, res->ai_addr, res->ai_addrlen); + while ( res ) { + + if ( dstlen >= res->ai_addrlen ) { + + memcpy( dst, res->ai_addr, res->ai_addrlen ); func_ret = 1; } res = res->ai_next; } - freeaddrinfo(ressave); + freeaddrinfo( ressave ); + return func_ret; } /* XX_httplib_inet_pton */ diff --git a/src/httplib_initialize_ssl.c b/src/httplib_initialize_ssl.c index 9e63e02b..740b465f 100644 --- a/src/httplib_initialize_ssl.c +++ b/src/httplib_initialize_ssl.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -58,31 +58,35 @@ int XX_httplib_initialize_ssl( struct httplib_context *ctx ) { size_t size; #if !defined(NO_SSL_DL) - if (!cryptolib_dll_handle) { - cryptolib_dll_handle = XX_httplib_load_dll(ctx, CRYPTO_LIB, XX_httplib_crypto_sw); - if (!cryptolib_dll_handle) return 0; + if ( ! cryptolib_dll_handle ) { + + cryptolib_dll_handle = XX_httplib_load_dll( ctx, CRYPTO_LIB, XX_httplib_crypto_sw ); + if ( ! cryptolib_dll_handle ) return 0; } #endif /* NO_SSL_DL */ - if (httplib_atomic_inc(&XX_httplib_cryptolib_users) > 1) return 1; + if ( httplib_atomic_inc( & XX_httplib_cryptolib_users ) > 1 ) return 1; - /* Initialize locking callbacks, needed for thread safety. + /* + * Initialize locking callbacks, needed for thread safety. * http://www.openssl.org/support/faq.html#PROG1 */ + i = CRYPTO_num_locks(); - if (i < 0) i = 0; + if ( i < 0 ) i = 0; + size = sizeof(pthread_mutex_t) * ((size_t)(i)); - if ((XX_httplib_ssl_mutexes = httplib_malloc( size )) == NULL) { - httplib_cry( XX_httplib_fc(ctx), "%s: cannot allocate mutexes: %s", __func__, XX_httplib_ssl_error()); + + if ( (XX_httplib_ssl_mutexes = httplib_malloc( size )) == NULL ) { + + httplib_cry( XX_httplib_fc(ctx), "%s: cannot allocate mutexes: %s", __func__, XX_httplib_ssl_error() ); return 0; } - for (i = 0; i < CRYPTO_num_locks(); i++) { - pthread_mutex_init(&XX_httplib_ssl_mutexes[i], &XX_httplib_pthread_mutex_attr); - } + for (i=0; irequest_info.local_uri; - const char *root = conn->ctx->config[DOCUMENT_ROOT]; + const char *uri; + const char *root; const char *rewrite; struct vec a; struct vec b; @@ -65,63 +65,87 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename, UNUSED_PARAMETER( filename_buf_len ); #endif /* NO_FILES */ - memset(filep, 0, sizeof(*filep)); - *filename = 0; - *is_found = false; - *is_script_resource = false; - *is_put_or_delete_request = XX_httplib_is_put_or_delete_method(conn); + if ( conn == NULL || conn->ctx == NULL || filep == NULL ) return; + + uri = conn->request_info.local_uri; + root = conn->ctx->config[DOCUMENT_ROOT]; + + memset( filep, 0, sizeof(*filep) ); + + *filename = 0; + *is_found = false; + *is_script_resource = false; + *is_put_or_delete_request = XX_httplib_is_put_or_delete_method( conn ); #if defined(USE_WEBSOCKET) - *is_websocket_request = XX_httplib_is_websocket_protocol(conn); + *is_websocket_request = XX_httplib_is_websocket_protocol( conn ); #if !defined(NO_FILES) - if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) { - root = conn->ctx->config[WEBSOCKET_ROOT]; - } + if ( *is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT] ) root = conn->ctx->config[WEBSOCKET_ROOT]; #endif /* !NO_FILES */ #else /* USE_WEBSOCKET */ *is_websocket_request = false; #endif /* USE_WEBSOCKET */ #if !defined(NO_FILES) - /* Note that root == NULL is a regular use case here. This occurs, + /* + * 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 + * 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 + /* + * 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. */ - XX_httplib_snprintf( conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri); + * If document_root is NULL, leave the file empty. + */ - if (truncated) goto interpret_cleanup; + XX_httplib_snprintf( conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri ); + + if ( truncated ) goto interpret_cleanup; rewrite = conn->ctx->config[REWRITE]; - while ((rewrite = XX_httplib_next_option(rewrite, &a, &b)) != NULL) { - if ((match_len = XX_httplib_match_prefix(a.ptr, a.len, uri)) > 0) { - XX_httplib_snprintf(conn, &truncated, filename, filename_buf_len - 1, "%.*s%s", (int)b.len, b.ptr, uri + match_len); + + while ( (rewrite = XX_httplib_next_option( rewrite, &a, &b )) != NULL ) { + + match_len = XX_httplib_match_prefix( a.ptr, a.len, uri ); + + if ( match_len > 0 ) { + + XX_httplib_snprintf( conn, &truncated, filename, filename_buf_len - 1, "%.*s%s", (int)b.len, b.ptr, uri + match_len ); break; } } - if (truncated) goto interpret_cleanup; + if ( truncated ) goto interpret_cleanup; - /* Local file path and name, corresponding to requested URI - * is now stored in "filename" variable. */ - if (XX_httplib_stat(conn, filename, filep)) { + /* + * Local file path and name, corresponding to requested URI + * is now stored in "filename" variable. + */ + + if ( XX_httplib_stat( conn, filename, filep ) ) { #if !defined(NO_CGI) - /* File exists. Check if it is a script type. */ + + /* + * File exists. Check if it is a script type. + */ + if (0 #if !defined(NO_CGI) - || XX_httplib_match_prefix(conn->ctx->config[CGI_EXTENSIONS], - strlen(conn->ctx->config[CGI_EXTENSIONS]), - filename) > 0 + || XX_httplib_match_prefix(conn->ctx->config[CGI_EXTENSIONS], strlen(conn->ctx->config[CGI_EXTENSIONS]), filename) > 0 #endif ) { - /* The request addresses a CGI script or a Lua script. The URI + /* + * 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). @@ -130,63 +154,89 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename, * 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_resource = !*is_put_or_delete_request; + * generated response. + */ + + *is_script_resource = ! *is_put_or_delete_request; } + #endif /* !defined(NO_CGI) */ + *is_found = true; return; } - /* If we can't find the actual file, look for the file + /* + * 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 = httplib_get_header(conn, "Accept-Encoding")) != NULL) { - if (strstr(accept_encoding, "gzip") != NULL) { - XX_httplib_snprintf( conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename); + * We can only do this if the browser declares support. + */ - if (truncated) { - goto interpret_cleanup; - } + if ( (accept_encoding = httplib_get_header( conn, "Accept-Encoding" )) != NULL ) { + + if ( strstr( accept_encoding, "gzip" ) != NULL ) { + + XX_httplib_snprintf( conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename ); + + if ( truncated ) goto interpret_cleanup; + + if ( XX_httplib_stat( conn, gz_path, filep ) ) { + + if ( filep ) { - if (XX_httplib_stat(conn, gz_path, filep)) { - if (filep) { filep->gzipped = 1; *is_found = true; } - /* Currently gz files can not be scripts. */ + + /* + * Currently gz files can not be scripts. + */ + return; } } } #if !defined(NO_CGI) - /* Support PATH_INFO for CGI scripts. */ - for (p = filename + strlen(filename); p > filename + 1; p--) { - if (*p == '/') { + + /* + * 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) - || XX_httplib_match_prefix(conn->ctx->config[CGI_EXTENSIONS], - strlen(conn->ctx->config[CGI_EXTENSIONS]), - filename) > 0 + || XX_httplib_match_prefix(conn->ctx->config[CGI_EXTENSIONS], strlen(conn->ctx->config[CGI_EXTENSIONS]), filename) > 0 #endif ) && XX_httplib_stat(conn, filename, filep)) { - /* Shift PATH_INFO block one character right, e.g. + + /* + * 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 XX_httplib_handle_request(), so PATH_INFO is not valid - * after XX_httplib_handle_request returns. */ + * after XX_httplib_handle_request returns. + */ + conn->path_info = p + 1; - memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for - * trailing \0 */ - p[1] = '/'; + + memmove( p + 2, p + 1, strlen(p + 1) + 1 ); /* +1 is for trailing \0 */ + + p[1] = '/'; *is_script_resource = true; + break; - } else *p = '/'; + } + + else *p = '/'; } } #endif /* !defined(NO_CGI) */ @@ -194,8 +244,13 @@ void XX_httplib_interpret_uri( struct httplib_connection *conn, char *filename, return; #if !defined(NO_FILES) -/* Reset all outputs */ + +/* + * Reset all outputs + */ + interpret_cleanup: + memset( filep, 0, sizeof(*filep) ); *filename = 0; @@ -203,6 +258,7 @@ interpret_cleanup: *is_script_resource = false; *is_websocket_request = false; *is_put_or_delete_request = false; + #endif /* !defined(NO_FILES) */ } /* XX_httplib_interpret_uri */ diff --git a/src/httplib_is_authorized_for_put.c b/src/httplib_is_authorized_for_put.c index 5ef0d564..674e29d0 100644 --- a/src/httplib_is_authorized_for_put.c +++ b/src/httplib_is_authorized_for_put.c @@ -22,26 +22,32 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #if !defined(NO_FILES) + int XX_httplib_is_authorized_for_put( struct httplib_connection *conn ) { - if ( conn == NULL ) return 0; - struct file file = STRUCT_FILE_INITIALIZER; - const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; - int ret = 0; + const char *passfile; + int ret; - if (passfile != NULL && XX_httplib_fopen(conn, passfile, "r", &file)) { - ret = XX_httplib_authorize(conn, &file); - XX_httplib_fclose(&file); + if ( conn == NULL || conn->ctx == NULL ) return 0; + + passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; + ret = 0; + + if ( passfile != NULL && XX_httplib_fopen( conn, passfile, "r", &file ) ) { + + ret = XX_httplib_authorize( conn, &file ); + XX_httplib_fclose( & file ); } return ret; } /* XX_httplib_is_authorized_for_put */ + #endif diff --git a/src/httplib_is_put_or_delete_method.c b/src/httplib_is_put_or_delete_method.c index a44e2f50..499d0ac6 100644 --- a/src/httplib_is_put_or_delete_method.c +++ b/src/httplib_is_put_or_delete_method.c @@ -22,17 +22,26 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" +/* + * bool XX_httplib_is_put_or_delete_method( const struct httplib_connection *conn ); + * + * The function XX_httplib_is_put_or_delete_method() returns true of the method + * of the request on a connection is one which writes at the file level on the + * server like PUT, DELETE, MKCOL and PATCH. + */ + bool XX_httplib_is_put_or_delete_method( const struct httplib_connection *conn ) { + const char *s; + if ( conn == NULL ) return false; - const char *s = conn->request_info.request_method; - - return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH")); + s = conn->request_info.request_method; + return s != NULL && ( ! strcmp( s, "PUT" ) || ! strcmp( s, "DELETE" ) || ! strcmp( s, "MKCOL" ) || ! strcmp( s, "PATCH" ) ); } /* X_httplib_is_put_or_delete_method */ diff --git a/src/httplib_is_valid_port.c b/src/httplib_is_valid_port.c index cc8c9d1b..afb77aac 100644 --- a/src/httplib_is_valid_port.c +++ b/src/httplib_is_valid_port.c @@ -22,13 +22,20 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -int XX_httplib_is_valid_port(unsigned long port) { +/* + * int XX_httplib_is_valid_port( unsigned long port ); + * + * The function XX_httplib_is_valid_port() returns true, if the port number + * specified is a valid port for socket communication. + */ - return (port < 0xffff); +int XX_httplib_is_valid_port( unsigned long port ) { + + return ( port < 0xffff ); } /* XX_httplib_is_valid_port */ diff --git a/src/httplib_is_websocket_protocol.c b/src/httplib_is_websocket_protocol.c index a5a4b911..e0f530e3 100644 --- a/src/httplib_is_websocket_protocol.c +++ b/src/httplib_is_websocket_protocol.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -41,23 +41,25 @@ bool XX_httplib_is_websocket_protocol( const struct httplib_connection *conn ) { const char *upgrade; const char *connection; - /* A websocket protocoll has the following HTTP headers: + /* + * A websocket protocol has the following HTTP headers: * * Connection: Upgrade * Upgrade: Websocket */ - upgrade = httplib_get_header(conn, "Upgrade"); - if (upgrade == NULL) return false; /* fail early, don't waste time checking other header * fields */ + upgrade = httplib_get_header( conn, "Upgrade" ); + if ( upgrade == NULL ) return false; /* fail early, don't waste time checking other header * fields */ if ( httplib_strcasestr(upgrade, "websocket") == NULL ) return false; - connection = httplib_get_header(conn, "Connection"); - if (connection == NULL) return false; + connection = httplib_get_header( conn, "Connection" ); + if ( connection == NULL ) return false; - if ( httplib_strcasestr(connection, "upgrade") == NULL ) return false; + if ( httplib_strcasestr( connection, "upgrade" ) == NULL ) return false; - /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and + /* + * The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and * "Sec-WebSocket-Version" are also required. * Don't check them here, since even an unsupported websocket protocol * request still IS a websocket request (in contrast to a standard HTTP diff --git a/src/httplib_load_dll.c b/src/httplib_load_dll.c index fb8f3ffa..cb1b345d 100644 --- a/src/httplib_load_dll.c +++ b/src/httplib_load_dll.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -31,37 +31,33 @@ #if defined(_WIN32) #if !defined(NO_SSL_DL) && !defined(NO_SSL) -/* If SSL is loaded dynamically, dlopen/dlclose is required. */ -/* Create substitutes for POSIX functions in Win32. */ +/* + * If SSL is loaded dynamically, dlopen/dlclose is required. + * Create substitutes for POSIX functions in Win32. + */ static HANDLE dlopen( const char *dll_name, int flags ) { wchar_t wbuf[PATH_MAX]; - (void)flags; - XX_httplib_path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf)); - return LoadLibraryW(wbuf); + UNUSED_PARAMETER(flags); + + XX_httplib_path_to_unicode( NULL, dll_name, wbuf, ARRAY_SIZE(wbuf) ); + return LoadLibraryW( wbuf ); } /* dlopen */ static int dlclose( void *handle ) { - int result; - - if ( FreeLibrary((HMODULE)handle) != 0 ) result = 0; - else result = -1; - - return result; + return ( FreeLibrary((HMODULE)handle) != 0 ) ? 0 : -1; } /* dlclose */ -#endif - -#endif - +#endif /* ! NO_SSL_DL && ! NO_SSL */ +#endif /* _WIN32 */ /* * XX_httplib_load_dll( struct httplib_context *ctx, const char *dll_name, struct ssl_func *sw ); @@ -71,9 +67,8 @@ static int dlclose( void *handle ) { * error occured. */ -#if !defined(NO_SSL) +#if !defined(NO_SSL) && !defined(NO_SSL_DL) -#if !defined(NO_SSL_DL) void *XX_httplib_load_dll( struct httplib_context *ctx, const char *dll_name, struct ssl_func *sw ) { union { @@ -83,31 +78,48 @@ void *XX_httplib_load_dll( struct httplib_context *ctx, const char *dll_name, st void *dll_handle; struct ssl_func *fp; - if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) { - httplib_cry( XX_httplib_fc(ctx), "%s: cannot load %s", __func__, dll_name); + dll_handle = dlopen( dll_name, RTLD_LAZY ); + + if ( dll_handle == NULL ) { + + httplib_cry( XX_httplib_fc(ctx), "%s: cannot load %s", __func__, dll_name ); return NULL; } - for (fp = sw; fp->name != NULL; fp++) { + for (fp=sw; fp->name != NULL; fp++) { #ifdef _WIN32 - /* GetProcAddress() returns pointer to function */ - u.fp = (void (*)(void))dlsym(dll_handle, fp->name); -#else - /* dlsym() on UNIX returns void *. ISO C forbids casts of data + + /* + * GetProcAddress() returns pointer to function + */ + + u.fp = (void (*)(void))dlsym( dll_handle, fp->name ); + +#else /* _WIN32 */ + + /* + * dlsym() on UNIX returns void *. ISO C forbids casts of data * pointers to function pointers. We need to use a union to make a - * cast. */ - u.p = dlsym(dll_handle, fp->name); + * cast. + */ + + u.p = dlsym( dll_handle, fp->name ); + #endif /* _WIN32 */ - if (u.fp == NULL) { - httplib_cry( XX_httplib_fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name); - dlclose(dll_handle); + + if ( u.fp == NULL ) { + + httplib_cry( XX_httplib_fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name ); + dlclose( dll_handle ); + return NULL; - } else fp->ptr = u.fp; + } + + else fp->ptr = u.fp; } return dll_handle; } /* XX_httplib_load_dll */ -#endif /* NO_SSL_DL */ -#endif /* !NO_SSL */ +#endif /* !NO_SSL_DL && !NO_SSL */ diff --git a/src/httplib_lock_unlock_connection.c b/src/httplib_lock_unlock_connection.c index c1d81e8e..27c3bcbe 100644 --- a/src/httplib_lock_unlock_connection.c +++ b/src/httplib_lock_unlock_connection.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" diff --git a/src/httplib_lock_unlock_context.c b/src/httplib_lock_unlock_context.c index 7d973e8e..f9476d8c 100644 --- a/src/httplib_lock_unlock_context.c +++ b/src/httplib_lock_unlock_context.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" diff --git a/src/httplib_log_access.c b/src/httplib_log_access.c index 9bb6d17f..3cafc00c 100644 --- a/src/httplib_log_access.c +++ b/src/httplib_log_access.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -44,37 +44,41 @@ void XX_httplib_log_access( const struct httplib_connection *conn ) { char date[64]; char src_addr[IP_ADDR_STR_LEN]; struct tm *tm; - const char *referer; const char *user_agent; - char buf[4096]; - if (!conn || !conn->ctx) return; + if ( conn == NULL || conn->ctx == NULL ) return; - if (conn->ctx->config[ACCESS_LOG_FILE] != NULL) { - if (XX_httplib_fopen(conn, conn->ctx->config[ACCESS_LOG_FILE], "a+", &fi) == 0) fi.fp = NULL; - } else fi.fp = NULL; + if ( conn->ctx->config[ACCESS_LOG_FILE] != NULL ) { - /* Log is written to a file and/or a callback. If both are not set, - * executing the rest of the function is pointless. */ - if ((fi.fp == NULL) && (conn->ctx->callbacks.log_access == NULL)) return; + if ( XX_httplib_fopen( conn, conn->ctx->config[ACCESS_LOG_FILE], "a+", &fi ) == 0 ) fi.fp = NULL; + } + else fi.fp = NULL; - tm = localtime(&conn->conn_birth_time); - if (tm != NULL) { - strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm); - } else { + /* + * Log is written to a file and/or a callback. If both are not set, + * executing the rest of the function is pointless. + */ + + if ( fi.fp == NULL && conn->ctx->callbacks.log_access == NULL ) return; + + tm = localtime( & conn->conn_birth_time ); + + if ( tm != NULL ) strftime( date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm ); + else { httplib_strlcpy( date, "01/Jan/1970:00:00:00 +0000", sizeof(date) ); date[sizeof(date) - 1] = '\0'; } - ri = &conn->request_info; + ri = & conn->request_info; - XX_httplib_sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); - referer = header_val(conn, "Referer"); - user_agent = header_val(conn, "User-Agent"); + XX_httplib_sockaddr_to_string( src_addr, sizeof(src_addr), &conn->client.rsa ); - XX_httplib_snprintf(conn, + referer = header_val( conn, "Referer" ); + user_agent = header_val( conn, "User-Agent" ); + + XX_httplib_snprintf( conn, NULL, /* Ignore truncation in access log */ buf, sizeof(buf), @@ -90,16 +94,18 @@ void XX_httplib_log_access( const struct httplib_connection *conn ) { conn->status_code, conn->num_bytes_sent, referer, - user_agent); + user_agent ); - if (conn->ctx->callbacks.log_access) conn->ctx->callbacks.log_access(conn, buf); + if ( conn->ctx->callbacks.log_access != NULL ) conn->ctx->callbacks.log_access( conn, buf ); - if (fi.fp) { - flockfile(fi.fp); - fprintf(fi.fp, "%s\n", buf); - fflush(fi.fp); - funlockfile(fi.fp); - XX_httplib_fclose(&fi); + if ( fi.fp ) { + + flockfile( fi.fp ); + fprintf( fi.fp, "%s\n", buf ); + fflush( fi.fp ); + funlockfile( fi.fp ); + + XX_httplib_fclose( &fi ); } } /* XX_httplib_log_access */ @@ -117,7 +123,9 @@ static const char *header_val( const struct httplib_connection *conn, const char const char *header_value; - if ((header_value = httplib_get_header(conn, header)) == NULL) return "-"; - else return header_value; + header_value = httplib_get_header( conn, header ); + + if ( header_value == NULL ) return "-"; + else return header_value; } /* header_val */ diff --git a/src/httplib_lowercase.c b/src/httplib_lowercase.c index 51df833f..176daf57 100644 --- a/src/httplib_lowercase.c +++ b/src/httplib_lowercase.c @@ -22,14 +22,14 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_utils.h" -int XX_httplib_lowercase(const char *s) { +int XX_httplib_lowercase( const char *s ) { - return tolower(*(const unsigned char *)s); + return tolower( *(const unsigned char *)s ); } /* XX_httplib_lowercase */ diff --git a/src/httplib_main.h b/src/httplib_main.h index 4ea13726..399310a2 100644 --- a/src/httplib_main.h +++ b/src/httplib_main.h @@ -791,12 +791,12 @@ void SHA1Init( SHA1_CTX *context ); void SHA1Update( SHA1_CTX *context, const unsigned char *data, uint32_t len ); void XX_httplib_accept_new_connection( const struct socket *listener, struct httplib_context *ctx ); -int XX_httplib_authorize( struct httplib_connection *conn, struct file *filep ); +bool XX_httplib_authorize( struct httplib_connection *conn, struct file *filep ); const char * XX_httplib_builtin_mime_ext( int index ); const char * XX_httplib_builtin_mime_type( int index ); int XX_httplib_check_acl( struct httplib_context *ctx, uint32_t remote_ip ); -int XX_httplib_check_authorization( struct httplib_connection *conn, const char *path ); -int XX_httplib_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 ); +bool XX_httplib_check_authorization( struct httplib_connection *conn, const char *path ); +bool XX_httplib_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 ); void XX_httplib_close_all_listening_sockets( struct httplib_context *ctx ); void XX_httplib_close_connection( struct httplib_connection *conn ); void XX_httplib_close_socket_gracefully( struct httplib_connection *conn ); @@ -866,7 +866,7 @@ int XX_httplib_pull_all( FILE *fp, struct httplib_connection *conn, char *buf, int64_t XX_httplib_push_all( struct httplib_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len ); int XX_httplib_put_dir( struct httplib_connection *conn, const char *path ); void XX_httplib_put_file( struct httplib_connection *conn, const char *path ); -int XX_httplib_read_auth_file( struct file *filep, struct read_auth_file_struct *workdata ); +bool XX_httplib_read_auth_file( struct file *filep, struct read_auth_file_struct *workdata ); int XX_httplib_read_request( FILE *fp, struct httplib_connection *conn, char *buf, int bufsiz, int *nread ); void XX_httplib_read_websocket( struct httplib_connection *conn, httplib_websocket_data_handler ws_data_handler, void *callback_data ); void XX_httplib_redirect_to_https_port( struct httplib_connection *conn, int ssl_index ); @@ -894,8 +894,8 @@ int XX_httplib_set_tcp_nodelay( SOCKET sock, int nodelay_on ); void XX_httplib_set_thread_name( const char *name ); int XX_httplib_set_throttle( const char *spec, uint32_t remote_ip, const char *uri ); int XX_httplib_set_uid_option( struct httplib_context *ctx ); -int XX_httplib_should_decode_url( const struct httplib_connection *conn ); -int XX_httplib_should_keep_alive( const struct httplib_connection *conn ); +bool XX_httplib_should_decode_url( const struct httplib_connection *conn ); +bool XX_httplib_should_keep_alive( const struct httplib_connection *conn ); char * XX_httplib_skip( char **buf, const char *delimiters ); char * XX_httplib_skip_quoted( char **buf, const char *delimiters, const char *whitespace, char quotechar ); void XX_httplib_sockaddr_to_string(char *buf, size_t len, const union usa *usa ); diff --git a/src/httplib_master_thread.c b/src/httplib_master_thread.c index 5b8d4669..49879aa3 100644 --- a/src/httplib_master_thread.c +++ b/src/httplib_master_thread.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -32,21 +32,18 @@ static void master_thread_run( void *thread_func_param ); /* - * ... XX_httplib_master_thread( void *thread_func_param ); + * LIBHTTP_THREAD XX_httplib_master_thread( void *thread_func_param ); * * The function XX_httplib_master_thread() runs the master thread of the * webserver. Due to operating system differences there are two different * function headers. */ -#ifdef _WIN32 -unsigned __stdcall XX_httplib_master_thread( void *thread_func_param ) { -#else -void *XX_httplib_master_thread( void *thread_func_param ) { -#endif +LIBHTTP_THREAD XX_httplib_master_thread( void *thread_func_param ) { master_thread_run( thread_func_param ); - return 0; + + return LIBHTTP_THREAD_RETNULL; } /* XX_httplib_master_thread */ @@ -71,97 +68,141 @@ static void master_thread_run(void *thread_func_param) { XX_httplib_set_thread_name( "master" ); -/* Increase priority of the master thread */ +/* + * Increase priority of the master thread + */ + #if defined(_WIN32) - SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL ); + SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL ); #elif defined(USE_MASTER_THREAD_PRIORITY) - int min_prio = sched_get_priority_min(SCHED_RR); - int max_prio = sched_get_priority_max(SCHED_RR); + int min_prio = sched_get_priority_min( SCHED_RR ); + int max_prio = sched_get_priority_max( SCHED_RR ); + if ((min_prio >= 0) && (max_prio >= 0) && ((USE_MASTER_THREAD_PRIORITY) <= max_prio) && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) { + struct sched_param sched_param = {0}; sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY); pthread_setschedparam( httplib_pthread_self(), SCHED_RR, & sched_param ); } #endif -/* Initialize thread local storage */ +/* + * Initialize thread local storage + */ + #if defined(_WIN32) - tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL); + tls.pthread_cond_helper_mutex = CreateEvent( NULL, FALSE, FALSE, NULL ); #endif tls.is_master = 1; httplib_pthread_setspecific( XX_httplib_sTlsKey, &tls ); - if (ctx->callbacks.init_thread) { - /* Callback for the master thread (type 0) */ - ctx->callbacks.init_thread(ctx, 0); + if ( ctx->callbacks.init_thread ) { + + /* + * Callback for the master thread (type 0) + */ + + ctx->callbacks.init_thread( ctx, 0 ); } - /* Server starts *now* */ + /* + * Server starts *now* + */ + ctx->start_time = time( NULL ); - /* Start the server */ + /* + * Start the server + */ + pfd = ctx->listening_socket_fds; - while (ctx->stop_flag == 0) { - for (i = 0; i < ctx->num_listening_sockets; i++) { - pfd[i].fd = ctx->listening_sockets[i].sock; + + while ( ctx->stop_flag == 0 ) { + + for (i=0; inum_listening_sockets; i++) { + + pfd[i].fd = ctx->listening_sockets[i].sock; pfd[i].events = POLLIN; } - if (poll(pfd, ctx->num_listening_sockets, 200) > 0) { - for (i = 0; i < ctx->num_listening_sockets; i++) { - /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the + if ( poll( pfd, ctx->num_listening_sockets, 200 ) > 0 ) { + + for (i=0; inum_listening_sockets; i++) { + + /* + * NOTE(lsm): on QNX, poll() returns POLLRDNORM after the * successful poll, and POLLIN is defined as * (POLLRDNORM | POLLRDBAND) * Therefore, we're checking pfd[i].revents & POLLIN, not - * pfd[i].revents == POLLIN. */ - if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) { - XX_httplib_accept_new_connection(&ctx->listening_sockets[i], ctx); - } + * pfd[i].revents == POLLIN. + */ + + if ( ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) XX_httplib_accept_new_connection( & ctx->listening_sockets[i], ctx ); } } } - /* Here stop_flag is 1 - Initiate shutdown. */ + /* + * Here stop_flag is 1 - Initiate shutdown. + */ - /* Stop signal received: somebody called httplib_stop. Quit. */ - XX_httplib_close_all_listening_sockets(ctx); + /* + * Stop signal received: somebody called httplib_stop. Quit. + */ + + XX_httplib_close_all_listening_sockets( ctx ); + + /* + * Wakeup workers that are waiting for connections to handle. + */ - /* Wakeup workers that are waiting for connections to handle. */ httplib_pthread_mutex_lock( & ctx->thread_mutex ); + #if defined(ALTERNATIVE_QUEUE) + for (i=0; icfg_worker_threads; i++) { event_signal( ctx->client_wait_events[i]i ); - /* Since we know all sockets, we can shutdown the connections. */ - if (ctx->client_socks[i].in_use) shutdown( ctx->client_socks[i].sock, SHUTDOWN_BOTH ); + /* + * Since we know all sockets, we can shutdown the connections. + */ + + if ( ctx->client_socks[i].in_use ) shutdown( ctx->client_socks[i].sock, SHUTDOWN_BOTH ); } #else httplib_pthread_cond_broadcast( & ctx->sq_full ); #endif httplib_pthread_mutex_unlock( & ctx->thread_mutex ); - /* Join all worker threads to avoid leaking threads. */ + /* + * Join all worker threads to avoid leaking threads. + */ + workerthreadcount = ctx->cfg_worker_threads; + for (i=0; iworkerthreadids[i] != 0 ) httplib_pthread_join( ctx->workerthreadids[i], NULL ); } #if !defined(NO_SSL) - if (ctx->ssl_ctx != NULL) XX_httplib_uninitialize_ssl(ctx); + if ( ctx->ssl_ctx != NULL ) XX_httplib_uninitialize_ssl( ctx ); #endif #if defined(_WIN32) - CloseHandle(tls.pthread_cond_helper_mutex); + CloseHandle( tls.pthread_cond_helper_mutex ); #endif httplib_pthread_setspecific( XX_httplib_sTlsKey, NULL ); - /* Signal httplib_stop() that we're done. + /* + * Signal httplib_stop() that we're done. * WARNING: This must be the very last thing this - * thread does, as ctx becomes invalid after this line. */ + * thread does, as ctx becomes invalid after this line. + */ + ctx->stop_flag = 2; } /* master_thread_run */ diff --git a/src/httplib_match_prefix.c b/src/httplib_match_prefix.c index f6acdbc7..97d450d9 100644 --- a/src/httplib_match_prefix.c +++ b/src/httplib_match_prefix.c @@ -22,13 +22,16 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_utils.h" -/* Perform case-insensitive match of string against pattern */ +/* + * Perform case-insensitive match of string against pattern + */ + int XX_httplib_match_prefix(const char *pattern, size_t pattern_len, const char *str) { const char *or_str; @@ -37,33 +40,48 @@ int XX_httplib_match_prefix(const char *pattern, size_t pattern_len, const char int len; int res; - if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) { - res = XX_httplib_match_prefix(pattern, (size_t)(or_str - pattern), str); + or_str = (const char *)memchr( pattern, '|', pattern_len ); + if ( or_str != NULL ) { + + res = XX_httplib_match_prefix( pattern, (size_t)(or_str - pattern), str ); return (res > 0) ? res : XX_httplib_match_prefix(or_str + 1, (size_t)((pattern + pattern_len) - (or_str + 1)), str); } - for (i = 0, j = 0; 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 = 0; + j = 0; + + while ( i 0); + res = XX_httplib_match_prefix( pattern + i, pattern_len - i, str + j + len ); + + } while ( res == -1 && len-- > 0 ); + return (res == -1) ? -1 : j + res + len; - } else if (XX_httplib_lowercase(&pattern[i]) != XX_httplib_lowercase(&str[j])) { - return -1; } + + else if ( XX_httplib_lowercase(&pattern[i] ) != XX_httplib_lowercase( &str[j] ) ) return -1; + + i++; + j++; } + return j; } /* XX_httplib_match_prefix */ diff --git a/src/httplib_md5.c b/src/httplib_md5.c index 5996f69a..1647efef 100644 --- a/src/httplib_md5.c +++ b/src/httplib_md5.c @@ -22,42 +22,50 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* Stringify binary data. Output buffer must be twice as big as input, - * because each byte takes 2 bytes in string representation */ +/* + * Stringify binary data. Output buffer must be twice as big as input, + * because each byte takes 2 bytes in string representation + */ + static void bin2str(char *to, const unsigned char *p, size_t len) { static const char *hex = "0123456789abcdef"; for (; len--; p++) { + *to++ = hex[p[0] >> 4]; *to++ = hex[p[0] & 0x0f]; } + *to = '\0'; } /* bin2str */ -/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */ -char * httplib_md5(char buf[33], ...) { +/* + * Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. + */ + +char * httplib_md5( char buf[33], ... ) { 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, 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, strlen( p ) ); + va_end( ap ); - md5_finish(&ctx, hash); - bin2str(buf, hash, sizeof(hash)); + md5_finish( & ctx, hash ); + bin2str( buf, hash, sizeof(hash) ); return buf; } /* httplib_md5 */ diff --git a/src/httplib_mkcol.c b/src/httplib_mkcol.c index 2de859ec..078a41d7 100644 --- a/src/httplib_mkcol.c +++ b/src/httplib_mkcol.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -48,22 +48,32 @@ void XX_httplib_mkcol( struct httplib_connection *conn, const char *path ) { curtime = time( NULL ); - /* TODO (mid): Check the XX_httplib_send_http_error situations in this function */ + /* + * TODO (mid): Check the XX_httplib_send_http_error situations in this function + */ - memset(&de.file, 0, sizeof(de.file)); - if (!XX_httplib_stat(conn, path, &de.file)) { - httplib_cry(conn, "%s: XX_httplib_stat(%s) failed: %s", __func__, path, strerror(ERRNO)); + memset( & de.file, 0, sizeof(de.file) ); + + if ( ! XX_httplib_stat( conn, path, & de.file ) ) { + + httplib_cry( conn, "%s: XX_httplib_stat(%s) failed: %s", __func__, path, strerror(ERRNO) ); } - if (de.file.last_modified) { - /* TODO (high): This check does not seem to make any sense ! */ - XX_httplib_send_http_error( conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO)); + if ( de.file.last_modified ) { + + /* + * TODO (high): This check does not seem to make any sense ! + */ + + XX_httplib_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) { - XX_httplib_send_http_error( conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO)); + + if ( body_len > 0 ) { + + XX_httplib_send_http_error( conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO) ); return; } @@ -72,13 +82,13 @@ void XX_httplib_mkcol( struct httplib_connection *conn, const char *path ) { if ( rc == 0 ) { conn->status_code = 201; - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - httplib_printf(conn, "HTTP/1.1 %d Created\r\n" "Date: %s\r\n", conn->status_code, date); - XX_httplib_send_static_cache_header(conn); - httplib_printf(conn, "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", XX_httplib_suggest_connection_header(conn)); + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); + httplib_printf( conn, "HTTP/1.1 %d Created\r\n" "Date: %s\r\n", conn->status_code, date ); + XX_httplib_send_static_cache_header( conn ); + httplib_printf( conn, "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", XX_httplib_suggest_connection_header(conn) ); } - else if (rc == -1) { + else if ( rc == -1 ) { if ( errno == EEXIST ) XX_httplib_send_http_error( conn, 405, "Error: mkcol(%s): %s", path, strerror( ERRNO ) ); else if ( errno == EACCES ) XX_httplib_send_http_error( conn, 403, "Error: mkcol(%s): %s", path, strerror( ERRNO ) ); diff --git a/src/httplib_modify_passwords_file.c b/src/httplib_modify_passwords_file.c index 35f5310a..b9d92a6a 100644 --- a/src/httplib_modify_passwords_file.c +++ b/src/httplib_modify_passwords_file.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -39,81 +39,118 @@ int httplib_modify_passwords_file( const char *fname, const char *domain, const FILE *fp2; found = 0; - fp = fp2 = NULL; + fp = NULL; + fp2 = 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; - - /* Using the given file format, user name and domain must not contain ':' + /* + * 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; + + /* + * 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; - } + /* + * 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; - /* The maximum length of the path to the password file is limited */ + /* + * The maximum length of the path to the password file is limited + */ + if ((strlen(fname) + 4) >= PATH_MAX) return 0; - /* Create a temporary file name. Length has been checked before. */ + /* + * Create a temporary file name. Length has been checked before. + */ + strcpy(tmp, fname); strcat(tmp, ".tmp"); - /* Create the file if does not exist */ - /* Use of fopen here is OK, since fname is only ASCII */ - if ((fp = fopen(fname, "a+")) != NULL) { (void)fclose(fp); } + /* + * Create the file if does not exist + * Use of fopen here is OK, since fname is only ASCII + */ - /* Open the given file and temporary file */ - if ((fp = fopen(fname, "r")) == NULL) { - return 0; - } else if ((fp2 = fopen(tmp, "w+")) == NULL) { - fclose(fp); + if ( (fp = fopen( fname, "a+" )) != NULL ) 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; } - /* Copy the stuff to temporary file */ - while (fgets(line, sizeof(line), fp) != NULL) { - if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) { - continue; - } + /* + * 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)) { + if ( ! strcmp( u, user ) && ! strcmp( d, domain ) ) { + found++; - if (pass != NULL) { - httplib_md5(ha1, user, ":", domain, ":", pass, NULL); - fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); + if ( pass != NULL ) { + + httplib_md5( ha1, user, ":", domain, ":", pass, NULL ); + fprintf( fp2, "%s:%s:%s\n", user, domain, ha1 ); } - } else { - fprintf(fp2, "%s", line); } + + else fprintf( fp2, "%s", line ); } - /* If new user, just add it */ - if (!found && pass != NULL) { - httplib_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 ) { + + httplib_md5( ha1, user, ":", domain, ":", pass, NULL ); + fprintf( fp2, "%s:%s:%s\n", user, domain, ha1 ); } - /* Close files */ - fclose(fp); - fclose(fp2); + /* + * Close files + */ - /* Put the temp file in place of real file */ - IGNORE_UNUSED_RESULT(remove(fname)); - IGNORE_UNUSED_RESULT(rename(tmp, fname)); + fclose( fp ); + fclose( fp2 ); + + /* + * Put the temp file in place of real file + */ + + remove( fname ); + rename( tmp, fname ); return 1; diff --git a/src/httplib_must_hide_file.c b/src/httplib_must_hide_file.c index bbb3d776..bee73a7e 100644 --- a/src/httplib_must_hide_file.c +++ b/src/httplib_must_hide_file.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -37,10 +37,6 @@ int XX_httplib_must_hide_file( struct httplib_connection *conn, const char *path pw_pattern = "**" PASSWORDS_FILE_NAME "$"; pattern = conn->ctx->config[HIDE_FILES]; - return XX_httplib_match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 - || (pattern != NULL - && XX_httplib_match_prefix(pattern, strlen(pattern), path) > 0); - - return 0; + return XX_httplib_match_prefix( pw_pattern, strlen(pw_pattern), path) > 0 || (pattern != NULL && XX_httplib_match_prefix(pattern, strlen(pattern), path) > 0); } /* XX_httplib_must_hide_file */ diff --git a/src/httplib_next_option.c b/src/httplib_next_option.c index 6a01b7e2..0b542c42 100644 --- a/src/httplib_next_option.c +++ b/src/httplib_next_option.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -43,42 +43,59 @@ const char *XX_httplib_next_option( const char *list, struct vec *val, struct ve reparse: if ( val == NULL || list == NULL || *list == '\0' ) return NULL; - /* Skip over leading LWS */ - while ( *list == ' ' || *list == '\t' ) list++; + /* + * Skip over leading LWS + */ + + while ( *list == ' ' || *list == '\t' ) list++; val->ptr = list; - if ((list = strchr(val->ptr, ',')) != NULL) { - /* Comma found. Store length and shift the list ptr */ + if ( (list = strchr( val->ptr, ',' )) != NULL ) { + + /* + * Comma found. Store length and shift the list ptr + */ + val->len = ((size_t)(list - val->ptr)); list++; } else { - /* This value is the last one */ - list = val->ptr + strlen(val->ptr); + + /* + * This value is the last one + */ + + list = val->ptr + strlen(val->ptr); val->len = ((size_t)(list - val->ptr)); } - /* Adjust length for trailing LWS */ + /* + * Adjust length for trailing LWS + */ + end = (int)val->len - 1; while ( end >= 0 && ( val->ptr[end] == ' ' || val->ptr[end] == '\t' ) ) end--; val->len = (size_t)(end + 1); - if (val->len == 0) { - /* Ignore any empty entries. */ - goto reparse; - } + if ( val->len == 0 ) goto reparse; /* Ignore any empty entries. */ + + 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". + */ - 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 = (const char *)memchr( val->ptr, '=', val->len ); + + if ( eq_val->ptr != NULL ) { + eq_val->ptr++; /* Skip over '=' character */ eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len; - val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1; + val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1; } } diff --git a/src/httplib_parse_auth_header.c b/src/httplib_parse_auth_header.c index 05b2cb5c..43e0a821 100644 --- a/src/httplib_parse_auth_header.c +++ b/src/httplib_parse_auth_header.c @@ -22,13 +22,16 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #include "httplib_string.h" -/* Return 1 on success. Always initializes the ah structure. */ +/* + * Return 1 on success. Always initializes the ah structure. + */ + int XX_httplib_parse_auth_header(struct httplib_connection *conn, char *buf, size_t buf_size, struct ah *ah) { char *name; @@ -37,33 +40,44 @@ int XX_httplib_parse_auth_header(struct httplib_connection *conn, char *buf, siz const char *auth_header; uint64_t nonce; - if (!ah || !conn) return 0; + if ( ah == NULL || conn == NULL ) return 0; - memset(ah, 0, sizeof(*ah)); - if ((auth_header = httplib_get_header(conn, "Authorization")) == NULL || httplib_strncasecmp(auth_header, "Digest ", 7) != 0) return 0; + memset( ah, 0, sizeof(*ah) ); + if ( (auth_header = httplib_get_header( conn, "Authorization" )) == NULL || httplib_strncasecmp( auth_header, "Digest ", 7 ) != 0 ) return 0; + + /* + * Make modifiable copy of the auth header + */ - /* Make modifiable copy of the auth header */ httplib_strlcpy( buf, auth_header + 7, buf_size ); s = buf; - /* Parse authorization header */ + /* + * Parse authorization header + */ + for (;;) { - /* Gobble initial spaces */ - while (isspace(*(unsigned char *)s)) { - s++; - } - name = XX_httplib_skip_quoted(&s, "=", " ", 0); - /* Value is either quote-delimited, or ends at first comma or space. */ + + /* + * Gobble initial spaces + */ + + while ( isspace(*(unsigned char *)s) ) s++; + + name = XX_httplib_skip_quoted( &s, "=", " ", 0 ); + + /* + * Value is either quote-delimited, or ends at first comma or space. + */ + if (s[0] == '\"') { + s++; - value = XX_httplib_skip_quoted(&s, "\"", " ", '\\'); - if (s[0] == ',') { - s++; - } - } else { - value = XX_httplib_skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses - * spaces */ + value = XX_httplib_skip_quoted( &s, "\"", " ", '\\' ); + if (s[0] == ',') s++; } + + else value = XX_httplib_skip_quoted( &s, ", ", " ", 0 ); /* IE uses commas, FF uses spaces */ if (*name == '\0') break; if ( ! strcmp( name, "username" ) ) ah->user = value; @@ -76,43 +90,59 @@ int XX_httplib_parse_auth_header(struct httplib_connection *conn, char *buf, siz } #ifndef NO_NONCE_CHECK - /* Read the nonce from the response. */ - if (ah->nonce == NULL) return 0; - s = NULL; - nonce = strtoull(ah->nonce, &s, 10); - if ((s == NULL) || (*s != 0)) { - return 0; - } - /* Convert the nonce from the client to a number. */ + /* + * Read the nonce from the response. + */ + + if ( ah->nonce == NULL ) return 0; + s = NULL; + nonce = strtoull( ah->nonce, &s, 10 ) + ; + if ( s == NULL || *s != 0 ) return 0; + + /* + * Convert the nonce from the client to a number. + */ + nonce ^= conn->ctx->auth_nonce_mask; - /* The converted number corresponds to the time the nounce has been - * created. This should not be earlier than the server start. */ - /* Server side nonce check is valuable in all situations but one: + /* + * The converted number corresponds to the time the nounce has been + * created. This should not be earlier than the server start. + * 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. */ - /* However, the reasonable default is to not accept a nonce from a + * that, so the server should accept nonces from previous starts. + * However, the reasonable default is to not accept a nonce from a * previous start, so if anyone changed the access rights between - * two restarts, a new login is required. */ - if (nonce < (uint64_t)conn->ctx->start_time) { - /* nonce is from a previous start of the server and no longer valid - * (replay attack?) */ + * two restarts, a new login is required. + */ + + if ( nonce < (uint64_t)conn->ctx->start_time ) { + + /* + * nonce is from a previous start of the server and no longer valid + * (replay attack?) + */ + return 0; } + /* Check if the nonce is too high, so it has not (yet) been used by the - * server. */ - if (nonce >= ((uint64_t)conn->ctx->start_time + conn->ctx->nonce_count)) { - return 0; - } + * server. + */ + + if ( nonce >= ( (uint64_t)conn->ctx->start_time + conn->ctx->nonce_count ) ) return 0; #else - (void)nonce; + UNUSED_PARAMETER(nonce); #endif - /* CGI needs it as REMOTE_USER */ - if (ah->user != NULL) { - conn->request_info.remote_user = XX_httplib_strdup(ah->user); - } else return 0; + /* + * CGI needs it as REMOTE_USER + */ + + if ( ah->user != NULL ) conn->request_info.remote_user = XX_httplib_strdup( ah->user ); + else return 0; return 1; diff --git a/src/httplib_parse_http_headers.c b/src/httplib_parse_http_headers.c index 98756214..68cfccb2 100644 --- a/src/httplib_parse_http_headers.c +++ b/src/httplib_parse_http_headers.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -42,51 +42,60 @@ int XX_httplib_parse_http_headers( char **buf, struct httplib_request_info *ri ) ri->num_headers = 0; - for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) { - char *dp = *buf; - while ((*dp != ':') && (*dp >= 33) && (*dp <= 126)) { - dp++; - } - if (dp == *buf) { - /* End of headers reached. */ - break; - } - if (*dp != ':') { - /* This is not a valid field. */ - return -1; - } + for (i=0; i<(int)ARRAY_SIZE(ri->http_headers); i++) { + + char *dp = *buf; + + while ( *dp != ':' && *dp >= 33 && *dp <= 126 ) dp++; + + if ( dp == *buf ) break; /* End of headers reached. */ + if ( *dp != ':' ) return -1; /* This is not a valid field. */ + + /* + * End of header key (*dp == ':') + * Truncate here and set the key name + */ - /* End of header key (*dp == ':') */ - /* Truncate here and set the key name */ *dp = 0; ri->http_headers[i].name = *buf; do { dp++; } while (*dp == ' '); - /* The rest of the line is the value */ + /* + * The rest of the line is the value + */ + ri->http_headers[i].value = dp; - *buf = dp + strcspn(dp, "\r\n"); - if (((*buf)[0] != '\r') || ((*buf)[1] != '\n')) { - *buf = NULL; - } + *buf = dp + strcspn(dp, "\r\n"); + + if ( (*buf)[0] != '\r' || (*buf)[1] != '\n' ) *buf = NULL; - ri->num_headers = i + 1; - if (*buf) { + ri->num_headers = i+1; + + if ( *buf ) { + (*buf)[0] = 0; (*buf)[1] = 0; - *buf += 2; - } else { + *buf += 2; + } + + else { *buf = dp; break; } - if ((*buf)[0] == '\r') { - /* This is the end of the header */ + if ( (*buf)[0] == '\r' ) { + + /* + * This is the end of the header + */ + break; } } + return ri->num_headers; } /* XX_httplib_parse_http_headers */ diff --git a/src/httplib_parse_http_message.c b/src/httplib_parse_http_message.c index e6293db3..aacebd2e 100644 --- a/src/httplib_parse_http_message.c +++ b/src/httplib_parse_http_message.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -48,11 +48,15 @@ int XX_httplib_parse_http_message( char *buf, int len, struct httplib_request_in int request_length; char *start_line; - request_length = XX_httplib_get_request_len(buf, len); + request_length = XX_httplib_get_request_len( buf, len ); + + if ( request_length > 0 ) { + + /* + * Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr, + * remote_port + */ - if (request_length > 0) { - /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr, - * remote_port */ ri->remote_user = NULL; ri->request_method = NULL; ri->request_uri = NULL; @@ -61,33 +65,48 @@ int XX_httplib_parse_http_message( char *buf, int len, struct httplib_request_in buf[request_length - 1] = '\0'; - /* RFC says that all initial whitespaces should be ingored */ - while (*buf != '\0' && isspace(*(unsigned char *)buf)) { - buf++; - } + /* + * RFC says that all initial whitespaces should be ingored + */ + + while (*buf != '\0' && isspace( *(unsigned char *)buf) ) buf++; + start_line = XX_httplib_skip( &buf, "\r\n" ); ri->request_method = XX_httplib_skip( &start_line, " " ); ri->request_uri = XX_httplib_skip( &start_line, " " ); ri->http_version = start_line; - /* HTTP message could be either HTTP request: + /* + * HTTP message could be either HTTP request: * "GET / HTTP/1.0 ..." * or a HTTP response: * "HTTP/1.0 200 OK ..." * otherwise it is invalid. */ - is_request = XX_httplib_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)) { - /* Not a valid request or response: invalid */ + + is_request = XX_httplib_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 ) ) { + + /* + * Not a valid request or response: invalid + */ + return -1; } - if (is_request) ri->http_version += 5; - if (XX_httplib_parse_http_headers(&buf, ri) < 0) { - /* Error while parsing headers */ + + if ( is_request ) ri->http_version += 5; + if ( XX_httplib_parse_http_headers( &buf, ri ) < 0 ) { + + /* + * Error while parsing headers + */ + return -1; } } + return request_length; } /* XX_httplib_parse_http_message */ diff --git a/src/httplib_path_to_unicode.c b/src/httplib_path_to_unicode.c index 95b0ab11..7fc3c42b 100644 --- a/src/httplib_path_to_unicode.c +++ b/src/httplib_path_to_unicode.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -30,22 +30,26 @@ #if defined(_WIN32) -/* For Windows, change all slashes to backslashes in path names. */ +/* + * For Windows, change all slashes to backslashes in path names. + */ + static void change_slashes_to_backslashes( char *path ) { int i; 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] == '/') { - memmove(path + i + 1, path + i + 2, strlen(path + i + 1)); - } + 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] == '/' ) memmove( path+i+1, path+i+2, strlen( path+i+1 ) ); } } @@ -61,7 +65,8 @@ static int httplib_wcscasecmp( const wchar_t *s1, const wchar_t *s2 ) { diff = tolower(*s1) - tolower(*s2); s1++; s2++; - } while (diff == 0 && s1[-1] != '\0'); + + } while ( diff == 0 && s1[-1] != '\0' ); return diff; @@ -82,16 +87,22 @@ void XX_httplib_path_to_unicode( const char *path, wchar_t *wbuf, size_t wbuf_le XX_httplib_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); + /* + * 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'; - /* TODO: Add a configuration to switch between case sensitive and - * case insensitive URIs for Windows server. */ + /* + * TODO: Add a configuration to switch between case sensitive and + * case insensitive URIs for Windows server. + */ + /* if (conn) { if (conn->ctx->config[WINDOWS_CASE_SENSITIVE]) { @@ -101,26 +112,39 @@ void XX_httplib_path_to_unicode( const char *path, wchar_t *wbuf, size_t wbuf_le */ #if !defined(_WIN32_WCE) - /* Only accept a full file path, not a Windows short (8.3) path. */ - memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t)); - long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1); - if (long_len == 0) { + /* + * Only accept a full file path, not a Windows short (8.3) path. + */ + + memset( wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t) ); + long_len = GetLongPathNameW( wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1 ); + + if ( long_len == 0 ) { + err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) { - /* File does not exist. This is not always a problem here. */ + if ( err == ERROR_FILE_NOT_FOUND ) { + + /* + * File does not exist. This is not always a problem here. + */ + return; } } - if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) { - /* Short name is used. */ + if ( long_len >= ARRAY_SIZE(wbuf2) || fcompare( wbuf, wbuf2 ) != 0 ) { + + /* + * Short name is used. + */ + wbuf[0] = L'\0'; } #else - (void)long_len; - (void)wbuf2; - (void)err; + UNUSED_PARAMETER(long_len); + UNUSED_PARAMETER(wbuf2); + UNUSED_PARAMETER(err); - if (strchr(path, '~')) wbuf[0] = L'\0'; + if ( strchr( path, '~' ) ) wbuf[0] = L'\0'; #endif } /* XX_httplib_path_to_unicode */ diff --git a/src/httplib_prepare_cgi_environment.c b/src/httplib_prepare_cgi_environment.c index 4bbba523..5f22079a 100644 --- a/src/httplib_prepare_cgi_environment.c +++ b/src/httplib_prepare_cgi_environment.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -65,21 +65,23 @@ void XX_httplib_prepare_cgi_environment( struct httplib_connection *conn, const XX_httplib_addenv( env, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT] ); XX_httplib_addenv( env, "SERVER_SOFTWARE=%s/%s", "LibHTTP", httplib_version() ); - /* Prepare the environment block */ + /* + * Prepare the environment block + */ + XX_httplib_addenv( env, "%s", "GATEWAY_INTERFACE=CGI/1.1" ); XX_httplib_addenv( env, "%s", "SERVER_PROTOCOL=HTTP/1.1" ); XX_httplib_addenv( env, "%s", "REDIRECT_STATUS=200" ); /* For PHP */ #if defined(USE_IPV6) - if (conn->client.lsa.sa.sa_family == AF_INET6) { - XX_httplib_addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port)); - } else + if ( conn->client.lsa.sa.sa_family == AF_INET6 ) XX_httplib_addenv( env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port) ); + else #endif { - XX_httplib_addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port)); + XX_httplib_addenv( env, "SERVER_PORT=%d", ntohs( conn->client.lsa.sin.sin_port ) ); } - XX_httplib_sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); + XX_httplib_sockaddr_to_string( src_addr, sizeof(src_addr), &conn->client.rsa ); XX_httplib_addenv( env, "REMOTE_ADDR=%s", src_addr ); XX_httplib_addenv( env, "REQUEST_METHOD=%s", conn->request_info.request_method ); @@ -87,14 +89,13 @@ void XX_httplib_prepare_cgi_environment( struct httplib_connection *conn, const XX_httplib_addenv( env, "REQUEST_URI=%s", conn->request_info.request_uri ); XX_httplib_addenv( env, "LOCAL_URI=%s", conn->request_info.local_uri ); - /* SCRIPT_NAME */ - XX_httplib_addenv(env, - "SCRIPT_NAME=%.*s", - (int)strlen(conn->request_info.local_uri) - - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)), - conn->request_info.local_uri); + /* + * SCRIPT_NAME + */ - XX_httplib_addenv(env, "SCRIPT_FILENAME=%s", prog); + XX_httplib_addenv( env, "SCRIPT_NAME=%.*s", (int)strlen(conn->request_info.local_uri) - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)), conn->request_info.local_uri); + + XX_httplib_addenv( env, "SCRIPT_FILENAME=%s", prog ); if ( conn->path_info == NULL ) XX_httplib_addenv( env, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT] ); else XX_httplib_addenv( env, "PATH_TRANSLATED=%s%s", conn->ctx->config[DOCUMENT_ROOT], conn->path_info ); @@ -102,14 +103,18 @@ void XX_httplib_prepare_cgi_environment( struct httplib_connection *conn, const XX_httplib_addenv(env, "HTTPS=%s", (conn->ssl == NULL) ? "off" : "on"); if ( (s = httplib_get_header( conn, "Content-Type" ) ) != NULL ) XX_httplib_addenv( env, "CONTENT_TYPE=%s", s ); - if ( conn->request_info.query_string != NULL ) XX_httplib_addenv( env, "QUERY_STRING=%s", conn->request_info.query_string ); + if ( conn->request_info.query_string != NULL ) XX_httplib_addenv( env, "QUERY_STRING=%s", conn->request_info.query_string ); if ( (s = httplib_get_header( conn, "Content-Length" ) ) != NULL ) XX_httplib_addenv( env, "CONTENT_LENGTH=%s", s ); - if ( (s = getenv( "PATH" )) != NULL ) XX_httplib_addenv( env, "PATH=%s", s ); - if ( conn->path_info != NULL ) XX_httplib_addenv( env, "PATH_INFO=%s", conn->path_info ); + if ( (s = getenv( "PATH" )) != NULL ) XX_httplib_addenv( env, "PATH=%s", s ); + if ( conn->path_info != NULL ) XX_httplib_addenv( env, "PATH_INFO=%s", conn->path_info ); if (conn->status_code > 0) { - /* CGI error handler should show the status code */ - XX_httplib_addenv(env, "STATUS=%d", conn->status_code); + + /* + * CGI error handler should show the status code + */ + + XX_httplib_addenv( env, "STATUS=%d", conn->status_code ); } #if defined(_WIN32) @@ -125,33 +130,46 @@ void XX_httplib_prepare_cgi_environment( struct httplib_connection *conn, const if ( (s = getenv("PERLLIB" )) != NULL ) XX_httplib_addenv( env, "PERLLIB=%s", s ); if (conn->request_info.remote_user != NULL) { - XX_httplib_addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user); - XX_httplib_addenv(env, "%s", "AUTH_TYPE=Digest"); + + XX_httplib_addenv( env, "REMOTE_USER=%s", conn->request_info.remote_user ); + XX_httplib_addenv( env, "%s", "AUTH_TYPE=Digest" ); } - /* Add all headers as HTTP_* variables */ - for (i = 0; i < conn->request_info.num_headers; i++) { + /* + * Add all headers as HTTP_* variables + */ - XX_httplib_snprintf(conn, &truncated, http_var_name, sizeof(http_var_name), "HTTP_%s", conn->request_info.http_headers[i].name); + for (i=0; irequest_info.num_headers; i++) { - if (truncated) { - httplib_cry(conn, "%s: HTTP header variable too long [%s]", __func__, conn->request_info.http_headers[i].name); + XX_httplib_snprintf( conn, & truncated, http_var_name, sizeof(http_var_name), "HTTP_%s", conn->request_info.http_headers[i].name ); + + if ( truncated ) { + httplib_cry( conn, "%s: HTTP header variable too long [%s]", __func__, conn->request_info.http_headers[i].name ); continue; } - /* Convert variable name into uppercase, and change - to _ */ - for (p = http_var_name; *p != '\0'; p++) { + /* + * Convert variable name into uppercase, and change - to _ + */ + + for (p=http_var_name; *p != '\0'; p++) { + if (*p == '-') *p = '_'; *p = (char)toupper(*(unsigned char *)p); } - XX_httplib_addenv(env, "%s=%s", http_var_name, conn->request_info.http_headers[i].value); + XX_httplib_addenv( env, "%s=%s", http_var_name, conn->request_info.http_headers[i].value ); } - /* Add user-specified variables */ + /* + * Add user-specified variables + */ + s = conn->ctx->config[CGI_ENVIRONMENT]; - while ((s = XX_httplib_next_option(s, &var_vec, NULL)) != NULL) { - XX_httplib_addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr); + + while ( (s = XX_httplib_next_option( s, &var_vec, NULL )) != NULL ) { + + XX_httplib_addenv( env, "%.*s", (int)var_vec.len, var_vec.ptr ); } env->var[env->varused] = NULL; diff --git a/src/httplib_print_dir_entry.c b/src/httplib_print_dir_entry.c index a824c042..c11443d4 100644 --- a/src/httplib_print_dir_entry.c +++ b/src/httplib_print_dir_entry.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -53,16 +53,16 @@ void XX_httplib_print_dir_entry( struct de *de ) { * So, string truncation checks are not required here. */ - tm = localtime(&de->file.last_modified); + tm = localtime( &de->file.last_modified ); if ( tm != NULL ) strftime( mod, sizeof(mod), "%d-%b-%Y %H:%M", tm ); else { httplib_strlcpy( mod, "01-Jan-1970 00:00", sizeof(mod) ); mod[sizeof(mod) - 1] = '\0'; } - httplib_url_encode(de->file_name, href, sizeof(href)); - de->conn->num_bytes_sent += - httplib_printf(de->conn, + + httplib_url_encode( de->file_name, href, sizeof(href) ); + de->conn->num_bytes_sent += httplib_printf( de->conn, "%s%s" " %s  %s\n", de->conn->request_info.local_uri, @@ -71,6 +71,6 @@ void XX_httplib_print_dir_entry( struct de *de ) { de->file_name, de->file.is_directory ? "/" : "", mod, - size); + size ); } /* XX_httplib_print_dir_entry */ diff --git a/src/httplib_process_new_connection.c b/src/httplib_process_new_connection.c index 3e4ffaa7..6ff0e97e 100644 --- a/src/httplib_process_new_connection.c +++ b/src/httplib_process_new_connection.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -52,99 +52,139 @@ void XX_httplib_process_new_connection( struct httplib_connection *conn ) { ri = & conn->request_info; keep_alive_enabled = ! strcmp( conn->ctx->config[ENABLE_KEEP_ALIVE], "yes" ); - /* Important: on new connection, reset the receiving buffer. Credit - * goes to crule42. */ + /* + * Important: on new connection, reset the receiving buffer. Credit + * goes to crule42. + */ + conn->data_len = 0; do { - if (!XX_httplib_getreq(conn, ebuf, sizeof(ebuf), &reqerr)) { - /* The request sent by the client could not be understood by - * the server, or it was incomplete or a timeout. Send an - * error message and close the connection. */ - if (reqerr > 0) { - /*assert(ebuf[0] != '\0');*/ - XX_httplib_send_http_error(conn, reqerr, "%s", ebuf); - } - } else if (strcmp(ri->http_version, "1.0") && strcmp(ri->http_version, "1.1")) { + if ( ! XX_httplib_getreq( conn, ebuf, sizeof(ebuf), &reqerr ) ) { - XX_httplib_snprintf(conn, NULL, ebuf, sizeof(ebuf), "Bad HTTP version: [%s]", ri->http_version); - XX_httplib_send_http_error(conn, 505, "%s", ebuf); + /* + * The request sent by the client could not be understood by + * the server, or it was incomplete or a timeout. Send an + * error message and close the connection. + */ + + if ( reqerr > 0 ) { + /*assert(ebuf[0] != '\0');*/ + XX_httplib_send_http_error( conn, reqerr, "%s", ebuf ); + } + } + + else if ( strcmp( ri->http_version, "1.0" ) && strcmp( ri->http_version, "1.1" ) ) { + + XX_httplib_snprintf( conn, NULL, ebuf, sizeof(ebuf), "Bad HTTP version: [%s]", ri->http_version ); + XX_httplib_send_http_error( conn, 505, "%s", ebuf ); } - if (ebuf[0] == '\0') { - uri_type = XX_httplib_get_uri_type(conn->request_info.request_uri); - switch (uri_type) { - case 1: - /* Asterisk */ - conn->request_info.local_uri = NULL; - break; - case 2: - /* relative uri */ - conn->request_info.local_uri = - conn->request_info.request_uri; - break; - case 3: - case 4: - /* absolute uri (with/without port) */ - hostend = XX_httplib_get_rel_url_at_current_server( conn->request_info.request_uri, conn); + if ( ebuf[0] == '\0' ) { - if (hostend) conn->request_info.local_uri = hostend; - else conn->request_info.local_uri = NULL; - break; - default: - XX_httplib_snprintf(conn, NULL, ebuf, sizeof(ebuf), "Invalid URI"); - XX_httplib_send_http_error(conn, 400, "%s", ebuf); - conn->request_info.local_uri = NULL; - break; + uri_type = XX_httplib_get_uri_type( conn->request_info.request_uri ); + + switch ( uri_type ) { + + case 1 : + /* + * Asterisk + */ + + conn->request_info.local_uri = NULL; + break; + + case 2 : + /* + * relative uri + */ + + conn->request_info.local_uri = conn->request_info.request_uri; + break; + case 3 : + case 4 : + /* + * absolute uri (with/without port) + */ + + hostend = XX_httplib_get_rel_url_at_current_server( conn->request_info.request_uri, conn ); + + if (hostend) conn->request_info.local_uri = hostend; + else conn->request_info.local_uri = NULL; + break; + + default : + XX_httplib_snprintf( conn, NULL, ebuf, sizeof(ebuf), "Invalid URI" ); + XX_httplib_send_http_error( conn, 400, "%s", ebuf ); + conn->request_info.local_uri = NULL; + break; } - /* TODO: cleanup uri, local_uri and request_uri */ + /* + * TODO: cleanup uri, local_uri and request_uri + */ conn->request_info.uri = conn->request_info.local_uri; } - if (ebuf[0] == '\0') { - if (conn->request_info.local_uri) { - /* handle request to local server */ - XX_httplib_handle_request(conn); + if ( ebuf[0] == '\0' ) { + + if ( conn->request_info.local_uri ) { + + /* + * handle request to local server + */ + + XX_httplib_handle_request( conn ); if (conn->ctx->callbacks.end_request != NULL) conn->ctx->callbacks.end_request(conn, conn->status_code); XX_httplib_log_access(conn); - } else { - /* TODO: handle non-local request (PROXY) */ + } + + else { + /* + * TODO: handle non-local request (PROXY) + */ conn->must_close = 1; } - } else conn->must_close = 1; + } + + else conn->must_close = 1; + + if ( ri->remote_user != NULL ) { - if (ri->remote_user != NULL) { httplib_free( (void *) ri->remote_user ); - /* Important! When having connections with and without auth - * would cause double free and then crash */ + + /* + * Important! When having connections with and without auth + * would cause double free and then crash + */ + ri->remote_user = NULL; } - /* NOTE(lsm): order is important here. XX_httplib_should_keep_alive() call - * is - * using parsed request, which will be invalid after memmove's - * below. - * Therefore, memorize XX_httplib_should_keep_alive() result now for later - * use - * in loop exit condition. */ - keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled && conn->content_len >= 0 && XX_httplib_should_keep_alive(conn); + /* + * NOTE(lsm): order is important here. XX_httplib_should_keep_alive() call is + * using parsed request, which will be invalid after memmove's below. + * Therefore, memorize XX_httplib_should_keep_alive() result now for later use + * in loop exit condition. + */ + + keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled && conn->content_len >= 0 && XX_httplib_should_keep_alive( conn ); + + /* + * Discard all buffered data for this request + */ - /* Discard all buffered data for this request */ discard_len = ((conn->content_len >= 0) && (conn->request_len > 0) && ((conn->request_len + conn->content_len) < (int64_t)conn->data_len)) ? (int)(conn->request_len + conn->content_len) : conn->data_len; - /*assert(discard_len >= 0);*/ - if (discard_len < 0) break; + + if ( discard_len < 0 ) break; conn->data_len -= discard_len; - if (conn->data_len > 0) memmove(conn->buf, conn->buf + discard_len, (size_t)conn->data_len); + if ( conn->data_len > 0 ) memmove( conn->buf, conn->buf + discard_len, (size_t)conn->data_len ); - /* assert(conn->data_len >= 0); */ - /* assert(conn->data_len <= conn->buf_size); */ + if ( conn->data_len < 0 || conn->data_len > conn->buf_size ) break; - if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) break; - - } while (keep_alive); + } while ( keep_alive ); } /* XX_httplib_process_new_connection */ diff --git a/src/httplib_produce_socket.c b/src/httplib_produce_socket.c index 56a5074d..7dd40a8b 100644 --- a/src/httplib_produce_socket.c +++ b/src/httplib_produce_socket.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -41,24 +41,39 @@ void XX_httplib_produce_socket( struct httplib_context *ctx, const struct socket unsigned int i; for (;;) { - for (i = 0; i < ctx->cfg_worker_threads; i++) { - /* find a free worker slot and signal it */ - if (ctx->client_socks[i].in_use == 0) { - ctx->client_socks[i] = *sp; + + for (i=0; icfg_worker_threads; i++) { + + /* + * find a free worker slot and signal it + */ + + if ( ctx->client_socks[i].in_use == 0 ) { + + ctx->client_socks[i] = *sp; ctx->client_socks[i].in_use = 1; + event_signal(ctx->client_wait_events[i]); + return; } } - /* queue is full */ - httplib_sleep(1); + + /* + * queue is full + */ + + httplib_sleep( 1 ); } } /* XX_httplib_produce_socket */ #else /* ALTERNATIVE_QUEUE */ -/* Master thread adds accepted socket to a queue */ +/* + * Master thread adds accepted socket to a queue + */ + void XX_httplib_produce_socket(struct httplib_context *ctx, const struct socket *sp) { #define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue))) @@ -67,21 +82,27 @@ void XX_httplib_produce_socket(struct httplib_context *ctx, const struct socket httplib_pthread_mutex_lock( & ctx->thread_mutex ); - /* If the queue is full, wait */ - while (ctx->stop_flag == 0 && ctx->sq_head - ctx->sq_tail >= QUEUE_SIZE(ctx)) { + /* + * If the queue is full, wait + */ - httplib_pthread_cond_wait( & ctx->sq_empty, & ctx->thread_mutex ); + while ( ctx->stop_flag == 0 && ctx->sq_head-ctx->sq_tail >= QUEUE_SIZE(ctx) ) { + + httplib_pthread_cond_wait( & ctx->sq_empty, & ctx->thread_mutex ); } - if (ctx->sq_head - ctx->sq_tail < QUEUE_SIZE(ctx)) { - /* Copy socket to the queue and increment head */ + if ( ctx->sq_head - ctx->sq_tail < QUEUE_SIZE(ctx) ) { + + /* + * Copy socket to the queue and increment head + */ + ctx->queue[ctx->sq_head % QUEUE_SIZE(ctx)] = *sp; ctx->sq_head++; } httplib_pthread_cond_signal( & ctx->sq_full ); httplib_pthread_mutex_unlock( & ctx->thread_mutex ); -#undef QUEUE_SIZE } /* XX_httplib_produce_socket */ diff --git a/src/httplib_pull.c b/src/httplib_pull.c index d0e53cfb..24babd87 100644 --- a/src/httplib_pull.c +++ b/src/httplib_pull.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.9 + * Release: 2.0 */ #include "httplib_main.h" @@ -84,9 +84,10 @@ int XX_httplib_pull( FILE *fp, struct httplib_connection *conn, char *buf, int l if (nread <= 0) { - err = SSL_get_error(conn->ssl, nread); - if ( err == SSL_ERROR_SYSCALL && (nread == -1)) err = ERRNO; - else if ( err == SSL_ERROR_WANT_READ || (err == SSL_ERROR_WANT_WRITE)) nread = 0; + err = SSL_get_error( conn->ssl, nread ); + + if ( err == SSL_ERROR_SYSCALL && nread == -1 ) err = ERRNO; + else if ( err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE ) nread = 0; else return -1; } @@ -96,49 +97,77 @@ int XX_httplib_pull( FILE *fp, struct httplib_connection *conn, char *buf, int l } else { - nread = (int)recv(conn->client.sock, buf, (len_t)len, 0); + nread = (int)recv( conn->client.sock, buf, (len_t)len, 0 ); err = (nread < 0) ? ERRNO : 0; if (nread == 0) return -1; /* shutdown of the socket at client side */ } if ( conn->ctx->stop_flag ) return -1; - if ((nread > 0) || (nread == 0 && len == 0)) { - /* some data has been read, or no data was requested */ + if ( nread > 0 || (nread == 0 && len == 0) ) { + + /* + * some data has been read, or no data was requested + */ + return nread; } if (nread < 0) { -/* socket error - check errno */ + + /* + * socket error - check errno + */ #ifdef _WIN32 - if (err == WSAEWOULDBLOCK) { - /* standard case if called from close_socket_gracefully */ + if ( err == WSAEWOULDBLOCK ) { + + /* + * standard case if called from close_socket_gracefully + */ + return -1; - } else if (err == WSAETIMEDOUT) { - /* timeout is handled by the while loop */ - } else return -1; + } + + else if ( err == WSAETIMEDOUT ) { + + /* + * timeout is handled by the while loop + */ + } + + else return -1; #else - /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases, + /* + * TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases, * if the timeout is reached and if the socket was set to non- * blocking in close_socket_gracefully, so we can not distinguish * here. We have to wait for the timeout in both cases for now. */ - if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) { - /* EAGAIN/EWOULDBLOCK: - * standard case if called from close_socket_gracefully - * => should return -1 */ - /* or timeout occured - * => the code must stay in the while loop */ - /* EINTR can be generated on a socket with a timeout set even + if ( err == EAGAIN || err == EWOULDBLOCK || err == EINTR ) { + + /* + * EAGAIN/EWOULDBLOCK: + * standard case if called from close_socket_gracefully + * => should return -1 + * or timeout occured + * => the code must stay in the while loop + * + * EINTR can be generated on a socket with a timeout set even * when SA_RESTART is effective for all relevant signals * (see signal(7)). - * => stay in the while loop */ - } else return -1; + * => stay in the while loop + */ + + } + + else return -1; #endif } - if (timeout > 0) clock_gettime(CLOCK_MONOTONIC, &now); - } while ((timeout <= 0) || (XX_httplib_difftimespec(&now, &start) <= timeout)); + + if ( timeout > 0 ) clock_gettime( CLOCK_MONOTONIC, &now ); + + } while ( timeout <= 0 || XX_httplib_difftimespec( & now, & start ) <= timeout ); /* * Timeout occured, but no data available. diff --git a/src/httplib_pull_all.c b/src/httplib_pull_all.c index b8f2a086..54259380 100644 --- a/src/httplib_pull_all.c +++ b/src/httplib_pull_all.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -30,26 +30,32 @@ int XX_httplib_pull_all( FILE *fp, struct httplib_connection *conn, char *buf, int len ) { int n; - int nread = 0; - double timeout = -1.0; + int nread; + double timeout; - if (conn->ctx->config[REQUEST_TIMEOUT]) { - timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; - } + if ( conn == NULL || conn->ctx == NULL ) return 0; - while (len > 0 && conn->ctx->stop_flag == 0) { - n = XX_httplib_pull(fp, conn, buf + nread, len, timeout); - if (n < 0) { - if (nread == 0) { - nread = n; /* Propagate the error */ - } + nread = 0; + timeout = -1.0; + + if ( conn->ctx->config[REQUEST_TIMEOUT] != NULL ) timeout = atof( conn->ctx->config[REQUEST_TIMEOUT] ) / 1000.0; + + while ( len > 0 && conn->ctx->stop_flag == 0 ) { + + n = XX_httplib_pull( fp, conn, buf + nread, len, timeout ); + + if ( n < 0 ) { + + if ( nread == 0 ) nread = n; /* Propagate the error */ break; - } else if (n == 0) { - break; /* No more data to read */ - } else { + } + + else if (n == 0) break; /* No more data to read */ + + else { conn->consumed_content += n; - nread += n; - len -= n; + nread += n; + len -= n; } } diff --git a/src/httplib_put_file.c b/src/httplib_put_file.c index 0263f7bd..2ff010f6 100644 --- a/src/httplib_put_file.c +++ b/src/httplib_put_file.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -51,94 +51,151 @@ void XX_httplib_put_file( struct httplib_connection *conn, const char *path ) { curtime = time( NULL ); - if (XX_httplib_stat(conn, path, &file)) { - /* File already exists */ + if ( XX_httplib_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. */ + 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.membuf != NULL) { - /* This is an "in-memory" file, that can not be replaced */ - XX_httplib_send_http_error( conn, 405, "Error: Put not possible\nReplacing %s is not supported", path); + if ( file.membuf != NULL ) { + + /* + * This is an "in-memory" file, that can not be replaced + */ + + XX_httplib_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 */ + /* + * Check if the server may write this file + */ + + if ( access( path, W_OK ) == 0 ) { + + /* + * Access granted + */ + conn->status_code = 200; - rc = 1; - } else { - XX_httplib_send_http_error( conn, 403, "Error: Put not possible\nReplacing %s is not allowed", path); + rc = 1; + } + + else { + XX_httplib_send_http_error( conn, 403, "Error: Put not possible\nReplacing %s is not allowed", path ); return; } } - } else { - /* File should be created */ + } + + else { + /* + * File should be created + */ + conn->status_code = 201; - rc = XX_httplib_put_dir(conn, path); + rc = XX_httplib_put_dir( conn, path ); } - if (rc == 0) { - /* XX_httplib_put_dir returns 0 if path is a directory */ - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - httplib_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, httplib_get_response_code_text(NULL, conn->status_code)); - XX_httplib_send_no_cache_header(conn); - httplib_printf(conn, "Date: %s\r\n" "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", date, XX_httplib_suggest_connection_header(conn)); + if ( rc == 0 ) { + + /* + * XX_httplib_put_dir returns 0 if path is a direct ory + */ + + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); + httplib_printf( conn, "HTTP/1.1 %d %s\r\n", conn->status_code, httplib_get_response_code_text( NULL, conn->status_code ) ); + XX_httplib_send_no_cache_header( conn ); + httplib_printf( conn, "Date: %s\r\n" "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", date, XX_httplib_suggest_connection_header( conn ) ); + + /* + * Request to create a directory has been fulfilled successfully. + * No need to put a file. + */ - /* Request to create a directory has been fulfilled successfully. - * No need to put a file. */ return; } - if (rc == -1) { - /* XX_httplib_put_dir returns -1 if the path is too long */ - XX_httplib_send_http_error(conn, 414, "Error: Path too long\nput_dir(%s): %s", path, strerror(ERRNO)); + if ( rc == -1 ) { + + /* + * XX_httplib_put_dir returns -1 if the path is too long + */ + + XX_httplib_send_http_error( conn, 414, "Error: Path too long\nput_dir(%s): %s", path, strerror(ERRNO) ); return; } - if (rc == -2) { - /* XX_httplib_put_dir returns -2 if the directory can not be created */ - XX_httplib_send_http_error(conn, 500, "Error: Can not create directory\nput_dir(%s): %s", path, strerror(ERRNO)); + if ( rc == -2 ) { + + /* + * XX_httplib_put_dir returns -2 if the directory can not be created + */ + + XX_httplib_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 (!XX_httplib_fopen(conn, path, "wb+", &file) || file.fp == NULL) { - XX_httplib_fclose(&file); - XX_httplib_send_http_error(conn, 500, "Error: Can not create file\nfopen(%s): %s", path, strerror(ERRNO)); + /* + * A file should be created or overwritten. + */ + + if ( ! XX_httplib_fopen( conn, path, "wb+", &file) || file.fp == NULL ) { + + XX_httplib_fclose( & file ); + XX_httplib_send_http_error( conn, 500, "Error: Can not create file\nfopen(%s): %s", path, strerror(ERRNO) ); return; } - XX_httplib_fclose_on_exec(&file, conn); - range = httplib_get_header(conn, "Content-Range"); - r1 = r2 = 0; - if (range != NULL && XX_httplib_parse_range_header(range, &r1, &r2) > 0) { + XX_httplib_fclose_on_exec( & file, conn ); + + range = httplib_get_header( conn, "Content-Range" ); + r1 = 0; + r2 = 0; + + if ( range != NULL && XX_httplib_parse_range_header( range, &r1, &r2 ) > 0 ) { + conn->status_code = 206; /* Partial content */ - fseeko(file.fp, r1, SEEK_SET); + fseeko( file.fp, r1, SEEK_SET ); } - if (!XX_httplib_forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { - /* XX_httplib_forward_body_data failed. + if ( ! XX_httplib_forward_body_data( conn, file.fp, INVALID_SOCKET, NULL ) ) { + + /* + * XX_httplib_forward_body_data failed. * The error code has already been sent to the client, - * and conn->status_code is already set. */ - XX_httplib_fclose(&file); + * and conn->status_code is already set. + */ + + XX_httplib_fclose( & file ); return; } - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - httplib_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, httplib_get_response_code_text(NULL, conn->status_code)); - XX_httplib_send_no_cache_header(conn); - httplib_printf(conn, "Date: %s\r\n" "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", date, XX_httplib_suggest_connection_header(conn)); + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); + httplib_printf( conn, "HTTP/1.1 %d %s\r\n", conn->status_code, httplib_get_response_code_text( NULL, conn->status_code ) ); + XX_httplib_send_no_cache_header( conn ); + httplib_printf( conn, "Date: %s\r\n" "Content-Length: 0\r\n" "Connection: %s\r\n\r\n", date, XX_httplib_suggest_connection_header( conn ) ); - XX_httplib_fclose(&file); + XX_httplib_fclose( & file ); } /* XX_httplib_put_file */ diff --git a/src/httplib_read.c b/src/httplib_read.c index 22b67012..1af8f420 100644 --- a/src/httplib_read.c +++ b/src/httplib_read.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.9 + * Release: 2.0 */ #include "httplib_main.h" diff --git a/src/httplib_read_auth_file.c b/src/httplib_read_auth_file.c index a16212fe..08afd8ab 100644 --- a/src/httplib_read_auth_file.c +++ b/src/httplib_read_auth_file.c @@ -28,23 +28,23 @@ #include "httplib_main.h" /* - * XX_httplib_read_auth_file( struct file *filep, struct read_auth_file_struct *workdata ); + * bool XX_httplib_read_auth_file( struct file *filep, struct read_auth_file_struct *workdata ); * * The function XX_httpib_read_auth_file() loops over the password file to * read its contents. Include statements are honored which lets the routine * also open and scan child files. */ -int XX_httplib_read_auth_file( struct file *filep, struct read_auth_file_struct *workdata ) { +bool XX_httplib_read_auth_file( struct file *filep, struct read_auth_file_struct *workdata ) { char *p; int is_authorized; struct file fp; size_t l; - if ( filep == NULL || workdata == NULL ) return 0; + if ( filep == NULL || workdata == NULL ) return false; - is_authorized = 0; + is_authorized = false; /* * Loop over passwords file diff --git a/src/httplib_read_request.c b/src/httplib_read_request.c index 1e77638c..24b61831 100644 --- a/src/httplib_read_request.c +++ b/src/httplib_read_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -42,39 +42,48 @@ int XX_httplib_read_request( FILE *fp, struct httplib_connection *conn, char *buf, int bufsiz, int *nread ) { int request_len; - int n = 0; + int n; struct timespec last_action_time; double request_timeout; if ( conn == NULL ) return 0; - memset(&last_action_time, 0, sizeof(last_action_time)); + n = 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; + memset( & last_action_time, 0, sizeof(last_action_time) ); - request_len = XX_httplib_get_request_len(buf, *nread); + if ( conn->ctx->config[REQUEST_TIMEOUT] != NULL ) { - /* first time reading from this connection */ - clock_gettime(CLOCK_MONOTONIC, &last_action_time); + /* + * 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 = XX_httplib_get_request_len( buf, *nread ); + + /* + * first time reading from this connection + */ + + clock_gettime( CLOCK_MONOTONIC, & last_action_time ); + + while ( (conn->ctx->stop_flag == 0) && + (*nread < bufsiz) && + (request_len == 0) && + ((XX_httplib_difftimespec(&last_action_time, &(conn->req_time)) <= request_timeout) || (request_timeout < 0)) && + ((n = XX_httplib_pull(fp, conn, buf + *nread, bufsiz - *nread, request_timeout)) > 0)) { - while ( - (conn->ctx->stop_flag == 0) && (*nread < bufsiz) && (request_len == 0) - && ((XX_httplib_difftimespec(&last_action_time, &(conn->req_time)) - <= request_timeout) || (request_timeout < 0)) - && ((n = XX_httplib_pull(fp, conn, buf + *nread, bufsiz - *nread, request_timeout)) - > 0)) { *nread += n; - /* assert(*nread <= bufsiz); */ - if (*nread > bufsiz) { - return -2; - } - request_len = XX_httplib_get_request_len(buf, *nread); - if (request_timeout > 0.0) clock_gettime(CLOCK_MONOTONIC, &last_action_time); + if ( *nread > bufsiz ) return -2; + + request_len = XX_httplib_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; } /* XX_httplib_read_request */ diff --git a/src/httplib_redirect_to_https_port.c b/src/httplib_redirect_to_https_port.c index 160504d2..0cb45d50 100644 --- a/src/httplib_redirect_to_https_port.c +++ b/src/httplib_redirect_to_https_port.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -37,40 +37,48 @@ void XX_httplib_redirect_to_https_port( struct httplib_connection *conn, int ssl_index ) { - char host[1025]; + char host[1024+1]; const char *host_header; size_t hostlen; + char *pos; + + if ( conn == NULL ) return; host_header = httplib_get_header(conn, "Host"); - hostlen = sizeof(host); - if (host_header != NULL) { - char *pos; + hostlen = sizeof( host ); + + if ( host_header != NULL ) { httplib_strlcpy( host, host_header, hostlen ); host[hostlen - 1] = '\0'; pos = strchr(host, ':'); if (pos != NULL) *pos = '\0'; - } else { - /* Cannot get host from the Host: header. - * Fallback to our IP address. */ - if (conn) XX_httplib_sockaddr_to_string(host, hostlen, &conn->client.lsa); + } + + else { + /* + * Cannot get host from the Host: header. + * Fallback to our IP address. + */ + + XX_httplib_sockaddr_to_string( host, hostlen, &conn->client.lsa ); } - /* Send host, port, uri and (if it exists) ?query_string */ - if (conn) { - httplib_printf(conn, - "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s%s%s\r\n\r\n", - host, + /* + * Send host, port, uri and (if it exists) ?query_string + */ + + httplib_printf( conn, "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s%s%s\r\n\r\n", + host, #if defined(USE_IPV6) - (conn->ctx->listening_sockets[ssl_index].lsa.sa.sa_family - == AF_INET6) - ? (int)ntohs(conn->ctx->listening_sockets[ssl_index].lsa.sin6.sin6_port) - : + (conn->ctx->listening_sockets[ssl_index].lsa.sa.sa_family + == AF_INET6) + ? (int)ntohs(conn->ctx->listening_sockets[ssl_index].lsa.sin6.sin6_port) + : #endif - (int)ntohs(conn->ctx->listening_sockets[ssl_index].lsa.sin.sin_port), - conn->request_info.local_uri, - (conn->request_info.query_string == NULL) ? "" : "?", - (conn->request_info.query_string == NULL) ? "" : conn->request_info.query_string); - } + (int)ntohs(conn->ctx->listening_sockets[ssl_index].lsa.sin.sin_port), + conn->request_info.local_uri, + (conn->request_info.query_string == NULL) ? "" : "?", + (conn->request_info.query_string == NULL) ? "" : conn->request_info.query_string); } /* XX_httplib_redirect_to_https_port */ diff --git a/src/httplib_send_authorization_request.c b/src/httplib_send_authorization_request.c index 8eaa46c0..72251f8f 100644 --- a/src/httplib_send_authorization_request.c +++ b/src/httplib_send_authorization_request.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -33,12 +33,13 @@ void XX_httplib_send_authorization_request( struct httplib_connection *conn ) { char date[64]; time_t curtime; + uint64_t nonce; if ( conn == NULL || conn->ctx == NULL ) return; curtime = time( NULL ); - uint64_t nonce = (uint64_t)(conn->ctx->start_time); + nonce = (uint64_t)conn->ctx->start_time; httplib_pthread_mutex_lock( & conn->ctx->nonce_mutex ); nonce += conn->ctx->nonce_count; @@ -47,13 +48,13 @@ void XX_httplib_send_authorization_request( struct httplib_connection *conn ) { nonce ^= conn->ctx->auth_nonce_mask; conn->status_code = 401; - conn->must_close = 1; + conn->must_close = 1; - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); - httplib_printf(conn, "HTTP/1.1 401 Unauthorized\r\n"); - XX_httplib_send_no_cache_header(conn); - httplib_printf(conn, + httplib_printf( conn, "HTTP/1.1 401 Unauthorized\r\n" ); + XX_httplib_send_no_cache_header( conn ); + httplib_printf( conn, "Date: %s\r\n" "Connection: %s\r\n" "Content-Length: 0\r\n" @@ -62,6 +63,6 @@ void XX_httplib_send_authorization_request( struct httplib_connection *conn ) { date, XX_httplib_suggest_connection_header(conn), conn->ctx->config[AUTHENTICATION_DOMAIN], - nonce); + nonce ); } /* XX_httplib_send_authorization_request */ diff --git a/src/httplib_send_file_data.c b/src/httplib_send_file_data.c index 8d265ff9..14b8d037 100644 --- a/src/httplib_send_file_data.c +++ b/src/httplib_send_file_data.c @@ -22,12 +22,15 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* Send len bytes from the opened file to the client. */ +/* + * Send len bytes from the opened file to the client. + */ + void XX_httplib_send_file_data( struct httplib_connection *conn, struct file *filep, int64_t offset, int64_t len ) { char buf[MG_BUF_LEN]; @@ -38,7 +41,10 @@ void XX_httplib_send_file_data( struct httplib_connection *conn, struct file *fi if ( filep == NULL || conn == NULL ) return; - /* Sanity check the offset */ + /* + * Sanity check the offset + */ + size = (filep->size > INT64_MAX) ? INT64_MAX : (int64_t)(filep->size); offset = (offset < 0) ? 0 : ((offset > size) ? size : offset); @@ -70,8 +76,11 @@ void XX_httplib_send_file_data( struct httplib_connection *conn, struct file *fi int loop_cnt = 0; do { - /* 2147479552 (0x7FFFF000) is a limit found by experiment on - * 64 bit Linux (2^31 minus one memory page of 4k?). */ + /* + * 2147479552 (0x7FFFF000) is a limit found by experiment on + * 64 bit Linux (2^31 minus one memory page of 4k?). + */ + size_t sf_tosend = (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000); sf_sent = sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend); @@ -83,48 +92,74 @@ void XX_httplib_send_file_data( struct httplib_connection *conn, struct file *fi } - else if (loop_cnt == 0) { - /* This file can not be sent using sendfile. + else if ( loop_cnt == 0 ) { + /* + * This file can not be sent using sendfile. * This might be the case for pseudo-files in the * /sys/ and /proc/ file system. - * Use the regular user mode copy code instead. */ + * Use the regular user mode copy code instead. + */ + break; - } else if (sf_sent == 0) { - /* No error, but 0 bytes sent. May be EOF? */ + } + + else if ( sf_sent == 0 ) { + + /* + * No error, but 0 bytes sent. May be EOF? + */ + return; } loop_cnt++; - } while ((len > 0) && (sf_sent >= 0)); + } while ( len > 0 && sf_sent >= 0 ); - if (sf_sent > 0) { - return; /* OK */ - } + if ( sf_sent > 0 ) return; /* OK */ + + /* + * sf_sent<0 means error, thus fall back to the classic way + * This is always the case, if sf_file is not a "normal" file, + * e.g., for sending data from the output of a CGI process. + */ - /* sf_sent<0 means error, thus fall back to the classic way */ - /* This is always the case, if sf_file is not a "normal" file, - * e.g., for sending data from the output of a CGI process. */ offset = (int64_t)sf_offs; } #endif - if ((offset > 0) && (fseeko(filep->fp, offset, SEEK_SET) != 0)) { - httplib_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO)); - XX_httplib_send_http_error( conn, 500, "%s", "Error: Unable to access file at requested position."); - } else { - while (len > 0) { - /* Calculate how much to read from the file in the buffer */ + if ( offset > 0 && fseeko( filep->fp, offset, SEEK_SET ) != 0 ) { + + httplib_cry( conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO) ); + XX_httplib_send_http_error( conn, 500, "%s", "Error: Unable to access file at requested position." ); + } + + else { + 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 + */ - /* Send read bytes to the client, exit the loop on error */ - if ((num_written = httplib_write(conn, buf, (size_t)num_read)) != num_read) break; + 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 = httplib_write( conn, buf, (size_t)num_read )) != num_read ) break; + + /* + * Both read and were successful, adjust counters + */ - /* Both read and were successful, adjust counters */ conn->num_bytes_sent += num_written; - len -= num_written; + len -= num_written; } } } diff --git a/src/httplib_send_http_error.c b/src/httplib_send_http_error.c index 7daa7c40..3c1852d9 100644 --- a/src/httplib_send_http_error.c +++ b/src/httplib_send_http_error.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -40,93 +40,136 @@ void XX_httplib_send_http_error( struct httplib_connection *conn, int status, co int truncated; int has_body; char date[64]; - time_t curtime = time(NULL); - const char *error_handler = NULL; + time_t curtime; + const char *error_handler; struct file error_page_file = STRUCT_FILE_INITIALIZER; - const char *error_page_file_ext, *tstr; + const char *error_page_file_ext; + const char *tstr; + const char *status_text; - const char *status_text = httplib_get_response_code_text(conn, status); + if ( conn == NULL ) return; - if (conn == NULL) return; + curtime = time( NULL ); + error_handler = NULL; + status_text = httplib_get_response_code_text( conn, status ); 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]; + + 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: /* Handler for specific error, e.g. 404 error */ - XX_httplib_snprintf(conn, &truncated, buf, sizeof(buf) - 32, "%serror%03u.", error_handler, status); - break; - case 2: /* Handler for error group, e.g., 5xx error handler - * for all server errors (500-599) */ - XX_httplib_snprintf(conn, &truncated, buf, sizeof(buf) - 32, "%serror%01uxx.", error_handler, status / 100); - break; - default: /* Handler for all errors */ - XX_httplib_snprintf(conn, &truncated, buf, sizeof(buf) - 32, "%serror.", error_handler); - break; + page_handler_found = 0; + + if ( error_handler != NULL ) { + + for (scope=1; (scope <= 3) && ! page_handler_found; scope++) { + + switch ( scope ) { + + case 1 : + /* + * Handler for specific error, e.g. 404 error + */ + + XX_httplib_snprintf( conn, &truncated, buf, sizeof(buf) - 32, "%serror%03u.", error_handler, status ); + break; + + case 2 : + /* + * Handler for error group, e.g., 5xx error handler + * for all server errors (500-599) + */ + + XX_httplib_snprintf( conn, &truncated, buf, sizeof(buf) - 32, "%serror%01uxx.", error_handler, status / 100 ); + break; + + default : + /* + * Handler for all errors + */ + + XX_httplib_snprintf( conn, &truncated, buf, sizeof(buf) - 32, "%serror.", error_handler ); + break; } - /* String truncation in buf may only occur if error_handler + /* + * String truncation in buf may only occur if error_handler * is too long. This string is from the config, not from a - * client. */ - (void)truncated; + * client. + */ - len = (int)strlen(buf); + len = (int)strlen( buf ); + tstr = strchr( error_page_file_ext, '.' ); - tstr = strchr(error_page_file_ext, '.'); + while ( tstr ) { - while (tstr) { - for (i = 1; i < 32 && tstr[i] != 0 && tstr[i] != ','; - i++) - buf[len + i - 1] = tstr[i]; + for (i=1; i<32 && tstr[i] != 0 && tstr[i] != ','; i++) buf[len + i - 1] = tstr[i]; buf[len + i - 1] = 0; - if (XX_httplib_stat(conn, buf, &error_page_file)) { + + if ( XX_httplib_stat( conn, buf, &error_page_file ) ) { + page_handler_found = 1; break; } - tstr = strchr(tstr + i, '.'); + tstr = strchr( tstr + i, '.' ); } } } - if (page_handler_found) { + if ( page_handler_found ) { + conn->in_error_handler = 1; - XX_httplib_handle_file_based_request(conn, buf, &error_page_file); + XX_httplib_handle_file_based_request( conn, buf, &error_page_file ); conn->in_error_handler = 0; + return; } } - /* No custom error page. Send default error page. */ - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); + /* + * No custom error page. Send default error page. + */ + + XX_httplib_gmt_time_string( date, sizeof(date), &curtime ); + + /* + * Errors 1xx, 204 and 304 MUST NOT send a body + */ - /* Errors 1xx, 204 and 304 MUST NOT send a body */ has_body = (status > 199 && status != 204 && status != 304); conn->must_close = 1; - httplib_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text); - XX_httplib_send_no_cache_header(conn); - if (has_body) httplib_printf(conn, "%s", "Content-Type: text/plain; charset=utf-8\r\n"); - httplib_printf(conn, "Date: %s\r\n" "Connection: close\r\n\r\n", date); + httplib_printf( conn, "HTTP/1.1 %d %s\r\n", status, status_text ); + XX_httplib_send_no_cache_header( conn ); - /* Errors 1xx, 204 and 304 MUST NOT send a body */ - if (has_body) { - httplib_printf(conn, "Error %d: %s\n", status, status_text); + if ( has_body ) httplib_printf( conn, "%s", "Content-Type: text/plain; charset=utf-8\r\n" ); + httplib_printf( conn, "Date: %s\r\n" "Connection: close\r\n\r\n", date ); - if (fmt != NULL) { - va_start(ap, fmt); - XX_httplib_vsnprintf(conn, NULL, buf, sizeof(buf), fmt, ap); - va_end(ap); - httplib_write(conn, buf, strlen(buf)); + /* + * Errors 1xx, 204 and 304 MUST NOT send a body + */ + + if ( has_body ) { + + httplib_printf( conn, "Error %d: %s\n", status, status_text ); + + if ( fmt != NULL ) { + + va_start( ap, fmt ); + XX_httplib_vsnprintf( conn, NULL, buf, sizeof(buf), fmt, ap ); + va_end( ap ); + httplib_write( conn, buf, strlen(buf) ); } - - } else { + } + + else { /* No body allowed. Close the connection. */ } } diff --git a/src/httplib_send_websocket_handshake.c b/src/httplib_send_websocket_handshake.c index d1a6ff42..36db19a9 100644 --- a/src/httplib_send_websocket_handshake.c +++ b/src/httplib_send_websocket_handshake.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -41,43 +41,59 @@ int XX_httplib_send_websocket_handshake( struct httplib_connection *conn, const char *websock_key ) { static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - const char *protocol = NULL; + const char *protocol; char buf[100]; char sha[20]; char b64_sha[sizeof(sha) * 2]; SHA1_CTX sha_ctx; int truncated; - /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */ - XX_httplib_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic); - if (truncated) { + protocol = NULL; + + /* + * Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. + */ + + XX_httplib_snprintf( conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic ); + if ( truncated ) { + conn->must_close = 1; return 0; } - SHA1Init(&sha_ctx); - SHA1Update(&sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf)); - SHA1Final((unsigned char *)sha, &sha_ctx); + SHA1Init( & sha_ctx ); + SHA1Update( & sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf) ); + SHA1Final( (unsigned char *)sha, &sha_ctx ); - XX_httplib_base64_encode((unsigned char *)sha, sizeof(sha), b64_sha); - httplib_printf(conn, + XX_httplib_base64_encode( (unsigned char *)sha, sizeof(sha), b64_sha ); + httplib_printf( conn, "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n", - b64_sha); - protocol = httplib_get_header(conn, "Sec-WebSocket-Protocol"); - if (protocol) { - /* The protocol is a comma seperated list of names. */ - /* The server must only return one value from this list. */ - /* First check if it is a list or just a single value. */ + b64_sha ); + + protocol = httplib_get_header( conn, "Sec-WebSocket-Protocol" ); + if ( protocol ) { + /* + * The protocol is a comma seperated list of names. + * The server must only return one value from this list. + * First check if it is a list or just a single value. + */ const char *sep = strchr(protocol, ','); if (sep == NULL) { - /* Just a single protocol -> accept it. */ + + /* + * Just a single protocol -> accept it. + */ + httplib_printf(conn, "Sec-WebSocket-Protocol: %s\r\n\r\n", protocol); - } else { - /* Multiple protocols -> accept the first one. */ - /* This is just a quick fix if the client offers multiple + } + + else { + /* + * Multiple protocols -> accept the first one. + * This is just a quick fix if the client offers multiple * protocols. In order to get the behavior intended by * RFC 6455 (https://tools.ietf.org/rfc/rfc6455.txt), it is * required to have a list of websocket subprotocols accepted @@ -86,12 +102,15 @@ int XX_httplib_send_websocket_handshake( struct httplib_connection *conn, const * handshake by not returning a Sec-Websocket-Protocol header if * no subprotocol is acceptable. */ - httplib_printf(conn, "Sec-WebSocket-Protocol: %.*s\r\n\r\n", (int)(sep - protocol), protocol); + + httplib_printf( conn, "Sec-WebSocket-Protocol: %.*s\r\n\r\n", (int)(sep - protocol), protocol ); } - /* TODO: Real subprotocol negotiation instead of just taking the first - * websocket subprotocol suggested by the client. */ + /* + * TODO: Real subprotocol negotiation instead of just taking the first + * websocket subprotocol suggested by the client. + */ } - else httplib_printf(conn, "%s", "\r\n"); + else httplib_printf( conn, "%s", "\r\n" ); return 1; diff --git a/src/httplib_set_close_on_exec.c b/src/httplib_set_close_on_exec.c index 03d8f5e9..2f0e3e14 100644 --- a/src/httplib_set_close_on_exec.c +++ b/src/httplib_set_close_on_exec.c @@ -22,21 +22,23 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" #if defined(_WIN32) -/* conn parameter may be NULL */ +/* + * conn parameter may be NULL + */ + void XX_httplib_set_close_on_exec( SOCKET sock, struct httplib_connection *conn ) { - (void)conn; /* Unused. */ + UNUSED_PARAMETER(conn); #if defined(_WIN32_WCE) - (void)sock; #else - SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation( (HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0 ); #endif } /* XX_httplib_set_close_on_exec */ @@ -44,11 +46,15 @@ void XX_httplib_set_close_on_exec( SOCKET sock, struct httplib_connection *conn #else -/* conn may be NULL */ +/* + * conn may be NULL + */ + void XX_httplib_set_close_on_exec( SOCKET fd, struct httplib_connection *conn ) { - if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { - if (conn) { httplib_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, strerror(ERRNO)); } + if ( fcntl( fd, F_SETFD, FD_CLOEXEC ) != 0 ) { + + if ( conn != NULL ) httplib_cry(conn, "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", __func__, strerror(ERRNO) ); } } /* XX_httplib_set_close_on_exec */ diff --git a/src/httplib_set_handler_type.c b/src/httplib_set_handler_type.c index ac7da721..8b7eda7b 100644 --- a/src/httplib_set_handler_type.c +++ b/src/httplib_set_handler_type.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -38,104 +38,140 @@ void XX_httplib_set_handler_type( struct httplib_context *ctx, const char *uri, int handler_type, int is_delete_request, httplib_request_handler handler, httplib_websocket_connect_handler connect_handler, httplib_websocket_ready_handler ready_handler, httplib_websocket_data_handler data_handler, httplib_websocket_close_handler close_handler, httplib_authorization_handler auth_handler, void *cbdata ) { - struct httplib_handler_info *tmp_rh, **lastref; - size_t urilen = strlen(uri); + struct httplib_handler_info *tmp_rh; + struct httplib_handler_info **lastref; + size_t urilen; - if (handler_type == WEBSOCKET_HANDLER) { - /* assert(handler == NULL); */ - /* assert(is_delete_request || connect_handler!=NULL || - * ready_handler!=NULL || data_handler!=NULL || - * close_handler!=NULL); - */ - /* assert(auth_handler == NULL); */ - if (handler != NULL) return; - if (!is_delete_request && connect_handler == NULL && ready_handler == NULL && data_handler == NULL && close_handler == NULL) return; - if (auth_handler != NULL) return; - } else if (handler_type == REQUEST_HANDLER) { - /* assert(connect_handler==NULL && ready_handler==NULL && - * data_handler==NULL && close_handler==NULL); */ - /* assert(is_delete_request || (handler!=NULL)); - */ - /* assert(auth_handler == NULL); */ - if (connect_handler != NULL || ready_handler != NULL || data_handler != NULL || close_handler != NULL) return; - if (!is_delete_request && (handler == NULL)) return; - if (auth_handler != NULL) return; - } else { /* AUTH_HANDLER */ - /* assert(handler == NULL); */ - /* assert(connect_handler==NULL && ready_handler==NULL && - * data_handler==NULL && close_handler==NULL); */ - /* assert(auth_handler != NULL); */ - if (handler != NULL) return; - if (connect_handler != NULL || ready_handler != NULL || data_handler != NULL || close_handler != NULL) return; - if (!is_delete_request && (auth_handler == NULL)) return; + if ( uri == NULL ) return; + + urilen = strlen( uri ); + + if ( handler_type == WEBSOCKET_HANDLER ) { + + if ( handler != NULL ) return; + if ( ! is_delete_request && connect_handler == NULL && ready_handler == NULL && data_handler == NULL && close_handler == NULL ) return; + if ( auth_handler != NULL ) return; + } + + else if ( handler_type == REQUEST_HANDLER ) { + + if ( connect_handler != NULL || ready_handler != NULL || data_handler != NULL || close_handler != NULL ) return; + if ( ! is_delete_request && handler == NULL ) return; + if ( auth_handler != NULL ) return; + } + + else { /* AUTH_HANDLER */ + if ( handler != NULL ) return; + if ( connect_handler != NULL || ready_handler != NULL || data_handler != NULL || close_handler != NULL ) return; + if ( ! is_delete_request && auth_handler == NULL ) return; } if ( ctx == NULL ) return; - httplib_lock_context(ctx); + httplib_lock_context( ctx ); + + /* + * first try to find an existing handler + */ + + lastref = & ctx->handlers; + + for ( tmp_rh=ctx->handlers; tmp_rh != NULL; tmp_rh=tmp_rh->next ) { + + if ( tmp_rh->handler_type == handler_type ) { + + if ( urilen == tmp_rh->uri_len && ! strcmp( tmp_rh->uri, uri ) ) { + + if ( ! is_delete_request ) { + + /* + * update existing handler + */ + + if ( handler_type == REQUEST_HANDLER ) { - /* first try to find an existing handler */ - lastref = &(ctx->handlers); - for (tmp_rh = ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { - if (tmp_rh->handler_type == handler_type) { - if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) { - if (!is_delete_request) { - /* update existing handler */ - if (handler_type == REQUEST_HANDLER) { tmp_rh->handler = handler; - } else if (handler_type == WEBSOCKET_HANDLER) { + } + + else if ( handler_type == WEBSOCKET_HANDLER ) { + tmp_rh->connect_handler = connect_handler; - tmp_rh->ready_handler = ready_handler; - tmp_rh->data_handler = data_handler; - tmp_rh->close_handler = close_handler; - } else { /* AUTH_HANDLER */ + tmp_rh->ready_handler = ready_handler; + tmp_rh->data_handler = data_handler; + tmp_rh->close_handler = close_handler; + } + + else { /* AUTH_HANDLER */ tmp_rh->auth_handler = auth_handler; } + tmp_rh->cbdata = cbdata; - } else { - /* remove existing handler */ + } + + else { + /* + * remove existing handler + */ *lastref = tmp_rh->next; httplib_free( tmp_rh->uri ); httplib_free( tmp_rh ); } + httplib_unlock_context(ctx); return; } } - lastref = &(tmp_rh->next); + lastref = & tmp_rh->next; } - if (is_delete_request) { - /* no handler to set, this was a remove request to a non-existing - * handler */ - httplib_unlock_context(ctx); + if ( is_delete_request ) { + + /* + * no handler to set, this was a remove request to a non-existing + * handler + */ + + httplib_unlock_context( ctx ); return; } tmp_rh = httplib_calloc( sizeof(struct httplib_handler_info), 1 ); - if (tmp_rh == NULL) { + if ( tmp_rh == NULL ) { + + httplib_unlock_context( ctx ); + httplib_cry( XX_httplib_fc(ctx), "%s", "Cannot create new request handler struct, OOM" ); - httplib_unlock_context(ctx); - httplib_cry( XX_httplib_fc(ctx), "%s", "Cannot create new request handler struct, OOM"); return; } - tmp_rh->uri = XX_httplib_strdup(uri); - if (!tmp_rh->uri) { - httplib_unlock_context(ctx); + + tmp_rh->uri = XX_httplib_strdup( uri ); + + if ( tmp_rh->uri == NULL ) { + + httplib_unlock_context( ctx ); httplib_free( tmp_rh ); - httplib_cry( XX_httplib_fc(ctx), "%s", "Cannot create new request handler struct, OOM"); + httplib_cry( XX_httplib_fc(ctx), "%s", "Cannot create new request handler struct, OOM" ); + return; } + tmp_rh->uri_len = urilen; - if (handler_type == REQUEST_HANDLER) { + + if ( handler_type == REQUEST_HANDLER ) { + tmp_rh->handler = handler; - } else if (handler_type == WEBSOCKET_HANDLER) { + } + + else if ( handler_type == WEBSOCKET_HANDLER ) { + tmp_rh->connect_handler = connect_handler; tmp_rh->ready_handler = ready_handler; tmp_rh->data_handler = data_handler; tmp_rh->close_handler = close_handler; - } else { /* AUTH_HANDLER */ + } + + else { /* AUTH_HANDLER */ tmp_rh->auth_handler = auth_handler; } tmp_rh->cbdata = cbdata; diff --git a/src/httplib_set_websocket_handler.c b/src/httplib_set_websocket_handler.c index 41aa2b70..83051e1d 100644 --- a/src/httplib_set_websocket_handler.c +++ b/src/httplib_set_websocket_handler.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -36,7 +36,9 @@ void httplib_set_websocket_handler( struct httplib_context *ctx, const char *uri, httplib_websocket_connect_handler connect_handler, httplib_websocket_ready_handler ready_handler, httplib_websocket_data_handler data_handler, httplib_websocket_close_handler close_handler, void *cbdata ) { - int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL) && (data_handler == NULL) && (close_handler == NULL); + int is_delete_request; + + is_delete_request = (connect_handler == NULL) && (ready_handler == NULL) && (data_handler == NULL) && (close_handler == NULL); XX_httplib_set_handler_type(ctx, uri, WEBSOCKET_HANDLER, is_delete_request, NULL, connect_handler, ready_handler, data_handler, close_handler, NULL, cbdata); } /* httplib_set_websocket_handler */ diff --git a/src/httplib_should_decode_url.c b/src/httplib_should_decode_url.c index 633c57d8..633e011e 100644 --- a/src/httplib_should_decode_url.c +++ b/src/httplib_should_decode_url.c @@ -22,15 +22,16 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -int XX_httplib_should_decode_url( const struct httplib_connection *conn ) { +bool XX_httplib_should_decode_url( const struct httplib_connection *conn ) { - if ( conn == NULL || conn->ctx == NULL ) return 0; + if ( conn == NULL || conn->ctx == NULL ) return false; + if ( conn->ctx->config[DECODE_URL] == NULL ) return false; - return (httplib_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0); + return ( ! httplib_strcasecmp( conn->ctx->config[DECODE_URL], "yes" ) ); } /* XX_httplib_should_decode_url */ diff --git a/src/httplib_should_keep_alive.c b/src/httplib_should_keep_alive.c index 5cf1d98e..d922d35c 100644 --- a/src/httplib_should_keep_alive.c +++ b/src/httplib_should_keep_alive.c @@ -22,28 +22,39 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* 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. */ -int XX_httplib_should_keep_alive( const struct httplib_connection *conn ) { +/* + * bool XX_httplib_should_keep_alive( const struct httplib_connection *conn ); + * + * The function XX_httplib_should_keep_alive() returns true if the connection + * should be kept alive and false if it should be closed. + * + * 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. + */ - if (conn != NULL) { - const char *http_version = conn->request_info.http_version; - const char *header = httplib_get_header(conn, "Connection"); - if (conn->must_close || conn->internal_error || conn->status_code == 401 - || httplib_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 - || (header != NULL && !XX_httplib_header_has_option(header, "keep-alive")) - || (header == NULL && http_version - && 0 != strcmp(http_version, "1.1"))) { - return 0; - } - return 1; - } - return 0; +bool XX_httplib_should_keep_alive( const struct httplib_connection *conn ) { + + const char *http_version; + const char *header; + + if ( conn == NULL || conn->ctx == NULL ) return false; + + http_version = conn->request_info.http_version; + header = httplib_get_header( conn, "Connection" ); + + if ( conn->must_close ) return false; + if ( conn->internal_error ) return false; + if ( conn->status_code == 401 ) return false; + if ( conn->ctx->config[ENABLE_KEEP_ALIVE] != NULL && httplib_strcasecmp( conn->ctx->config[ENABLE_KEEP_ALIVE], "yes" ) ) return false; + if ( header != NULL && ! XX_httplib_header_has_option( header, "keep-alive" ) ) return false; + if ( header == NULL && http_version != NULL && strcmp( http_version, "1.1" ) ) return false; + + return true; } /* XX_httplib_should_keep_alive */ diff --git a/src/httplib_skip_quoted.c b/src/httplib_skip_quoted.c index fcaa3b0b..8f97d36e 100644 --- a/src/httplib_skip_quoted.c +++ b/src/httplib_skip_quoted.c @@ -22,53 +22,71 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" -/* Skip the characters until one of the delimiters characters found. +/* + * Skip the characters until one of the delimiters characters found. * 0-terminate resulting word. Skip the delimiter and following whitespaces. * Advance pointer to buffer to the next word. Return found 0-terminated word. - * Delimiters can be quoted with quotechar. */ -char * XX_httplib_skip_quoted( char **buf, const char *delimiters, const char *whitespace, char quotechar ) { + * Delimiters can be quoted with quotechar. + */ + +char *XX_httplib_skip_quoted( char **buf, const char *delimiters, const char *whitespace, char quotechar ) { char *p; char *begin_word; char *end_word; char *end_whitespace; + if ( buf == NULL || *buf == NULL ) return NULL; + begin_word = *buf; - end_word = begin_word + strcspn(begin_word, delimiters); + end_word = begin_word + strcspn( begin_word, delimiters ); + + /* + * Check for quotechar + */ + + if ( end_word > begin_word ) { - /* Check for quotechar */ - if (end_word > begin_word) { p = end_word - 1; - while (*p == quotechar) { - /* While the delimiter is quoted, look for the next delimiter. */ - /* This happens, e.g., in calls from XX_httplib_parse_auth_header, - * if the user name contains a " character. */ - /* If there is anything beyond end_word, copy it. */ - if (*end_word != '\0') { - 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 */ + while ( *p == quotechar ) { + + /* + * While the delimiter is quoted, look for the next delimiter. + * This happens, e.g., in calls from XX_httplib_parse_auth_header, + * if the user name contains a " character. + * + * If there is anything beyond end_word, copy it. + */ + + if ( *end_word != '\0' ) { + + 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; - } else { + } + + else { *p = '\0'; break; } } + 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; psa.sa_family == AF_INET) { - getnameinfo(&usa->sa, sizeof(usa->sin), buf, (unsigned)len, NULL, 0, NI_NUMERICHOST); - } + if ( usa->sa.sa_family == AF_INET ) getnameinfo(&usa->sa, sizeof(usa->sin), buf, (unsigned)len, NULL, 0, NI_NUMERICHOST ); #if defined(USE_IPV6) - else if (usa->sa.sa_family == AF_INET6) { - getnameinfo(&usa->sa, sizeof(usa->sin6), buf, (unsigned)len, NULL, 0, NI_NUMERICHOST); - } + else if ( usa->sa.sa_family == AF_INET6 ) getnameinfo(&usa->sa, sizeof(usa->sin6), buf, (unsigned)len, NULL, 0, NI_NUMERICHOST ); #endif } /* XX_httplib_sockaddr_to_string */ diff --git a/src/httplib_spawn_process.c b/src/httplib_spawn_process.c index ea2608d9..e02de066 100644 --- a/src/httplib_spawn_process.c +++ b/src/httplib_spawn_process.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -54,83 +54,97 @@ pid_t XX_httplib_spawn_process( struct httplib_connection *conn, const char *pro STARTUPINFOA si; PROCESS_INFORMATION pi = {0}; - (void)envp; + UNUSED_PARAMETER(envp); - memset(&si, 0, sizeof(si)); + memset( &si, 0, sizeof(si) ); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; me = GetCurrentProcess(); - DuplicateHandle( me, (HANDLE)_get_osfhandle(fdin[0]), me, &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS); - DuplicateHandle( me, (HANDLE)_get_osfhandle(fdout[1]), me, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); - DuplicateHandle( me, (HANDLE)_get_osfhandle(fderr[1]), me, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS); + DuplicateHandle( me, (HANDLE)_get_osfhandle(fdin[0]), me, &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS ); + DuplicateHandle( me, (HANDLE)_get_osfhandle(fdout[1]), me, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS ); + DuplicateHandle( me, (HANDLE)_get_osfhandle(fderr[1]), me, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS ); - /* Mark handles that should not be inherited. See + /* + * Mark handles that should not be inherited. See * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx */ - SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]), HANDLE_FLAG_INHERIT, 0); - SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]), HANDLE_FLAG_INHERIT, 0); - SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]), HANDLE_FLAG_INHERIT, 0); - /* If CGI file is a script, try to read the interpreter line */ + SetHandleInformation( (HANDLE)_get_osfhandle(fdin[1]), HANDLE_FLAG_INHERIT, 0 ); + SetHandleInformation( (HANDLE)_get_osfhandle(fdout[0]), HANDLE_FLAG_INHERIT, 0 ); + SetHandleInformation( (HANDLE)_get_osfhandle(fderr[0]), HANDLE_FLAG_INHERIT, 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 */ - XX_httplib_snprintf( conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog); + if ( interp == NULL ) { + + buf[0] = '\0'; + buf[1] = '\0'; + + /* + * Read the first line of the script into the buffer + */ + + XX_httplib_snprintf( conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog ); + + if ( truncated ) { - if (truncated) { pi.hProcess = (pid_t)-1; goto spawn_cleanup; } - if (XX_httplib_fopen(conn, cmdline, "r", &file)) { + if ( XX_httplib_fopen( conn, cmdline, "r", &file ) ) { p = (char *)file.membuf; - XX_httplib_fgets(buf, sizeof(buf), &file, &p); - XX_httplib_fclose(&file); + XX_httplib_fgets( buf, sizeof(buf), &file, &p ); + XX_httplib_fclose( & file ); buf[sizeof(buf) - 1] = '\0'; } - if (buf[0] == '#' && buf[1] == '!') { - trim_trailing_whitespaces(buf + 2); - } else { - buf[2] = '\0'; - } + 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); + 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') { - XX_httplib_snprintf(conn, &truncated, cmdline, sizeof(cmdline), "\"%s\" \"%s\\%s\"", interp, full_dir, prog); - } else { - XX_httplib_snprintf(conn, &truncated, cmdline, sizeof(cmdline), "\"%s\\%s\"", full_dir, prog); - } + GetFullPathNameA( dir, sizeof(full_dir), full_dir, NULL ); + + if ( interp[0] != '\0' ) XX_httplib_snprintf( conn, &truncated, cmdline, sizeof(cmdline), "\"%s\" \"%s\\%s\"", interp, full_dir, prog); + else XX_httplib_snprintf( conn, &truncated, cmdline, sizeof(cmdline), "\"%s\\%s\"", full_dir, prog); + + if ( truncated ) { - if (truncated) { pi.hProcess = (pid_t)-1; goto spawn_cleanup; } - if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) { + if ( CreateProcessA( NULL, cmdline, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi ) == 0 ) { + httplib_cry( conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO); pi.hProcess = (pid_t)-1; - /* goto spawn_cleanup; */ + + /* + * goto spawn_cleanup; + */ } spawn_cleanup: - CloseHandle(si.hStdOutput); - CloseHandle(si.hStdError); - CloseHandle(si.hStdInput); + CloseHandle( si.hStdOutput ); + CloseHandle( si.hStdError ); + CloseHandle( si.hStdInput ); - if (pi.hThread != NULL) CloseHandle(pi.hThread); + if ( pi.hThread != NULL ) CloseHandle( pi.hThread ); return (pid_t)pi.hProcess; @@ -146,49 +160,73 @@ pid_t XX_httplib_spawn_process( struct httplib_connection *conn, const char *pro pid_t pid; const char *interp; - (void)envblk; + UNUSED_PARAMETER(envblk); if ( conn == NULL ) return 0; - if ((pid = fork()) == -1) { - /* Parent */ - XX_httplib_send_http_error(conn, 500, "Error: Creating CGI process\nfork(): %s", strerror(ERRNO)); - } else if (pid == 0) { - /* Child */ - if ( chdir( dir ) != 0 ) httplib_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); - else if ( dup2( fdin[0], 0 ) == -1 ) httplib_cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin[0], strerror(ERRNO)); - else if ( dup2( fdout[1], 1 ) == -1 ) httplib_cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout[1], strerror(ERRNO)); - else if ( dup2( fderr[1], 2 ) == -1 ) httplib_cry(conn, "%s: dup2(%d, 2): %s", __func__, fderr[1], strerror(ERRNO)); + if ( (pid = fork()) == -1 ) { + + /* + * Parent + */ + + XX_httplib_send_http_error( conn, 500, "Error: Creating CGI process\nfork(): %s", strerror(ERRNO) ); + } + + else if ( pid == 0 ) { + + /* + * Child + */ + + if ( chdir( dir ) != 0 ) httplib_cry( conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO) ); + else if ( dup2( fdin[0], 0 ) == -1 ) httplib_cry( conn, "%s: dup2(%d, 0): %s", __func__, fdin[0], strerror(ERRNO) ); + else if ( dup2( fdout[1], 1 ) == -1 ) httplib_cry( conn, "%s: dup2(%d, 1): %s", __func__, fdout[1], strerror(ERRNO) ); + else if ( dup2( fderr[1], 2 ) == -1 ) httplib_cry( conn, "%s: dup2(%d, 2): %s", __func__, fderr[1], strerror(ERRNO) ); else { - /* Keep stderr and stdout in two different pipes. + /* + * Keep stderr and stdout in two different pipes. * Stdout will be sent back to the client, - * stderr should go into a server error log. */ - close(fdin[0]); - close(fdout[1]); - close(fderr[1]); + * stderr should go into a server error log. + */ - /* Close write end fdin and read end fdout and fderr */ - close(fdin[1]); - close(fdout[0]); - close(fderr[0]); + close( fdin[0] ); + close( fdout[1] ); + close( fderr[1] ); - /* After exec, all signal handlers are restored to their default + /* + * Close write end fdin and read end fdout and fderr + */ + + close( fdin[1] ); + close( fdout[0] ); + close( fderr[0] ); + + /* + * 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); + * it to default action. + */ + + signal( SIGCHLD, SIG_DFL ); interp = conn->ctx->config[CGI_INTERPRETER]; - if (interp == NULL) { - execle(prog, prog, NULL, envp); - httplib_cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO)); - } else { - execle(interp, interp, prog, NULL, envp); - httplib_cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog, strerror(ERRNO)); + + if ( interp == NULL ) { + + execle( prog, prog, NULL, envp ); + httplib_cry( conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO) ); + } + + else { + execle( interp, interp, prog, NULL, envp ); + httplib_cry( conn, "%s: execle(%s %s): %s", __func__, interp, prog, strerror(ERRNO) ); } } - exit(EXIT_FAILURE); + + exit( EXIT_FAILURE ); } return pid; diff --git a/src/httplib_ssi.c b/src/httplib_ssi.c index 887c8fde..5e7f43d6 100644 --- a/src/httplib_ssi.c +++ b/src/httplib_ssi.c @@ -22,7 +22,7 @@ * THE SOFTWARE. * * ============ - * Release: 1.8 + * Release: 2.0 */ #include "httplib_main.h" @@ -43,72 +43,106 @@ static void do_ssi_include(struct httplib_connection *conn, const char *ssi, cha if ( conn == NULL ) return; - /* sscanf() is safe here, since send_ssi_file() also uses buffer + /* + * 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; - XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s/%s", conn->ctx->config[DOCUMENT_ROOT], file_name); + * always < MG_BUF_LEN. + */ - } 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; - XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name); + if ( sscanf( tag, " virtual=\"%511[^\"]\"", file_name ) == 1 ) { - } 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; - XX_httplib_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi); + /* + * File name is relative to the webserver root + */ - if (!truncated) { - if ((p = strrchr(path, '/')) != NULL) p[1] = '\0'; - len = strlen(path); - XX_httplib_snprintf(conn, &truncated, path + len, sizeof(path) - len, "%s", file_name); + file_name[511] = 0; + XX_httplib_snprintf( conn, &truncated, path, sizeof(path), "%s/%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; + XX_httplib_snprintf( conn, &truncated, 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; + XX_httplib_snprintf( conn, &truncated, path, sizeof(path), "%s", ssi ); + + if ( ! truncated ) { + + if ( (p = strrchr( path, '/' )) != NULL) p[1] = '\0'; + len = strlen( path ); + XX_httplib_snprintf( conn, &truncated, path + len, sizeof(path) - len, "%s", file_name ); } - } else { - httplib_cry(conn, "Bad SSI #include: [%s]", tag); + } + + else { + httplib_cry( conn, "Bad SSI #include: [%s]", tag ); return; } - if (truncated) { - httplib_cry(conn, "SSI #include path length overflow: [%s]", tag); + if ( truncated ) { + + httplib_cry( conn, "SSI #include path length overflow: [%s]", tag ); return; } - if (!XX_httplib_fopen(conn, path, "rb", &file)) { - httplib_cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", tag, path, strerror(ERRNO)); - } else { - XX_httplib_fclose_on_exec(&file, conn); - if (XX_httplib_match_prefix(conn->ctx->config[SSI_EXTENSIONS], strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) { + if ( ! XX_httplib_fopen( conn, path, "rb", &file ) ) { + + httplib_cry( conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", tag, path, strerror(ERRNO) ); + } + + else { + XX_httplib_fclose_on_exec( & file, conn ); + + if ( XX_httplib_match_prefix( conn->ctx->config[SSI_EXTENSIONS], strlen( conn->ctx->config[SSI_EXTENSIONS] ), path) > 0 ) { send_ssi_file(conn, path, &file, include_level + 1); - } else { - XX_httplib_send_file_data(conn, &file, 0, INT64_MAX); } - XX_httplib_fclose(&file); + + else XX_httplib_send_file_data(conn, &file, 0, INT64_MAX); + + XX_httplib_fclose( & file ); } -} + +} /* do_ssi_include */ #if !defined(NO_POPEN) -static void do_ssi_exec(struct httplib_connection *conn, char *tag) { +static void do_ssi_exec( struct httplib_connection *conn, char *tag ) { char cmd[1024] = ""; struct file file = STRUCT_FILE_INITIALIZER; - if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) { - httplib_cry(conn, "Bad SSI #exec: [%s]", tag); - } else { + if ( sscanf(tag, " \"%1023[^\"]\"", cmd) != 1 ) { + + httplib_cry( conn, "Bad SSI #exec: [%s]", tag ); + } + + else { cmd[1023] = 0; - if ((file.fp = popen(cmd, "r")) == NULL) { - httplib_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); - } else { - XX_httplib_send_file_data(conn, &file, 0, INT64_MAX); - pclose(file.fp); + if ( (file.fp = popen( cmd, "r" ) ) == NULL ) { + + httplib_cry( conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO) ); + } + + else { + XX_httplib_send_file_data( conn, &file, 0, INT64_MAX ); + pclose( file.fp ); } } } @@ -134,60 +168,100 @@ static void send_ssi_file( struct httplib_connection *conn, const char *path, st int len; int in_ssi_tag; - if (include_level > 10) { - httplib_cry(conn, "SSI #include level is too deep (%s)", path); + if ( include_level > 10 ) { + + httplib_cry( conn, "SSI #include level is too deep (%s)", path ); return; } - in_ssi_tag = len = offset = 0; - while ((ch = httplib_fgetc(filep, offset)) != EOF) { - if (in_ssi_tag && ch == '>') { + in_ssi_tag = 0; + len = 0; + offset = 0; + + while ( (ch = httplib_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, "