diff --git a/examples/http-server/main.c b/examples/http-server/main.c index 9e1e19ae5a..d01f6209c2 100644 --- a/examples/http-server/main.c +++ b/examples/http-server/main.c @@ -9,6 +9,7 @@ static const char *s_root_dir = "."; static const char *s_listening_address = "http://0.0.0.0:8000"; static const char *s_enable_hexdump = "no"; static const char *s_ssi_pattern = "#.html"; +static const char *s_upload_dir = NULL; // File uploads disabled by default // Handle interrupts, like Ctrl-C static int s_signo; @@ -20,18 +21,45 @@ static void signal_handler(int signo) { // Simply serve static files from `s_root_dir` static void cb(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = ev_data, tmp = {0}; - struct mg_str unknown = mg_str_n("?", 1), *cl; - struct mg_http_serve_opts opts = {0}; - opts.root_dir = s_root_dir; - opts.ssi_pattern = s_ssi_pattern; - mg_http_serve_dir(c, hm, &opts); - mg_http_parse((char *) c->send.buf, c->send.len, &tmp); - cl = mg_http_get_header(&tmp, "Content-Length"); - if (cl == NULL) cl = &unknown; - MG_INFO(("%.*s %.*s %.*s %.*s", (int) hm->method.len, hm->method.ptr, - (int) hm->uri.len, hm->uri.ptr, (int) tmp.uri.len, tmp.uri.ptr, - (int) cl->len, cl->ptr)); + struct mg_http_message *hm = ev_data; + + if (mg_match(hm->uri, mg_str("/upload"), NULL)) { + // Serve file upload + if (s_upload_dir == NULL) { + mg_http_reply(c, 403, "", "Denied: file upload directory not set\n"); + } else { + struct mg_http_part part; + size_t pos = 0, total_bytes = 0, num_files = 0; + while ((pos = mg_http_next_multipart(hm->body, pos, &part)) > 0) { + char path[MG_PATH_MAX]; + MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes", + part.name.len, part.name.ptr, part.filename.len, + part.filename.ptr, part.body.len)); + mg_snprintf(path, sizeof(path), "%s/%.*s", s_upload_dir, + part.filename.len, part.filename.ptr); + if (mg_path_is_sane(path)) { + mg_file_write(&mg_fs_posix, path, part.body.ptr, part.body.len); + total_bytes += part.body.len; + num_files++; + } else { + MG_ERROR(("Rejecting dangerous path %s", path)); + } + } + mg_http_reply(c, 200, "", "Uploaded %lu files, %lu bytes\n", num_files, + total_bytes); + } + } else { + // Serve web root directory + struct mg_http_serve_opts opts = {0}; + opts.root_dir = s_root_dir; + opts.ssi_pattern = s_ssi_pattern; + mg_http_serve_dir(c, hm, &opts); + } + + // Log request + MG_INFO(("%.*s %.*s %lu -> %.*s %lu", hm->method.len, hm->method.ptr, + hm->uri.len, hm->uri.ptr, hm->body.len, 3, c->send.buf + 9, + c->send.len)); } } @@ -43,6 +71,7 @@ static void usage(const char *prog) { " -S PAT - SSI filename pattern, default: '%s'\n" " -d DIR - directory to serve, default: '%s'\n" " -l ADDR - listening address, default: '%s'\n" + " -u DIR - file upload directory, default: unset\n" " -v LEVEL - debug level, from 0 to 4, default: %d\n", MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir, s_listening_address, s_debug_level); @@ -65,6 +94,8 @@ int main(int argc, char *argv[]) { s_ssi_pattern = argv[++i]; } else if (strcmp(argv[i], "-l") == 0) { s_listening_address = argv[++i]; + } else if (strcmp(argv[i], "-u") == 0) { + s_upload_dir = argv[++i]; } else if (strcmp(argv[i], "-v") == 0) { s_debug_level = atoi(argv[++i]); } else { @@ -95,6 +126,7 @@ int main(int argc, char *argv[]) { MG_INFO(("Mongoose version : v%s", MG_VERSION)); MG_INFO(("Listening on : %s", s_listening_address)); MG_INFO(("Web root : [%s]", s_root_dir)); + MG_INFO(("Upload dir : [%s]", s_upload_dir ? s_upload_dir : "unset")); while (s_signo == 0) mg_mgr_poll(&mgr, 1000); mg_mgr_free(&mgr); MG_INFO(("Exiting on signal %d", s_signo));