|  | @@ -8906,187 +8906,66 @@ get_remote_ip(const struct mg_connection *conn)
 | 
	
		
			
				|  |  |  #include "handle_form.inl"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int
 | 
	
		
			
				|  |  | -mg_upload(struct mg_connection *conn, const char *destination_dir)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -	/* TODO (high): completely rewrite this function. See issue #180. */
 | 
	
		
			
				|  |  | -	/* TODO (mid): set a timeout */
 | 
	
		
			
				|  |  | -	const char *content_type_header, *boundary_start, *sc;
 | 
	
		
			
				|  |  | -	char *s;
 | 
	
		
			
				|  |  | -	char buf[MG_BUF_LEN], path[PATH_MAX], tmp_path[PATH_MAX];
 | 
	
		
			
				|  |  | -	char fname[1024], boundary[100];
 | 
	
		
			
				|  |  | -	FILE *fp;
 | 
	
		
			
				|  |  | -	int bl, n, i, headers_len, boundary_len, eof, truncated;
 | 
	
		
			
				|  |  | -	int len = 0, num_uploaded_files = 0;
 | 
	
		
			
				|  |  | +struct mg_upload_user_data {
 | 
	
		
			
				|  |  | +	struct mg_connection *conn;
 | 
	
		
			
				|  |  | +	const char *destination_dir;
 | 
	
		
			
				|  |  | +	int num_uploaded_files;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	struct mg_request_info part_request_info;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	/* 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 = mg_strcasestr(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[99] = 0;
 | 
	
		
			
				|  |  | -	boundary_len = (int)strlen(boundary);
 | 
	
		
			
				|  |  | -	bl = boundary_len + 4; /* \r\n--<boundary> */
 | 
	
		
			
				|  |  | -	for (;;) {
 | 
	
		
			
				|  |  | -		/* Pull in headers */
 | 
	
		
			
				|  |  | -		/* assert(len >= 0 && len <= (int) sizeof(buf)); */
 | 
	
		
			
				|  |  | -		if ((len < 0) || (len > (int)sizeof(buf))) {
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		while ((n = mg_read(conn, buf + len, sizeof(buf) - (size_t)len)) > 0) {
 | 
	
		
			
				|  |  | -			len += n;
 | 
	
		
			
				|  |  | -			/* assert(len <= (int) sizeof(buf)); */
 | 
	
		
			
				|  |  | -			if (len > (int)sizeof(buf)) {
 | 
	
		
			
				|  |  | -				break;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		if ((headers_len = get_request_len(buf, len)) <= 0) {
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		/* terminate header */
 | 
	
		
			
				|  |  | -		buf[headers_len - 1] = 0;
 | 
	
		
			
				|  |  | +int
 | 
	
		
			
				|  |  | +mg_upload_field_found(const char *key,
 | 
	
		
			
				|  |  | +                      const char *filename,
 | 
	
		
			
				|  |  | +                      char *path,
 | 
	
		
			
				|  |  | +                      size_t pathlen,
 | 
	
		
			
				|  |  | +                      void *user_data)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int truncated = 0;
 | 
	
		
			
				|  |  | +	struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* Scan for the boundary string and skip it */
 | 
	
		
			
				|  |  | -		if (buf[0] == '-' && buf[1] == '-'
 | 
	
		
			
				|  |  | -		    && !memcmp(buf + 2, boundary, (size_t)boundary_len)) {
 | 
	
		
			
				|  |  | -			s = &buf[bl];
 | 
	
		
			
				|  |  | -		} else {
 | 
	
		
			
				|  |  | -			s = &buf[2];
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +	if (!filename) {
 | 
	
		
			
				|  |  | +		return FORM_FIELD_STORAGE_ABORT;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	mg_snprintf(fud->conn,
 | 
	
		
			
				|  |  | +	            &truncated,
 | 
	
		
			
				|  |  | +	            path,
 | 
	
		
			
				|  |  | +	            pathlen - 1,
 | 
	
		
			
				|  |  | +	            "%s/%s",
 | 
	
		
			
				|  |  | +	            fud->destination_dir,
 | 
	
		
			
				|  |  | +	            filename);
 | 
	
		
			
				|  |  | +	if (!truncated) {
 | 
	
		
			
				|  |  | +		return FORM_FIELD_STORAGE_ABORT;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return FORM_FIELD_STORAGE_STORE;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* Get headers for this part of the multipart message */
 | 
	
		
			
				|  |  | -		memset(&part_request_info, 0, sizeof(part_request_info));
 | 
	
		
			
				|  |  | -		parse_http_headers(&s, &part_request_info);
 | 
	
		
			
				|  |  | -		/* assert(&buf[headers_len-1] == s); */
 | 
	
		
			
				|  |  | -		if (&buf[headers_len - 1] != s) {
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* Fetch file name. */
 | 
	
		
			
				|  |  | -		sc = get_header(&part_request_info, "Content-Disposition");
 | 
	
		
			
				|  |  | -		if (!sc) {
 | 
	
		
			
				|  |  | -			/* invalid part of a multipart message */
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +int
 | 
	
		
			
				|  |  | +mg_upload_field_stored(const char *path, size_t file_size, void *user_data)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		sc = strstr(sc, "filename");
 | 
	
		
			
				|  |  | -		if (!sc) {
 | 
	
		
			
				|  |  | -			/* no filename set */
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		sc += 8; /* skip "filename" */
 | 
	
		
			
				|  |  | -		fname[0] = '\0';
 | 
	
		
			
				|  |  | -		IGNORE_UNUSED_RESULT(sscanf(sc, " = \"%1023[^\"]", fname));
 | 
	
		
			
				|  |  | -		fname[1023] = 0;
 | 
	
		
			
				|  |  | +	(void)file_size;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* Give up if the headers are not what we expect */
 | 
	
		
			
				|  |  | -		if (fname[0] == '\0') {
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +	fud->num_uploaded_files++;
 | 
	
		
			
				|  |  | +	fud->conn->ctx->callbacks.upload(fud->conn, path);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* Construct destination file name. Do not allow paths to have
 | 
	
		
			
				|  |  | -		 * slashes. */
 | 
	
		
			
				|  |  | -		if ((s = strrchr(fname, '/')) == NULL
 | 
	
		
			
				|  |  | -		    && (s = strrchr(fname, '\\')) == NULL) {
 | 
	
		
			
				|  |  | -			s = fname;
 | 
	
		
			
				|  |  | -		} else {
 | 
	
		
			
				|  |  | -			s++;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* There data is written to a temporary file first. */
 | 
	
		
			
				|  |  | -		/* Different users should use a different destination_dir. */
 | 
	
		
			
				|  |  | -		mg_snprintf(conn,
 | 
	
		
			
				|  |  | -		            &truncated,
 | 
	
		
			
				|  |  | -		            path,
 | 
	
		
			
				|  |  | -		            sizeof(path) - 1,
 | 
	
		
			
				|  |  | -		            "%s/%s",
 | 
	
		
			
				|  |  | -		            destination_dir,
 | 
	
		
			
				|  |  | -		            s);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		/* TODO(high): kick client on buffer overflow */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		strcpy(tmp_path, path);
 | 
	
		
			
				|  |  | -		strcat(tmp_path, "~");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		/* 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;
 | 
	
		
			
				|  |  | -		/* Open file in binary mode. */
 | 
	
		
			
				|  |  | -		if ((fp = fopen(tmp_path, "wb")) == NULL) {
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* Move data to the beginning of the buffer */
 | 
	
		
			
				|  |  | -		/* part_request_info is no longer valid after this operation */
 | 
	
		
			
				|  |  | -		/* assert(len >= headers_len); */
 | 
	
		
			
				|  |  | -		if (len < headers_len) {
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		memmove(buf, &buf[headers_len], (size_t)(len - headers_len));
 | 
	
		
			
				|  |  | -		len -= headers_len;
 | 
	
		
			
				|  |  | +int
 | 
	
		
			
				|  |  | +mg_upload(struct mg_connection *conn, const char *destination_dir)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct mg_upload_user_data fud = {conn, destination_dir, 0};
 | 
	
		
			
				|  |  | +	struct mg_form_data_handler fdh = {mg_upload_field_found,
 | 
	
		
			
				|  |  | +	                                   NULL,
 | 
	
		
			
				|  |  | +	                                   mg_upload_field_stored,
 | 
	
		
			
				|  |  | +	                                   &fud};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		/* Read POST data, write into file until boundary is found. */
 | 
	
		
			
				|  |  | -		eof = n = 0;
 | 
	
		
			
				|  |  | -		do {
 | 
	
		
			
				|  |  | -			len += n;
 | 
	
		
			
				|  |  | -			for (i = 0; i < len - bl; i++) {
 | 
	
		
			
				|  |  | -				if (!memcmp(&buf[i], "\r\n--", 4)
 | 
	
		
			
				|  |  | -				    && !memcmp(&buf[i + 4], boundary, (size_t)boundary_len)) {
 | 
	
		
			
				|  |  | -					/* Found boundary, that's the end of file data. */
 | 
	
		
			
				|  |  | -					fwrite(buf, 1, (size_t)i, fp);
 | 
	
		
			
				|  |  | -					eof = 1;
 | 
	
		
			
				|  |  | -					memmove(buf, &buf[i + bl], (size_t)(len - (i + bl)));
 | 
	
		
			
				|  |  | -					len -= i + bl;
 | 
	
		
			
				|  |  | -					break;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			if (!eof && len > bl) {
 | 
	
		
			
				|  |  | -				fwrite(buf, 1, (size_t)(len - bl), fp);
 | 
	
		
			
				|  |  | -				memmove(buf, &buf[len - bl], (size_t)bl);
 | 
	
		
			
				|  |  | -				len = bl;
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			if (!eof) {
 | 
	
		
			
				|  |  | -				n = mg_read(conn, buf + len, sizeof(buf) - ((size_t)(len)));
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		} while (!eof && (n > 0));
 | 
	
		
			
				|  |  | -		fclose(fp);
 | 
	
		
			
				|  |  | -		if (eof) {
 | 
	
		
			
				|  |  | -			remove(path);
 | 
	
		
			
				|  |  | -			rename(tmp_path, path);
 | 
	
		
			
				|  |  | -			num_uploaded_files++;
 | 
	
		
			
				|  |  | -			if (conn && conn->ctx && conn->ctx->callbacks.upload != NULL) {
 | 
	
		
			
				|  |  | -				conn->ctx->callbacks.upload(conn, path);
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		} else {
 | 
	
		
			
				|  |  | -			remove(tmp_path);
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +	int ret = mg_handle_form_request(conn, &fdh);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	return num_uploaded_files;
 | 
	
		
			
				|  |  | +	return fud.num_uploaded_files;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 |