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