diff --git a/examples/file-upload-multiple-posts/main.c b/examples/file-upload-multiple-posts/main.c index 0e34474a5a..c549b47133 100644 --- a/examples/file-upload-multiple-posts/main.c +++ b/examples/file-upload-multiple-posts/main.c @@ -10,7 +10,17 @@ static void cb(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_http_match_uri(hm, "/upload")) { - mg_http_upload(c, hm, &mg_fs_posix, "/tmp", 99999); + const char *tempfile = "/tmp/upload.data"; + long res = mg_http_upload(c, hm, &mg_fs_posix, tempfile, 99999); + if (res == 0) { + // Upload finished. Rename temprorary file into a target one + char path[80], file[64]; + mg_http_get_var(&hm->query, "file", file, sizeof(file)); + mg_snprintf(path, sizeof(path), "/tmp/%s", file); + if (mg_path_is_sane(path)) { + mg_fs_posix.mv(tempfile, path); + } + } } else { struct mg_http_serve_opts opts = {.root_dir = "web_root"}; mg_http_serve_dir(c, ev_data, &opts); diff --git a/mongoose.c b/mongoose.c index 6d25f19a60..86e94571ba 100644 --- a/mongoose.c +++ b/mongoose.c @@ -3134,28 +3134,20 @@ bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) { } long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size) { - char buf[20] = "0", file[40], path[MG_PATH_MAX]; + struct mg_fs *fs, const char *path, size_t max_size) { + char buf[20] = "0"; long res = 0, offset; mg_http_get_var(&hm->query, "offset", buf, sizeof(buf)); - mg_http_get_var(&hm->query, "file", file, sizeof(file)); offset = strtol(buf, NULL, 0); - mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file); if (hm->body.len == 0) { mg_http_reply(c, 200, "", "%ld", res); // Nothing to write - } else if (file[0] == '\0') { - mg_http_reply(c, 400, "", "file required"); - res = -1; - } else if (mg_path_is_sane(file) == false) { - mg_http_reply(c, 400, "", "%s: invalid file", file); - res = -2; } else if (offset < 0) { mg_http_reply(c, 400, "", "offset required"); - res = -3; + res = -1; } else if ((size_t) offset + hm->body.len > max_size) { mg_http_reply(c, 400, "", "%s: over max size of %lu", path, (unsigned long) max_size); - res = -4; + res = -2; } else { struct mg_fd *fd; size_t current_size = 0; @@ -3164,10 +3156,10 @@ long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, fs->st(path, ¤t_size, NULL); if (offset > 0 && current_size != (size_t) offset) { mg_http_reply(c, 400, "", "%s: offset mismatch", path); - res = -5; + res = -3; } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { mg_http_reply(c, 400, "", "open(%s): %d", path, errno); - res = -6; + res = -4; } else { res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len); mg_fs_close(fd); diff --git a/mongoose.h b/mongoose.h index 8295160a73..5c89f59959 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2287,7 +2287,7 @@ size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len); void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t); bool mg_http_match_uri(const struct mg_http_message *, const char *glob); long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size); + struct mg_fs *fs, const char *path, size_t max_size); void mg_http_bauth(struct mg_connection *, const char *user, const char *pass); struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v); size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *); diff --git a/src/http.c b/src/http.c index f3a19cc1c0..62fbf22456 100644 --- a/src/http.c +++ b/src/http.c @@ -918,28 +918,20 @@ bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) { } long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size) { - char buf[20] = "0", file[40], path[MG_PATH_MAX]; + struct mg_fs *fs, const char *path, size_t max_size) { + char buf[20] = "0"; long res = 0, offset; mg_http_get_var(&hm->query, "offset", buf, sizeof(buf)); - mg_http_get_var(&hm->query, "file", file, sizeof(file)); offset = strtol(buf, NULL, 0); - mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file); if (hm->body.len == 0) { mg_http_reply(c, 200, "", "%ld", res); // Nothing to write - } else if (file[0] == '\0') { - mg_http_reply(c, 400, "", "file required"); - res = -1; - } else if (mg_path_is_sane(file) == false) { - mg_http_reply(c, 400, "", "%s: invalid file", file); - res = -2; } else if (offset < 0) { mg_http_reply(c, 400, "", "offset required"); - res = -3; + res = -1; } else if ((size_t) offset + hm->body.len > max_size) { mg_http_reply(c, 400, "", "%s: over max size of %lu", path, (unsigned long) max_size); - res = -4; + res = -2; } else { struct mg_fd *fd; size_t current_size = 0; @@ -948,10 +940,10 @@ long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, fs->st(path, ¤t_size, NULL); if (offset > 0 && current_size != (size_t) offset) { mg_http_reply(c, 400, "", "%s: offset mismatch", path); - res = -5; + res = -3; } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { mg_http_reply(c, 400, "", "open(%s): %d", path, errno); - res = -6; + res = -4; } else { res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len); mg_fs_close(fd); diff --git a/src/http.h b/src/http.h index 17a28b8be1..0e810bb82b 100644 --- a/src/http.h +++ b/src/http.h @@ -59,7 +59,7 @@ size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len); void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t); bool mg_http_match_uri(const struct mg_http_message *, const char *glob); long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size); + struct mg_fs *fs, const char *path, size_t max_size); void mg_http_bauth(struct mg_connection *, const char *user, const char *pass); struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v); size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *); diff --git a/test/unit_test.c b/test/unit_test.c index 5d3654662a..78c1fd5f61 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -681,7 +681,7 @@ static void eh1(struct mg_connection *c, int ev, void *ev_data) { mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass)); mg_http_reply(c, 200, "", "[%s]:[%s]", user, pass); } else if (mg_http_match_uri(hm, "/upload")) { - mg_http_upload(c, hm, &mg_fs_posix, ".", 99999); + mg_http_upload(c, hm, &mg_fs_posix, "./uploaded.txt", 99999); c->is_hexdumping = 1; } else if (mg_http_match_uri(hm, "/test/")) { struct mg_http_serve_opts sopts; @@ -1062,10 +1062,6 @@ static void test_http_server(void) { char *p; remove("uploaded.txt"); ASSERT((p = mg_file_read(&mg_fs_posix, "uploaded.txt", NULL)) == NULL); - ASSERT(fetch(&mgr, buf, url, - "POST /upload HTTP/1.0\n" - "Content-Length: 1\n\nx") == 400); - ASSERT(fetch(&mgr, buf, url, "POST /upload?file=uploaded.txt HTTP/1.0\r\n" "Content-Length: 5\r\n" @@ -1080,18 +1076,6 @@ static void test_http_server(void) { remove("uploaded.txt"); } - { - // Test upload directory traversal - char *p; - remove("uploaded.txt"); - ASSERT((p = mg_file_read(&mg_fs_posix, "uploaded.txt", NULL)) == NULL); - ASSERT(fetch(&mgr, buf, url, - "POST /upload?file=../uploaded.txt HTTP/1.0\r\n" - "Content-Length: 5\r\n" - "\r\nhello") == 400); - ASSERT((p = mg_file_read(&mg_fs_posix, "uploaded.txt", NULL)) == NULL); - } - // HEAD request ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200); ASSERT(fetch(&mgr, buf, url, "HEAD /a.txt HTTP/1.0\n\n") == 200);