From 985fd93aec30c198ade3180ded05f26d3113cf53 Mon Sep 17 00:00:00 2001 From: Lammert Bies Date: Sat, 10 Dec 2016 22:33:01 +0100 Subject: [PATCH] Moved put_file to own file --- Makefile | 1 + src/httplib_put_file.c | 145 +++++++++++++++++++++++++++++++++++++++++ src/libhttp-private.h | 3 + src/libhttp.c | 127 ++++-------------------------------- 4 files changed, 161 insertions(+), 115 deletions(-) create mode 100644 src/httplib_put_file.c diff --git a/Makefile b/Makefile index ac33dfe4..1e681d93 100644 --- a/Makefile +++ b/Makefile @@ -78,6 +78,7 @@ LIB_SOURCES = src/libhttp.c \ src/httplib_parse_net.c \ src/httplib_process_new_connection.c \ src/httplib_produce_socket.c \ + src/httplib_put_file.c \ src/httplib_read_websocket.c \ src/httplib_redirect_to_https_port.c \ src/httplib_refresh_trust.c \ diff --git a/src/httplib_put_file.c b/src/httplib_put_file.c new file mode 100644 index 00000000..dbafa214 --- /dev/null +++ b/src/httplib_put_file.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2016 Lammert Bies + * Copyright (c) 2013-2016 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + + +#include "libhttp-private.h" + + + +/* + * void XX_httplib_put_file( struct mg_connection *conn, const char *path ); + * + * The function XX_httplib_put_file() processes a file PUT request coming from + * a remote client. + */ + +#if !defined(NO_FILES) + +void XX_httplib_put_file( struct mg_connection *conn, const char *path ) { + + struct file file = STRUCT_FILE_INITIALIZER; + const char *range; + int64_t r1; + int64_t r2; + int rc; + char date[64]; + time_t curtime; + + if (conn == NULL) return; + + curtime = time( NULL ); + + 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. */ + rc = 0; + + } 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); + return; + } + + /* 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); + return; + } + } + } else { + /* File should be created */ + conn->status_code = 201; + 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); + mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(NULL, conn->status_code)); + XX_httplib_send_no_cache_header(conn); + mg_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. */ + 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)); + 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)); + 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)); + return; + } + + XX_httplib_fclose_on_exec(&file, conn); + range = mg_get_header(conn, "Content-Range"); + r1 = 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); + } + + 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); + return; + } + + XX_httplib_gmt_time_string(date, sizeof(date), &curtime); + mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(NULL, conn->status_code)); + XX_httplib_send_no_cache_header(conn); + mg_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_put_file */ + +#endif /* !NO_FILES */ diff --git a/src/libhttp-private.h b/src/libhttp-private.h index d2b797dc..5609879e 100644 --- a/src/libhttp-private.h +++ b/src/libhttp-private.h @@ -874,6 +874,7 @@ struct mg_connection * XX_httplib_fc( struct mg_context *ctx ); void XX_httplib_fclose( struct file *filep ); void XX_httplib_fclose_on_exec( struct file *filep, struct mg_connection *conn ); int XX_httplib_fopen( const struct mg_connection *conn, const char *path, const char *mode, struct file *filep ); +int XX_httplib_forward_body_data( struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl ); void XX_httplib_free_context( struct mg_context *ctx ); int XX_httplib_get_first_ssl_listener_index( const struct mg_context *ctx ); const char * XX_httplib_get_header( const struct mg_request_info *ri, const char *name ); @@ -913,9 +914,11 @@ const char * XX_httplib_next_option( const char *list, struct vec *val, struct int XX_httplib_parse_http_headers( char **buf, struct mg_request_info *ri ); int XX_httplib_parse_http_message( char *buf, int len, struct mg_request_info *ri ); int XX_httplib_parse_net( const char *spec, uint32_t *net, uint32_t *mask ); +int XX_httplib_parse_range_header( const char *header, int64_t *a, int64_t *b ); void XX_httplib_process_new_connection( struct mg_connection *conn ); void XX_httplib_produce_socket( struct mg_context *ctx, const struct socket *sp ); int XX_httplib_pull( FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout ); +int XX_httplib_put_dir( struct mg_connection *conn, const char *path ); void XX_httplib_put_file( struct mg_connection *conn, const char *path ); int XX_httplib_read_request( FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread ); void XX_httplib_read_websocket( struct mg_connection *conn, mg_websocket_data_handler ws_data_handler, void *callback_data ); diff --git a/src/libhttp.c b/src/libhttp.c index 00ffc011..534f84b1 100644 --- a/src/libhttp.c +++ b/src/libhttp.c @@ -4860,10 +4860,11 @@ void XX_httplib_send_file_data( struct mg_connection *conn, struct file *filep, } /* XX_httplib_send_file_data */ -static int parse_range_header(const char *header, int64_t *a, int64_t *b) { +int XX_httplib_parse_range_header( const char *header, int64_t *a, int64_t *b ) { return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); -} + +} /* XX_httplib_parse_range_header */ static void construct_etag(char *buf, size_t buf_len, const struct file *filep) { @@ -4950,7 +4951,7 @@ void XX_httplib_handle_static_file_request( struct mg_connection *conn, const ch /* If Range: header specified, act accordingly */ r1 = r2 = 0; hdr = mg_get_header(conn, "Range"); - if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && r1 >= 0 + 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) */ @@ -5086,7 +5087,7 @@ void mg_send_mime_file2( struct mg_connection *conn, const char *path, const cha * Return -1 for if the path is too long. * Return -2 if path can not be created. */ -static int put_dir(struct mg_connection *conn, const char *path) { +int XX_httplib_put_dir( struct mg_connection *conn, const char *path ) { char buf[PATH_MAX]; const char *s; @@ -5117,7 +5118,8 @@ static int put_dir(struct mg_connection *conn, const char *path) { } return res; -} + +} /* XX_httplib_put_dir */ void XX_httplib_remove_bad_file( const struct mg_connection *conn, const char *path ) { @@ -5142,7 +5144,7 @@ long long mg_store_body( struct mg_connection *conn, const char *path ) { return -11; } - ret = put_dir(conn, path); + ret = XX_httplib_put_dir(conn, path); if (ret < 0) { /* -1 for path too long, * -2 for path can not be created. */ @@ -5433,7 +5435,7 @@ int XX_httplib_is_not_modified( const struct mg_connection *conn, const struct f #if !defined(NO_CGI) || !defined(NO_FILES) -static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl) { +int XX_httplib_forward_body_data( struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl ) { const char *expect; const char *body; @@ -5512,7 +5514,8 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, } return success; -} + +} /* XX_httplib_forward_body_data */ #endif #if !defined(NO_CGI) @@ -5839,7 +5842,7 @@ void XX_httplib_handle_cgi_request( struct mg_connection *conn, const char *prog if ((conn->request_info.content_length > 0) || conn->is_chunked) { /* This is a POST/PUT request, or another request with body data. */ - if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) { + if (!XX_httplib_forward_body_data(conn, in, INVALID_SOCKET, NULL)) { /* Error sending the body data */ mg_cry(conn, "Error: CGI program \"%s\": Forward body data failed", prog); goto done; @@ -6003,110 +6006,4 @@ void XX_httplib_mkcol( struct mg_connection *conn, const char *path ) { } /* XX_httplib_mkcol */ - -void XX_httplib_put_file( struct mg_connection *conn, const char *path ) { - - struct file file = STRUCT_FILE_INITIALIZER; - const char *range; - int64_t r1; - int64_t r2; - int rc; - char date[64]; - time_t curtime; - - if (conn == NULL) return; - - curtime = time( NULL ); - - 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. */ - rc = 0; - - } 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); - return; - } - - /* 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); - return; - } - } - } else { - /* File should be created */ - conn->status_code = 201; - rc = put_dir(conn, path); - } - - if (rc == 0) { - /* put_dir returns 0 if path is a directory */ - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(NULL, conn->status_code)); - XX_httplib_send_no_cache_header(conn); - mg_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. */ - return; - } - - if (rc == -1) { - /* 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) { - /* 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)); - return; - } - - XX_httplib_fclose_on_exec(&file, conn); - range = mg_get_header(conn, "Content-Range"); - r1 = r2 = 0; - if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { - conn->status_code = 206; /* Partial content */ - fseeko(file.fp, r1, SEEK_SET); - } - - if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { - /* forward_body_data failed. - * The error code has already been sent to the client, - * and conn->status_code is already set. */ - XX_httplib_fclose(&file); - return; - } - - XX_httplib_gmt_time_string(date, sizeof(date), &curtime); - mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, mg_get_response_code_text(NULL, conn->status_code)); - XX_httplib_send_no_cache_header(conn); - mg_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_put_file */ - #endif /* !NO_FILES */