From c388bc2dd3fe88320808a515502713c6a40b40bb Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Fri, 14 Sep 2012 10:23:51 +0100
Subject: [PATCH] added upload example

---
 examples/upload.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 130 insertions(+)
 create mode 100644 examples/upload.c

diff --git a/examples/upload.c b/examples/upload.c
new file mode 100644
index 000000000..dea8cdd41
--- /dev/null
+++ b/examples/upload.c
@@ -0,0 +1,130 @@
+// Copyright (c) 2004-2012 Sergey Lyubka
+// This file is a part of mongoose project, http://github.com/valenok/mongoose
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include "mongoose.h"
+
+// Make sure that form has enctype="multipart/form-data" attribute
+static const char *html_form =
+  "<html><body>Upload example."
+  "<form method=\"POST\" action=\"/handle_post_request\" "
+  "  enctype=\"multipart/form-data\">"
+  "<input type=\"file\" name=\"file\" /> <br/>"
+  "<input type=\"submit\" value=\"Upload\" />"
+  "</form></body></html>";
+ 
+static const char *HTTP_500 = "HTTP/1.0 500 Server Error\r\n\r\n";
+
+static void handle_file_upload(struct mg_connection *conn) {
+  const char *cl_header;
+  char post_data[16 * 1024], path[999], file_name[1024], mime_type[100],
+       buf[BUFSIZ], *eop, *s, *p;
+  FILE *fp;
+  long long int cl, written;
+  int fd, n, post_data_len;
+
+  // Figure out total content length. Return if it is not present or invalid.
+  cl_header = mg_get_header(conn, "Content-Length");
+  if (cl_header == NULL || (cl = strtoll(cl_header, NULL, 10)) <= 0) {
+    mg_printf(conn, "%s%s", HTTP_500, "Invalid Conent-Length");
+    return;
+  }
+
+  // Read the initial chunk into memory. This should be multipart POST data.
+  // Parse headers, where we should find file name and content-type.
+  post_data_len = mg_read(conn, post_data, sizeof(post_data));
+  file_name[0] = mime_type[0] = '\0';
+  for (s = p = post_data; p < &post_data[post_data_len]; p++) {
+    if (p[0] == '\r' && p[1] == '\n') {
+      if (s == p) {
+        p += 2;
+        break;  // End of headers
+      }
+      p[0] = p[1] = '\0';
+      sscanf(s, "Content-Type: %99s", mime_type);
+      // TODO(lsm): don't expect filename to be the 3rd field,
+      // parse the header properly instead.
+      sscanf(s, "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
+             file_name);
+      s = p + 2;
+    }
+  }
+
+  // Finished parsing headers. Now "p" points to the first byte of data.
+  // Calculate file size
+  cl -= p - post_data;      // Subtract headers size
+  cl -= strlen(post_data);  // Subtract the boundary marker at the end
+  cl -= 6;                  // Subtract "\r\n" before and after boundary
+
+  // Construct destination file name. Write to /tmp, do not allow
+  // paths that contain slashes.
+  if ((s = strrchr(file_name, '/')) == NULL) {
+    s = file_name;
+  }
+  snprintf(path, sizeof(path), "/tmp/%s", s);
+
+  if (file_name[0] == '\0') {
+    mg_printf(conn, "%s%s", HTTP_500, "Can't get file name");
+  } else if (cl <= 0) {
+    mg_printf(conn, "%s%s", HTTP_500, "Empty file");
+  } else if ((fd = open(path, O_CREAT | O_TRUNC |
+                        O_WRONLY | O_EXLOCK | O_CLOEXEC)) < 0) {
+    // We're opening the file with exclusive lock held. This guarantee us that
+    // there is no other thread can save into the same file simultaneously.
+    mg_printf(conn, "%s%s", HTTP_500, "Cannot open file");
+  } else if ((fp = fdopen(fd, "w")) == NULL) {
+    mg_printf(conn, "%s%s", HTTP_500, "Cannot reopen file stream");
+    close(fd);
+  } else {
+    // Success. Write data into the file.
+    eop = post_data + post_data_len;
+    n = p + cl > eop ? (int) (eop - p) : (int) cl;
+    (void) fwrite(p, 1, n, fp);
+    written = n;
+    while (written < cl &&
+           (n = mg_read(conn, buf, cl - written > (long long) sizeof(buf) ?
+                        sizeof(buf) : cl - written)) > 0) {
+      (void) fwrite(buf, 1, n, fp);
+      written += n;
+    }
+    (void) fclose(fp);
+    mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n"
+              "Saved to [%s], written %llu bytes", path, cl);
+  }
+}
+
+static void *callback(enum mg_event event, struct mg_connection *conn) {
+  const struct mg_request_info *ri = mg_get_request_info(conn);
+
+  if (event == MG_NEW_REQUEST) {
+    if (!strcmp(ri->uri, "/handle_post_request")) {
+      handle_file_upload(conn);
+    } else {
+      // Show HTML form.
+      mg_printf(conn, "HTTP/1.0 200 OK\r\n"
+                "Content-Length: %d\r\n"
+                "Content-Type: text/html\r\n\r\n%s",
+                (int) strlen(html_form), html_form);
+    }
+    // Mark as processed
+    return "";
+  } else {
+    return NULL;
+  }
+}
+
+int main(void) {
+  struct mg_context *ctx;
+  const char *options[] = {"listening_ports", "8080", NULL};
+
+  ctx = mg_start(&callback, NULL, options);
+  getchar();  // Wait until user hits "enter"
+  mg_stop(ctx);
+
+  return 0;
+}
-- 
GitLab