|  | @@ -747,7 +747,7 @@ static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  // Skip the characters until one of the delimiters characters found.
 |  |  // Skip the characters until one of the delimiters characters found.
 | 
											
												
													
														|  | -// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
 |  | 
 | 
											
												
													
														|  | 
 |  | +// 0-terminate resulting word. Skip the delimiter and following whitespaces.
 | 
											
												
													
														|  |  // Advance pointer to buffer to the next word. Return found 0-terminated word.
 |  |  // Advance pointer to buffer to the next word. Return found 0-terminated word.
 | 
											
												
													
														|  |  // Delimiters can be quoted with quotechar.
 |  |  // Delimiters can be quoted with quotechar.
 | 
											
												
													
														|  |  static char *skip_quoted(char **buf, const char *delimiters,
 |  |  static char *skip_quoted(char **buf, const char *delimiters,
 | 
											
										
											
												
													
														|  | @@ -4035,6 +4035,113 @@ static void handle_lsp_request(struct mg_connection *conn, const char *path,
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  #endif // USE_LUA
 |  |  #endif // USE_LUA
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +int mg_upload(struct mg_connection *conn, const char *destination_dir) {
 | 
											
												
													
														|  | 
 |  | +  const char *content_type_header, *boundary_start;
 | 
											
												
													
														|  | 
 |  | +  char buf[8192], path[PATH_MAX], fname[1024], boundary[100], *s;
 | 
											
												
													
														|  | 
 |  | +  FILE *fp;
 | 
											
												
													
														|  | 
 |  | +  int bl, n, i, j, headers_len, boundary_len, len = 0, num_uploaded_files = 0;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  // Request looks like this:
 | 
											
												
													
														|  | 
 |  | +  //
 | 
											
												
													
														|  | 
 |  | +  // POST /upload HTTP/1.1
 | 
											
												
													
														|  | 
 |  | +  // Host: 127.0.0.1:8080
 | 
											
												
													
														|  | 
 |  | +  // Content-Length: 244894
 | 
											
												
													
														|  | 
 |  | +  // Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRVr
 | 
											
												
													
														|  | 
 |  | +  //
 | 
											
												
													
														|  | 
 |  | +  // ------WebKitFormBoundaryRVr
 | 
											
												
													
														|  | 
 |  | +  // Content-Disposition: form-data; name="file"; filename="accum.png"
 | 
											
												
													
														|  | 
 |  | +  // Content-Type: image/png
 | 
											
												
													
														|  | 
 |  | +  //
 | 
											
												
													
														|  | 
 |  | +  //  <89>PNG
 | 
											
												
													
														|  | 
 |  | +  //  <PNG DATA>
 | 
											
												
													
														|  | 
 |  | +  // ------WebKitFormBoundaryRVr
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  // Extract boundary string from the Content-Type header
 | 
											
												
													
														|  | 
 |  | +  if ((content_type_header = mg_get_header(conn, "Content-Type")) == NULL ||
 | 
											
												
													
														|  | 
 |  | +      (boundary_start = strstr(content_type_header, "boundary=")) == NULL ||
 | 
											
												
													
														|  | 
 |  | +      (sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 &&
 | 
											
												
													
														|  | 
 |  | +       sscanf(boundary_start, "boundary=%99s", boundary) == 0) ||
 | 
											
												
													
														|  | 
 |  | +      boundary[0] == '\0') {
 | 
											
												
													
														|  | 
 |  | +    return num_uploaded_files;
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  boundary_len = strlen(boundary);
 | 
											
												
													
														|  | 
 |  | +  bl = boundary_len + 4;  // \r\n--<boundary>
 | 
											
												
													
														|  | 
 |  | +  for (;;) {
 | 
											
												
													
														|  | 
 |  | +    // Pull in headers
 | 
											
												
													
														|  | 
 |  | +    assert(len >= 0 && len <= (int) sizeof(buf));
 | 
											
												
													
														|  | 
 |  | +    while ((n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0) {
 | 
											
												
													
														|  | 
 |  | +      len += n;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +    if ((headers_len = get_request_len(buf, len)) <= 0) {
 | 
											
												
													
														|  | 
 |  | +      break;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    // Fetch file name.
 | 
											
												
													
														|  | 
 |  | +    fname[0] = '\0';
 | 
											
												
													
														|  | 
 |  | +    for (i = j = 0; i < headers_len; i++) {
 | 
											
												
													
														|  | 
 |  | +      if (buf[i] == '\r' && buf[i + 1] == '\n') {
 | 
											
												
													
														|  | 
 |  | +        buf[i] = buf[i + 1] = '\0';
 | 
											
												
													
														|  | 
 |  | +        // TODO(lsm): don't expect filename to be the 3rd field,
 | 
											
												
													
														|  | 
 |  | +        // parse the header properly instead.
 | 
											
												
													
														|  | 
 |  | +        sscanf(&buf[j], "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
 | 
											
												
													
														|  | 
 |  | +               fname);
 | 
											
												
													
														|  | 
 |  | +        j = i + 2;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    // Give up if the headers are not what we expect
 | 
											
												
													
														|  | 
 |  | +    if (fname[0] == '\0') {
 | 
											
												
													
														|  | 
 |  | +      break;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    // Move data to the beginning of the buffer
 | 
											
												
													
														|  | 
 |  | +    assert(len >= headers_len);
 | 
											
												
													
														|  | 
 |  | +    memmove(buf, &buf[headers_len], len - headers_len);
 | 
											
												
													
														|  | 
 |  | +    len -= headers_len;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    // We open the file with exclusive lock held. This guarantee us
 | 
											
												
													
														|  | 
 |  | +    // there is no other thread can save into the same file simultaneously.
 | 
											
												
													
														|  | 
 |  | +    fp = NULL;
 | 
											
												
													
														|  | 
 |  | +    // Construct destination file name. Do not allow paths to have slashes.
 | 
											
												
													
														|  | 
 |  | +    if ((s = strrchr(fname, '/')) == NULL) {
 | 
											
												
													
														|  | 
 |  | +      s = fname;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +    // Open file in binary mode with exclusive lock set
 | 
											
												
													
														|  | 
 |  | +    snprintf(path, sizeof(path), "%s/%s", destination_dir, s);
 | 
											
												
													
														|  | 
 |  | +    if ((fp = fopen(path, "wbx")) == NULL) {
 | 
											
												
													
														|  | 
 |  | +      break;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    // Read POST data, write into file until boundary is found.
 | 
											
												
													
														|  | 
 |  | +    n = 0;
 | 
											
												
													
														|  | 
 |  | +    do {
 | 
											
												
													
														|  | 
 |  | +      len += n;
 | 
											
												
													
														|  | 
 |  | +      for (i = 0; i < len - bl; i++) {
 | 
											
												
													
														|  | 
 |  | +        if (!memcmp(&buf[i], "\r\n--", 4) &&
 | 
											
												
													
														|  | 
 |  | +            !memcmp(&buf[i + 4], boundary, boundary_len)) {
 | 
											
												
													
														|  | 
 |  | +          // Found boundary, that's the end of file data.
 | 
											
												
													
														|  | 
 |  | +          (void) fwrite(buf, 1, i, fp);
 | 
											
												
													
														|  | 
 |  | +          num_uploaded_files++;
 | 
											
												
													
														|  | 
 |  | +          conn->request_info.ev_data = (void *) path;
 | 
											
												
													
														|  | 
 |  | +          call_user(conn, MG_UPLOAD);
 | 
											
												
													
														|  | 
 |  | +          memmove(buf, &buf[i + bl], len - (i + bl));
 | 
											
												
													
														|  | 
 |  | +          len -= i + bl;
 | 
											
												
													
														|  | 
 |  | +          break;
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +      if (len > bl) {
 | 
											
												
													
														|  | 
 |  | +        fwrite(buf, 1, len - bl, fp);
 | 
											
												
													
														|  | 
 |  | +        memmove(buf, &buf[len - bl], len - bl);
 | 
											
												
													
														|  | 
 |  | +        len = bl;
 | 
											
												
													
														|  | 
 |  | +      }
 | 
											
												
													
														|  | 
 |  | +    } while ((n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0);
 | 
											
												
													
														|  | 
 |  | +    fclose(fp);
 | 
											
												
													
														|  | 
 |  | +  }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +  return num_uploaded_files;
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  // This is the heart of the Mongoose's logic.
 |  |  // This is the heart of the Mongoose's logic.
 | 
											
												
													
														|  |  // This function is called when the request is read, parsed and validated,
 |  |  // This function is called when the request is read, parsed and validated,
 | 
											
												
													
														|  |  // and Mongoose must decide what action to take: serve a file, or
 |  |  // and Mongoose must decide what action to take: serve a file, or
 |