diff --git a/src/protocol/http/Makefile b/src/protocol/http/Makefile index fb4dfa0f1..0386ce2bd 100644 --- a/src/protocol/http/Makefile +++ b/src/protocol/http/Makefile @@ -3,6 +3,6 @@ include $(top_builddir)/Makefile.config OBJS-$(CONFIG_GSSAPI) += http_negotiate.o -OBJS = blacklist.o codes.o http.o +OBJS = blacklist.o codes.o http.o post.o include $(top_srcdir)/Makefile.lib diff --git a/src/protocol/http/http.c b/src/protocol/http/http.c index 8e82f2a72..1028882ea 100644 --- a/src/protocol/http/http.c +++ b/src/protocol/http/http.c @@ -9,12 +9,6 @@ #include #include #include -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include /* OS/2 needs this after sys/types.h */ -#endif #ifdef HAVE_LIMITS_H #include #endif @@ -599,35 +593,6 @@ accept_encoding_header(struct string *header) #endif } -/** Initialize *@a http_post so that done_http_post() can be safely - * called. - * - * @relates http_post */ -void -init_http_post(struct http_post *http_post) -{ - http_post->total_upload_length = 0; - http_post->uploaded = 0; - http_post->post_data = NULL; - http_post->post_fd = -1; -} - -/** Free all resources owned by *@a http_post, but do not free the - * structure itself. It is safe to call this multiple times. - * - * @relates http_post */ -void -done_http_post(struct http_post *http_post) -{ - http_post->total_upload_length = 0; - http_post->uploaded = 0; - http_post->post_data = NULL; - if (http_post->post_fd != -1) { - close(http_post->post_fd); - http_post->post_fd = -1; - } -} - /* This sets the Content-Length of POST data and counts files. */ static off_t post_length(unsigned char *post_data, unsigned int *count) @@ -662,126 +627,6 @@ post_length(unsigned char *post_data, unsigned int *count) #define POST_BUFFER_SIZE 4096 #define BIG_READ 655360 -/** @relates http_post */ -static int -read_http_post_inline(struct http_post *http_post, - unsigned char buffer[], int max) -{ - unsigned char *post = http_post->post_data; - unsigned char *end = strchr(post, FILE_CHAR); - int total = 0; - - assert(http_post->post_fd < 0); - if_assert_failed { errno = EINVAL; return -1; } - - if (!end) - end = strchr(post, '\0'); - - while (post < end && total < max) { - int h1, h2; - - h1 = unhx(post[0]); - assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d (%d/%c)", h1, post[0], post[0]); - if_assert_failed h1 = 0; - - h2 = unhx(post[1]); - assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d (%d/%c)", h2, post[0], post[0]); - if_assert_failed h2 = 0; - - buffer[total++] = (h1<<4) + h2; - post += 2; - } - if (post != end || *end != FILE_CHAR) { - http_post->post_data = post; - return total; - } - - end = strchr(post + 1, FILE_CHAR); - assert(end); - *end = '\0'; - http_post->post_fd = open(post + 1, O_RDONLY); - /* Be careful not to change errno here. */ - *end = FILE_CHAR; - if (http_post->post_fd < 0) { - http_post->post_data = post; - if (total > 0) - return total; /* retry the open on the next call */ - else - return -1; /* caller gets errno from open() */ - } - http_post->post_data = end + 1; - return total; -} - -/** @relates http_post */ -static int -read_http_post_fd(struct http_post *http_post, - unsigned char buffer[], int max) -{ - int ret; - - /* safe_read() would set errno = EBADF anyway, but check this - * explicitly to make any such bugs easier to detect. */ - assert(http_post->post_fd >= 0); - if_assert_failed { errno = EBADF; return -1; } - - ret = safe_read(http_post->post_fd, buffer, max); - if (ret <= 0) { - const int errno_from_read = errno; - - close(http_post->post_fd); - http_post->post_fd = -1; - - errno = errno_from_read; - } - return ret; -} - -/** Read data from connection.uri->post or from the files to which it - * refers. - * - * @return >0 if read that many bytes; 0 if EOF; -1 on error and set - * errno. - * - * @relates http_post */ -int -read_http_post(struct http_post *http_post, - unsigned char buffer[], int max) -{ - int total = 0; - - while (total < max) { - int chunk; - int post_fd = http_post->post_fd; - - if (post_fd < 0) - chunk = read_http_post_inline(http_post, - buffer + total, - max - total); - else - chunk = read_http_post_fd(http_post, - buffer + total, - max - total); - /* Be careful not to change errno here. */ - - if (chunk == 0 && http_post->post_fd == post_fd) - return total; /* EOF */ - if (chunk < 0) { - /* If some data has already been successfully - * read to buffer[], tell the caller about - * that and forget about the error. The next - * http_read_post_data() call will retry the - * operation that failed. */ - if (total != 0) - return total; - else - return chunk; /* caller gets errno from above */ - } - total += chunk; - } - return total; -} - static void send_more_post_data(struct socket *socket) { diff --git a/src/protocol/http/http.h b/src/protocol/http/http.h index 36485b8a0..df7a9c9df 100644 --- a/src/protocol/http/http.h +++ b/src/protocol/http/http.h @@ -4,6 +4,7 @@ #include "main/module.h" #include "protocol/http/blacklist.h" +#include "protocol/http/post.h" #include "protocol/protocol.h" struct connection; @@ -16,31 +17,6 @@ struct http_version { int minor; }; -/** State of reading POST data from connection.uri->post and related - * files. */ -struct http_post { - /** Total size of the POST body to be uploaded */ - off_t total_upload_length; - - /** Amount of POST body data uploaded so far */ - off_t uploaded; - - /** Points to the next byte to be read from connection.uri->post. - * Does not point to const because http_read_post() momentarily - * substitutes a null character for the FILE_CHAR at the end of - * each file name. */ - unsigned char *post_data; - - /** File descriptor from which data is being read, or -1 if - * none. */ - int post_fd; -}; - -void init_http_post(struct http_post *http_post); -void done_http_post(struct http_post *http_post); -int read_http_post(struct http_post *http_post, - unsigned char buffer[], int max); - /** connection.info points to this in HTTP and local CGI connections. */ struct http_connection_info { enum blacklist_flags bl_flags; diff --git a/src/protocol/http/post.c b/src/protocol/http/post.c new file mode 100644 index 000000000..cdaffd5fc --- /dev/null +++ b/src/protocol/http/post.c @@ -0,0 +1,171 @@ +/** Parsing uri.post and uploading files in a POST request. + * @file */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include /* OS/2 needs this after sys/types.h */ +#endif + +#include "elinks.h" + +#include "protocol/http/post.h" +#include "protocol/uri.h" +#include "util/conv.h" +#include "util/error.h" + +/** Initialize *@a http_post so that done_http_post() can be safely + * called. + * + * @relates http_post */ +void +init_http_post(struct http_post *http_post) +{ + http_post->total_upload_length = 0; + http_post->uploaded = 0; + http_post->post_data = NULL; + http_post->post_fd = -1; +} + +/** Free all resources owned by *@a http_post, but do not free the + * structure itself. It is safe to call this multiple times. + * + * @relates http_post */ +void +done_http_post(struct http_post *http_post) +{ + http_post->total_upload_length = 0; + http_post->uploaded = 0; + http_post->post_data = NULL; + if (http_post->post_fd != -1) { + close(http_post->post_fd); + http_post->post_fd = -1; + } +} + +/** @relates http_post */ +static int +read_http_post_inline(struct http_post *http_post, + unsigned char buffer[], int max) +{ + unsigned char *post = http_post->post_data; + unsigned char *end = strchr(post, FILE_CHAR); + int total = 0; + + assert(http_post->post_fd < 0); + if_assert_failed { errno = EINVAL; return -1; } + + if (!end) + end = strchr(post, '\0'); + + while (post < end && total < max) { + int h1, h2; + + h1 = unhx(post[0]); + assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d (%d/%c)", h1, post[0], post[0]); + if_assert_failed h1 = 0; + + h2 = unhx(post[1]); + assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d (%d/%c)", h2, post[0], post[0]); + if_assert_failed h2 = 0; + + buffer[total++] = (h1<<4) + h2; + post += 2; + } + if (post != end || *end != FILE_CHAR) { + http_post->post_data = post; + return total; + } + + end = strchr(post + 1, FILE_CHAR); + assert(end); + *end = '\0'; + http_post->post_fd = open(post + 1, O_RDONLY); + /* Be careful not to change errno here. */ + *end = FILE_CHAR; + if (http_post->post_fd < 0) { + http_post->post_data = post; + if (total > 0) + return total; /* retry the open on the next call */ + else + return -1; /* caller gets errno from open() */ + } + http_post->post_data = end + 1; + return total; +} + +/** @relates http_post */ +static int +read_http_post_fd(struct http_post *http_post, + unsigned char buffer[], int max) +{ + int ret; + + /* safe_read() would set errno = EBADF anyway, but check this + * explicitly to make any such bugs easier to detect. */ + assert(http_post->post_fd >= 0); + if_assert_failed { errno = EBADF; return -1; } + + ret = safe_read(http_post->post_fd, buffer, max); + if (ret <= 0) { + const int errno_from_read = errno; + + close(http_post->post_fd); + http_post->post_fd = -1; + + errno = errno_from_read; + } + return ret; +} + +/** Read data from connection.uri->post or from the files to which it + * refers. + * + * @return >0 if read that many bytes; 0 if EOF; -1 on error and set + * errno. + * + * @relates http_post */ +int +read_http_post(struct http_post *http_post, + unsigned char buffer[], int max) +{ + int total = 0; + + while (total < max) { + int chunk; + int post_fd = http_post->post_fd; + + if (post_fd < 0) + chunk = read_http_post_inline(http_post, + buffer + total, + max - total); + else + chunk = read_http_post_fd(http_post, + buffer + total, + max - total); + /* Be careful not to change errno here. */ + + if (chunk == 0 && http_post->post_fd == post_fd) + return total; /* EOF */ + if (chunk < 0) { + /* If some data has already been successfully + * read to buffer[], tell the caller about + * that and forget about the error. The next + * http_read_post_data() call will retry the + * operation that failed. */ + if (total != 0) + return total; + else + return chunk; /* caller gets errno from above */ + } + total += chunk; + } + return total; +} diff --git a/src/protocol/http/post.h b/src/protocol/http/post.h new file mode 100644 index 000000000..27c1c589e --- /dev/null +++ b/src/protocol/http/post.h @@ -0,0 +1,32 @@ +/** Parsing uri.post and uploading files in a POST request. + * @file */ + +#ifndef EL__PROTOCOL_HTTP_POST_H +#define EL__PROTOCOL_HTTP_POST_H + +/** State of reading POST data from connection.uri->post and related + * files. */ +struct http_post { + /** Total size of the POST body to be uploaded */ + off_t total_upload_length; + + /** Amount of POST body data uploaded so far */ + off_t uploaded; + + /** Points to the next byte to be read from connection.uri->post. + * Does not point to const because http_read_post() momentarily + * substitutes a null character for the FILE_CHAR at the end of + * each file name. */ + unsigned char *post_data; + + /** File descriptor from which data is being read, or -1 if + * none. */ + int post_fd; +}; + +void init_http_post(struct http_post *http_post); +void done_http_post(struct http_post *http_post); +int read_http_post(struct http_post *http_post, + unsigned char buffer[], int max); + +#endif