1/* Copyright (c) 2016 the Civetweb developers
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
20 */
21
22
23static int
24url_encoded_field_found(const struct mg_connection *conn,
25                        const char *key,
26                        size_t key_len,
27                        const char *filename,
28                        size_t filename_len,
29                        char *path,
30                        size_t path_len,
31                        struct mg_form_data_handler *fdh)
32{
33	char key_dec[1024];
34	char filename_dec[1024];
35	int key_dec_len;
36	int filename_dec_len;
37	int ret;
38
39	key_dec_len =
40	    mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
41
42	if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
43		return FORM_FIELD_STORAGE_SKIP;
44	}
45
46	if (filename) {
47		filename_dec_len = mg_url_decode(filename,
48		                                 (int)filename_len,
49		                                 filename_dec,
50		                                 (int)sizeof(filename_dec),
51		                                 1);
52
53		if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
54		    || (filename_dec_len < 0)) {
55			/* Log error message and skip this field. */
56			mg_cry(conn, "%s: Cannot decode filename", __func__);
57			return FORM_FIELD_STORAGE_SKIP;
58		}
59	} else {
60		filename_dec[0] = 0;
61	}
62
63	ret =
64	    fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
65
66	if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) {
67		if (fdh->field_get == NULL) {
68			mg_cry(conn, "%s: Function \"Get\" not available", __func__);
69			return FORM_FIELD_STORAGE_SKIP;
70		}
71	}
72	if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) {
73		if (fdh->field_store == NULL) {
74			mg_cry(conn, "%s: Function \"Store\" not available", __func__);
75			return FORM_FIELD_STORAGE_SKIP;
76		}
77	}
78
79	return ret;
80}
81
82
83static int
84url_encoded_field_get(const struct mg_connection *conn,
85                      const char *key,
86                      size_t key_len,
87                      const char *value,
88                      size_t value_len,
89                      struct mg_form_data_handler *fdh)
90{
91	char key_dec[1024];
92
93	char *value_dec = mg_malloc(value_len + 1);
94	int value_dec_len, ret;
95
96	if (!value_dec) {
97		/* Log error message and stop parsing the form data. */
98		mg_cry(conn,
99		       "%s: Not enough memory (required: %lu)",
100		       __func__,
101		       (unsigned long)(value_len + 1));
102		return FORM_FIELD_STORAGE_ABORT;
103	}
104
105	mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
106
107	value_dec_len =
108	    mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
109
110	ret = fdh->field_get(key_dec,
111	                     value_dec,
112	                     (size_t)value_dec_len,
113	                     fdh->user_data);
114
115	mg_free(value_dec);
116
117	return ret;
118}
119
120
121static int
122unencoded_field_get(const struct mg_connection *conn,
123                    const char *key,
124                    size_t key_len,
125                    const char *value,
126                    size_t value_len,
127                    struct mg_form_data_handler *fdh)
128{
129	char key_dec[1024];
130	(void)conn;
131
132	mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
133
134	return fdh->field_get(key_dec, value, value_len, fdh->user_data);
135}
136
137
138static int
139field_stored(const struct mg_connection *conn,
140             const char *path,
141             long long file_size,
142             struct mg_form_data_handler *fdh)
143{
144	/* Equivalent to "upload" callback of "mg_upload". */
145
146	(void)conn; /* we do not need mg_cry here, so conn is currently unused */
147
148	return fdh->field_store(path, file_size, fdh->user_data);
149}
150
151
152static const char *
153search_boundary(const char *buf,
154                size_t buf_len,
155                const char *boundary,
156                size_t boundary_len)
157{
158	/* We must do a binary search here, not a string search, since the buffer
159	 * may contain '\x00' bytes, if binary data is transferred. */
160	int clen = (int)buf_len - (int)boundary_len - 4;
161	int i;
162
163	for (i = 0; i <= clen; i++) {
164		if (!memcmp(buf + i, "\r\n--", 4)) {
165			if (!memcmp(buf + i + 4, boundary, boundary_len)) {
166				return buf + i;
167			}
168		}
169	}
170	return NULL;
171}
172
173
174int
175mg_handle_form_request(struct mg_connection *conn,
176                       struct mg_form_data_handler *fdh)
177{
178	const char *content_type;
179	char path[512];
180	char buf[1024];
181	int field_storage;
182	int buf_fill = 0;
183	int r;
184	int field_count = 0;
185	struct mg_file fstore = STRUCT_FILE_INITIALIZER;
186	int64_t file_size = 0; /* init here, to a avoid a false positive
187	                         "uninitialized variable used" warning */
188
189	int has_body_data =
190	    (conn->request_info.content_length > 0) || (conn->is_chunked);
191
192	/* There are three ways to encode data from a HTML form:
193	 * 1) method: GET (default)
194	 *    The form data is in the HTTP query string.
195	 * 2) method: POST, enctype: "application/x-www-form-urlencoded"
196	 *    The form data is in the request body.
197	 *    The body is url encoded (the default encoding for POST).
198	 * 3) method: POST, enctype: "multipart/form-data".
199	 *    The form data is in the request body of a multipart message.
200	 *    This is the typical way to handle file upload from a form.
201	 */
202
203	if (!has_body_data) {
204		const char *data;
205
206		if (strcmp(conn->request_info.request_method, "GET")) {
207			/* No body data, but not a GET request.
208			 * This is not a valid form request. */
209			return -1;
210		}
211
212		/* GET request: form data is in the query string. */
213		/* The entire data has already been loaded, so there is no nead to
214		 * call mg_read. We just need to split the query string into key-value
215		 * pairs. */
216		data = conn->request_info.query_string;
217		if (!data) {
218			/* No query string. */
219			return -1;
220		}
221
222		/* Split data in a=1&b=xy&c=3&c=4 ... */
223		while (*data) {
224			const char *val = strchr(data, '=');
225			const char *next;
226			ptrdiff_t keylen, vallen;
227
228			if (!val) {
229				break;
230			}
231			keylen = val - data;
232
233			/* In every "field_found" callback we ask what to do with the
234			 * data ("field_storage"). This could be:
235			 * FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field
236			 * FORM_FIELD_STORAGE_GET (1) ... read the data and call the get
237			 *                              callback function
238			 * FORM_FIELD_STORAGE_STORE (2) ... store the data in a file
239			 * FORM_FIELD_STORAGE_READ (3) ... let the user read the data
240			 *                               (for parsing long data on the fly)
241			 *                               (currently not implemented)
242			 * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing
243			 */
244			memset(path, 0, sizeof(path));
245			field_count++;
246			field_storage = url_encoded_field_found(conn,
247			                                        data,
248			                                        (size_t)keylen,
249			                                        NULL,
250			                                        0,
251			                                        path,
252			                                        sizeof(path) - 1,
253			                                        fdh);
254
255			val++;
256			next = strchr(val, '&');
257			if (next) {
258				vallen = next - val;
259				next++;
260			} else {
261				vallen = (ptrdiff_t)strlen(val);
262				next = val + vallen;
263			}
264
265			if (field_storage == FORM_FIELD_STORAGE_GET) {
266				/* Call callback */
267				url_encoded_field_get(
268				    conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
269			}
270			if (field_storage == FORM_FIELD_STORAGE_STORE) {
271				/* Store the content to a file */
272				if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
273					fstore.access.fp = NULL;
274				}
275				file_size = 0;
276				if (fstore.access.fp != NULL) {
277					size_t n = (size_t)
278					    fwrite(val, 1, (size_t)vallen, fstore.access.fp);
279					if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
280						mg_cry(conn,
281						       "%s: Cannot write file %s",
282						       __func__,
283						       path);
284						(void)mg_fclose(&fstore.access);
285						remove_bad_file(conn, path);
286					}
287					file_size += (int64_t)n;
288
289					if (fstore.access.fp) {
290						r = mg_fclose(&fstore.access);
291						if (r == 0) {
292							/* stored successfully */
293							field_stored(conn, path, file_size, fdh);
294						} else {
295							mg_cry(conn,
296							       "%s: Error saving file %s",
297							       __func__,
298							       path);
299							remove_bad_file(conn, path);
300						}
301						fstore.access.fp = NULL;
302					}
303
304				} else {
305					mg_cry(conn, "%s: Cannot create file %s", __func__, path);
306				}
307			}
308
309			/* if (field_storage == FORM_FIELD_STORAGE_READ) { */
310			/* The idea of "field_storage=read" is to let the API user read
311			 * data chunk by chunk and to some data processing on the fly.
312			 * This should avoid the need to store data in the server:
313			 * It should neither be stored in memory, like
314			 * "field_storage=get" does, nor in a file like
315			 * "field_storage=store".
316			 * However, for a "GET" request this does not make any much
317			 * sense, since the data is already stored in memory, as it is
318			 * part of the query string.
319			 */
320			/* } */
321
322			if ((field_storage & FORM_FIELD_STORAGE_ABORT)
323			    == FORM_FIELD_STORAGE_ABORT) {
324				/* Stop parsing the request */
325				break;
326			}
327
328			/* Proceed to next entry */
329			data = next;
330		}
331
332		return field_count;
333	}
334
335	content_type = mg_get_header(conn, "Content-Type");
336
337	if (!content_type
338	    || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
339	    || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
340		/* The form data is in the request body data, encoded in key/value
341		 * pairs. */
342		int all_data_read = 0;
343
344		/* Read body data and split it in keys and values.
345		 * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
346		 * Here we use "POST", and read the data from the request body.
347		 * The data read on the fly, so it is not required to buffer the
348		 * entire request in memory before processing it. */
349		for (;;) {
350			const char *val;
351			const char *next;
352			ptrdiff_t keylen, vallen;
353			ptrdiff_t used;
354			int end_of_key_value_pair_found = 0;
355			int get_block;
356
357			if ((size_t)buf_fill < (sizeof(buf) - 1)) {
358
359				size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
360				r = mg_read(conn, buf + (size_t)buf_fill, to_read);
361				if (r < 0) {
362					/* read error */
363					return -1;
364				}
365				if (r != (int)to_read) {
366					/* TODO: Create a function to get "all_data_read" from
367					 * the conn object. All data is read if the Content-Length
368					 * has been reached, or if chunked encoding is used and
369					 * the end marker has been read, or if the connection has
370					 * been closed. */
371					all_data_read = 1;
372				}
373				buf_fill += r;
374				buf[buf_fill] = 0;
375				if (buf_fill < 1) {
376					break;
377				}
378			}
379
380			val = strchr(buf, '=');
381
382			if (!val) {
383				break;
384			}
385			keylen = val - buf;
386			val++;
387
388			/* Call callback */
389			memset(path, 0, sizeof(path));
390			field_count++;
391			field_storage = url_encoded_field_found(conn,
392			                                        buf,
393			                                        (size_t)keylen,
394			                                        NULL,
395			                                        0,
396			                                        path,
397			                                        sizeof(path) - 1,
398			                                        fdh);
399
400			if ((field_storage & FORM_FIELD_STORAGE_ABORT)
401			    == FORM_FIELD_STORAGE_ABORT) {
402				/* Stop parsing the request */
403				break;
404			}
405
406			if (field_storage == FORM_FIELD_STORAGE_STORE) {
407				if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
408					fstore.access.fp = NULL;
409				}
410				file_size = 0;
411				if (!fstore.access.fp) {
412					mg_cry(conn, "%s: Cannot create file %s", __func__, path);
413				}
414			}
415
416			get_block = 0;
417			/* Loop to read values larger than sizeof(buf)-keylen-2 */
418			do {
419				next = strchr(val, '&');
420				if (next) {
421					vallen = next - val;
422					next++;
423					end_of_key_value_pair_found = 1;
424				} else {
425					vallen = (ptrdiff_t)strlen(val);
426					next = val + vallen;
427				}
428
429				if (field_storage == FORM_FIELD_STORAGE_GET) {
430#if 0
431					if (!end_of_key_value_pair_found && !all_data_read) {
432						/* This callback will deliver partial contents */
433					}
434#else
435					(void)all_data_read; /* avoid warning */
436#endif
437
438					/* Call callback */
439					url_encoded_field_get(conn,
440					                      ((get_block > 0) ? NULL : buf),
441					                      ((get_block > 0) ? 0
442					                                       : (size_t)keylen),
443					                      val,
444					                      (size_t)vallen,
445					                      fdh);
446					get_block++;
447				}
448				if (fstore.access.fp) {
449					size_t n = (size_t)
450					    fwrite(val, 1, (size_t)vallen, fstore.access.fp);
451					if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
452						mg_cry(conn,
453						       "%s: Cannot write file %s",
454						       __func__,
455						       path);
456						mg_fclose(&fstore.access);
457						remove_bad_file(conn, path);
458					}
459					file_size += (int64_t)n;
460				}
461
462				if (!end_of_key_value_pair_found) {
463					used = next - buf;
464					memmove(buf,
465					        buf + (size_t)used,
466					        sizeof(buf) - (size_t)used);
467					buf_fill -= (int)used;
468					if ((size_t)buf_fill < (sizeof(buf) - 1)) {
469
470						size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
471						r = mg_read(conn, buf + (size_t)buf_fill, to_read);
472						if (r < 0) {
473							/* read error */
474							return -1;
475						}
476						if (r != (int)to_read) {
477							/* TODO: Create a function to get "all_data_read"
478							 * from the conn object. All data is read if the
479							 * Content-Length has been reached, or if chunked
480							 * encoding is used and the end marker has been
481							 * read, or if the connection has been closed. */
482							all_data_read = 1;
483						}
484						buf_fill += r;
485						buf[buf_fill] = 0;
486						if (buf_fill < 1) {
487							break;
488						}
489						val = buf;
490					}
491				}
492
493			} while (!end_of_key_value_pair_found);
494
495			if (fstore.access.fp) {
496				r = mg_fclose(&fstore.access);
497				if (r == 0) {
498					/* stored successfully */
499					field_stored(conn, path, file_size, fdh);
500				} else {
501					mg_cry(conn, "%s: Error saving file %s", __func__, path);
502					remove_bad_file(conn, path);
503				}
504				fstore.access.fp = NULL;
505			}
506
507			/* Proceed to next entry */
508			used = next - buf;
509			memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
510			buf_fill -= (int)used;
511		}
512
513		return field_count;
514	}
515
516	if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
517		/* The form data is in the request body data, encoded as multipart
518		 * content (see https://www.ietf.org/rfc/rfc1867.txt,
519		 * https://www.ietf.org/rfc/rfc2388.txt). */
520		const char *boundary;
521		size_t bl;
522		ptrdiff_t used;
523		struct mg_request_info part_header;
524		char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend;
525		const char *content_disp;
526		const char *next;
527
528		memset(&part_header, 0, sizeof(part_header));
529
530		/* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
531		bl = 20;
532		while (content_type[bl] == ' ') {
533			bl++;
534		}
535
536		/* There has to be a BOUNDARY definition in the Content-Type header */
537		if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
538			/* Malformed request */
539			return -1;
540		}
541
542		boundary = content_type + bl + 9;
543		bl = strlen(boundary);
544
545		if (bl + 800 > sizeof(buf)) {
546			/* Sanity check:  The algorithm can not work if bl >= sizeof(buf),
547			 * and it will not work effectively, if the buf is only a few byte
548			 * larger than bl, or it buf can not hold the multipart header
549			 * plus the boundary.
550			 * Check some reasonable number here, that should be fulfilled by
551			 * any reasonable request from every browser. If it is not
552			 * fulfilled, it might be a hand-made request, intended to
553			 * interfere with the algorithm. */
554			return -1;
555		}
556
557		for (;;) {
558			size_t towrite, n;
559			int get_block;
560
561			r = mg_read(conn,
562			            buf + (size_t)buf_fill,
563			            sizeof(buf) - 1 - (size_t)buf_fill);
564			if (r < 0) {
565				/* read error */
566				return -1;
567			}
568			buf_fill += r;
569			buf[buf_fill] = 0;
570			if (buf_fill < 1) {
571				/* No data */
572				return -1;
573			}
574
575			if (buf[0] != '-' || buf[1] != '-') {
576				/* Malformed request */
577				return -1;
578			}
579			if (strncmp(buf + 2, boundary, bl)) {
580				/* Malformed request */
581				return -1;
582			}
583			if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
584				/* Every part must end with \r\n, if there is another part.
585				 * The end of the request has an extra -- */
586				if (((size_t)buf_fill != (size_t)(bl + 6))
587				    || (strncmp(buf + bl + 2, "--\r\n", 4))) {
588					/* Malformed request */
589					return -1;
590				}
591				/* End of the request */
592				break;
593			}
594
595			/* Next, we need to get the part header: Read until \r\n\r\n */
596			hbuf = buf + bl + 4;
597			hend = strstr(hbuf, "\r\n\r\n");
598			if (!hend) {
599				/* Malformed request */
600				return -1;
601			}
602
603			parse_http_headers(&hbuf, &part_header);
604			if ((hend + 2) != hbuf) {
605				/* Malformed request */
606				return -1;
607			}
608
609			/* Skip \r\n\r\n */
610			hend += 4;
611
612			/* According to the RFC, every part has to have a header field like:
613			 * Content-Disposition: form-data; name="..." */
614			content_disp = get_header(&part_header, "Content-Disposition");
615			if (!content_disp) {
616				/* Malformed request */
617				return -1;
618			}
619
620			/* Get the mandatory name="..." part of the Content-Disposition
621			 * header. */
622			nbeg = strstr(content_disp, "name=\"");
623			if (!nbeg) {
624				/* Malformed request */
625				return -1;
626			}
627			nbeg += 6;
628			nend = strchr(nbeg, '\"');
629			if (!nend) {
630				/* Malformed request */
631				return -1;
632			}
633
634			/* Get the optional filename="..." part of the Content-Disposition
635			 * header. */
636			fbeg = strstr(content_disp, "filename=\"");
637			if (fbeg) {
638				fbeg += 10;
639				fend = strchr(fbeg, '\"');
640				if (!fend) {
641					/* Malformed request (the filename field is optional, but if
642					 * it exists, it needs to be terminated correctly). */
643					return -1;
644				}
645
646				/* TODO: check Content-Type */
647				/* Content-Type: application/octet-stream */
648
649			} else {
650				fend = fbeg;
651			}
652
653			memset(path, 0, sizeof(path));
654			field_count++;
655			field_storage = url_encoded_field_found(conn,
656			                                        nbeg,
657			                                        (size_t)(nend - nbeg),
658			                                        fbeg,
659			                                        (size_t)(fend - fbeg),
660			                                        path,
661			                                        sizeof(path) - 1,
662			                                        fdh);
663
664			/* If the boundary is already in the buffer, get the address,
665			 * otherwise next will be NULL. */
666			next = search_boundary(hbuf,
667			                       (size_t)((buf - hbuf) + buf_fill),
668			                       boundary,
669			                       bl);
670
671			if (field_storage == FORM_FIELD_STORAGE_STORE) {
672				/* Store the content to a file */
673				if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
674					fstore.access.fp = NULL;
675				}
676				file_size = 0;
677
678				if (!fstore.access.fp) {
679					mg_cry(conn, "%s: Cannot create file %s", __func__, path);
680				}
681			}
682
683			get_block = 0;
684			while (!next) {
685				/* Set "towrite" to the number of bytes available
686				 * in the buffer */
687				towrite = (size_t)(buf - hend + buf_fill);
688				/* Subtract the boundary length, to deal with
689				 * cases the boundary is only partially stored
690				 * in the buffer. */
691				towrite -= bl + 4;
692
693				if (field_storage == FORM_FIELD_STORAGE_GET) {
694					unencoded_field_get(conn,
695					                    ((get_block > 0) ? NULL : nbeg),
696					                    ((get_block > 0)
697					                         ? 0
698					                         : (size_t)(nend - nbeg)),
699					                    hend,
700					                    towrite,
701					                    fdh);
702					get_block++;
703				}
704
705				if (field_storage == FORM_FIELD_STORAGE_STORE) {
706					if (fstore.access.fp) {
707
708						/* Store the content of the buffer. */
709						n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
710						if ((n != towrite) || (ferror(fstore.access.fp))) {
711							mg_cry(conn,
712							       "%s: Cannot write file %s",
713							       __func__,
714							       path);
715							mg_fclose(&fstore.access);
716							remove_bad_file(conn, path);
717						}
718						file_size += (int64_t)n;
719					}
720				}
721
722				memmove(buf, hend + towrite, bl + 4);
723				buf_fill = (int)(bl + 4);
724				hend = buf;
725
726				/* Read new data */
727				r = mg_read(conn,
728				            buf + (size_t)buf_fill,
729				            sizeof(buf) - 1 - (size_t)buf_fill);
730				if (r < 0) {
731					/* read error */
732					return -1;
733				}
734				buf_fill += r;
735				buf[buf_fill] = 0;
736				if (buf_fill < 1) {
737					/* No data */
738					return -1;
739				}
740
741				/* Find boundary */
742				next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
743			}
744
745			towrite = (size_t)(next - hend);
746
747			if (field_storage == FORM_FIELD_STORAGE_GET) {
748				/* Call callback */
749				unencoded_field_get(conn,
750				                    ((get_block > 0) ? NULL : nbeg),
751				                    ((get_block > 0) ? 0
752				                                     : (size_t)(nend - nbeg)),
753				                    hend,
754				                    towrite,
755				                    fdh);
756			}
757
758			if (field_storage == FORM_FIELD_STORAGE_STORE) {
759
760				if (fstore.access.fp) {
761					n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
762					if ((n != towrite) || (ferror(fstore.access.fp))) {
763						mg_cry(conn,
764						       "%s: Cannot write file %s",
765						       __func__,
766						       path);
767						mg_fclose(&fstore.access);
768						remove_bad_file(conn, path);
769					}
770					file_size += (int64_t)n;
771				}
772			}
773
774			if (field_storage == FORM_FIELD_STORAGE_STORE) {
775
776				if (fstore.access.fp) {
777					r = mg_fclose(&fstore.access);
778					if (r == 0) {
779						/* stored successfully */
780						field_stored(conn, path, file_size, fdh);
781					} else {
782						mg_cry(conn,
783						       "%s: Error saving file %s",
784						       __func__,
785						       path);
786						remove_bad_file(conn, path);
787					}
788					fstore.access.fp = NULL;
789				}
790			}
791
792			if ((field_storage & FORM_FIELD_STORAGE_ABORT)
793			    == FORM_FIELD_STORAGE_ABORT) {
794				/* Stop parsing the request */
795				break;
796			}
797
798			/* Remove from the buffer */
799			used = next - buf + 2;
800			memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
801			buf_fill -= (int)used;
802		}
803
804		/* All parts handled */
805		return field_count;
806	}
807
808	/* Unknown Content-Type */
809	return -1;
810}
811