diff --git a/src/network/state.c b/src/network/state.c index b5e4f098..910274c5 100644 --- a/src/network/state.c +++ b/src/network/state.c @@ -70,6 +70,7 @@ static const struct s_msg_dsc msg_dsc[] = { {S_HTTP_ERROR, N_("Bad HTTP response")}, {S_HTTP_204, N_("No content")}, + {S_HTTP_UPLOAD_RESIZED, N_("File was resized during upload")}, {S_FILE_TYPE, N_("Unknown file type")}, {S_FILE_ERROR, N_("Error opening file")}, diff --git a/src/network/state.h b/src/network/state.h index e5e443a6..4583241b 100644 --- a/src/network/state.h +++ b/src/network/state.h @@ -72,6 +72,7 @@ enum connection_state { S_HTTP_ERROR = -100100, S_HTTP_204 = -100101, + S_HTTP_UPLOAD_RESIZED = -100102, S_FILE_TYPE = -100200, S_FILE_ERROR = -100201, diff --git a/src/protocol/http/post.c b/src/protocol/http/post.c index b4be5bc1..e8961916 100644 --- a/src/protocol/http/post.c +++ b/src/protocol/http/post.c @@ -35,6 +35,7 @@ init_http_post(struct http_post *http_post) http_post->post_fd = -1; http_post->file_index = 0; http_post->file_count = 0; + http_post->file_read = 0; http_post->files = NULL; } @@ -54,6 +55,7 @@ done_http_post(struct http_post *http_post) } http_post->file_index = 0; http_post->file_count = 0; + http_post->file_read = 0; mem_free_set(&http_post->files, NULL); } @@ -170,6 +172,7 @@ read_http_post_inline(struct http_post *http_post, return total; } + http_post->file_read = 0; end = strchr(post + 1, FILE_CHAR); assert(end); *end = '\0'; @@ -199,6 +202,8 @@ read_http_post_fd(struct http_post *http_post, unsigned char buffer[], int max, enum connection_state *error) { + const struct http_post_file *const file + = &http_post->files[http_post->file_index]; int ret; /* safe_read() would set errno = EBADF anyway, but check this @@ -213,18 +218,42 @@ read_http_post_fd(struct http_post *http_post, close(http_post->post_fd); http_post->post_fd = -1; http_post->file_index++; + /* http_post->file_read is used below so don't clear it here. + * It will be cleared when the next file is opened. */ - if (ret == 0) { + if (ret == -1) { + *error = -errno_from_read; + return -1; + } else if (http_post->file_read != file->size) { + /* ELinks already sent a Content-Length header + * based on the size of this file, but the + * file has since been shrunk. Abort the + * connection because ELinks can no longer get + * enough data to fill the Content-Length. + * (Well, it could pad with zeroes, but that + * would be just weird.) */ + *error = S_HTTP_UPLOAD_RESIZED; + return -1; + } else { /* The upload file ended but there may still * be more data in uri.post. If not, * read_http_post_inline() will return 0 to * indicate the final end of file. */ return -2; - } else { - *error = -errno_from_read; - return -1; } } + + http_post->file_read += ret; + if (http_post->file_read > file->size) { + /* ELinks already sent a Content-Length header based + * on the size of this file, but the file has since + * been extended. Abort the connection because ELinks + * can no longer fit the entire file in the original + * Content-Length. */ + *error = S_HTTP_UPLOAD_RESIZED; + return -1; + } + return ret; } diff --git a/src/protocol/http/post.h b/src/protocol/http/post.h index 2fb79464..f17349d9 100644 --- a/src/protocol/http/post.h +++ b/src/protocol/http/post.h @@ -38,9 +38,14 @@ struct http_post { * be read next (when post_fd == -1). */ size_t file_index; - /** Number of elements in the #files array. */ + /** Number of files to be uploaded, i.e. the number of + * elements in the #files array. */ size_t file_count; + /** Number of bytes read from the current file so far. + * The value makes sense only when post_fd != -1. */ + off_t file_read; + /** Array of information about files to be uploaded. */ struct http_post_file *files; };