Logo ROOT  
Reference Guide
handle_form.inl
Go to the documentation of this file.
1/* Copyright (c) 2016-2018 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)) {
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_internal(conn, "%s: Cannot decode filename", __func__);
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) == MG_FORM_FIELD_STORAGE_GET) {
67 if (fdh->field_get == NULL) {
68 mg_cry_internal(conn,
69 "%s: Function \"Get\" not available",
70 __func__);
72 }
73 }
74 if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_STORE) {
75 if (fdh->field_store == NULL) {
76 mg_cry_internal(conn,
77 "%s: Function \"Store\" not available",
78 __func__);
80 }
81 }
82
83 return ret;
84}
85
86
87static int
88url_encoded_field_get(const struct mg_connection *conn,
89 const char *key,
90 size_t key_len,
91 const char *value,
92 size_t value_len,
93 struct mg_form_data_handler *fdh)
94{
95 char key_dec[1024];
96
97 char *value_dec = (char *)mg_malloc_ctx(value_len + 1, conn->phys_ctx);
98 int value_dec_len, ret;
99
100 if (!value_dec) {
101 /* Log error message and stop parsing the form data. */
102 mg_cry_internal(conn,
103 "%s: Not enough memory (required: %lu)",
104 __func__,
105 (unsigned long)(value_len + 1));
107 }
108
109 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
110
111 value_dec_len =
112 mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
113
114 ret = fdh->field_get(key_dec,
115 value_dec,
116 (size_t)value_dec_len,
117 fdh->user_data);
118
119 mg_free(value_dec);
120
121 return ret;
122}
123
124
125static int
126unencoded_field_get(const struct mg_connection *conn,
127 const char *key,
128 size_t key_len,
129 const char *value,
130 size_t value_len,
131 struct mg_form_data_handler *fdh)
132{
133 char key_dec[1024];
134 (void)conn;
135
136 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
137
138 return fdh->field_get(key_dec, value, value_len, fdh->user_data);
139}
140
141
142static int
143field_stored(const struct mg_connection *conn,
144 const char *path,
145 long long file_size,
146 struct mg_form_data_handler *fdh)
147{
148 /* Equivalent to "upload" callback of "mg_upload". */
149
150 (void)conn; /* we do not need mg_cry here, so conn is currently unused */
151
152 return fdh->field_store(path, file_size, fdh->user_data);
153}
154
155
156static const char *
157search_boundary(const char *buf,
158 size_t buf_len,
159 const char *boundary,
160 size_t boundary_len)
161{
162 /* We must do a binary search here, not a string search, since the buffer
163 * may contain '\x00' bytes, if binary data is transferred. */
164 int clen = (int)buf_len - (int)boundary_len - 4;
165 int i;
166
167 for (i = 0; i <= clen; i++) {
168 if (!memcmp(buf + i, "\r\n--", 4)) {
169 if (!memcmp(buf + i + 4, boundary, boundary_len)) {
170 return buf + i;
171 }
172 }
173 }
174 return NULL;
175}
176
177
178int
179mg_handle_form_request(struct mg_connection *conn,
180 struct mg_form_data_handler *fdh)
181{
182 const char *content_type;
183 char path[512];
184 char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */
185 int field_storage;
186 int buf_fill = 0;
187 int r;
188 int field_count = 0;
189 struct mg_file fstore = STRUCT_FILE_INITIALIZER;
190 int64_t file_size = 0; /* init here, to a avoid a false positive
191 "uninitialized variable used" warning */
192
193 int has_body_data =
194 (conn->request_info.content_length > 0) || (conn->is_chunked);
195
196 /* There are three ways to encode data from a HTML form:
197 * 1) method: GET (default)
198 * The form data is in the HTTP query string.
199 * 2) method: POST, enctype: "application/x-www-form-urlencoded"
200 * The form data is in the request body.
201 * The body is url encoded (the default encoding for POST).
202 * 3) method: POST, enctype: "multipart/form-data".
203 * The form data is in the request body of a multipart message.
204 * This is the typical way to handle file upload from a form.
205 */
206
207 if (!has_body_data) {
208 const char *data;
209
210 if (0 != strcmp(conn->request_info.request_method, "GET")) {
211 /* No body data, but not a GET request.
212 * This is not a valid form request. */
213 return -1;
214 }
215
216 /* GET request: form data is in the query string. */
217 /* The entire data has already been loaded, so there is no nead to
218 * call mg_read. We just need to split the query string into key-value
219 * pairs. */
220 data = conn->request_info.query_string;
221 if (!data) {
222 /* No query string. */
223 return -1;
224 }
225
226 /* Split data in a=1&b=xy&c=3&c=4 ... */
227 while (*data) {
228 const char *val = strchr(data, '=');
229 const char *next;
230 ptrdiff_t keylen, vallen;
231
232 if (!val) {
233 break;
234 }
235 keylen = val - data;
236
237 /* In every "field_found" callback we ask what to do with the
238 * data ("field_storage"). This could be:
239 * MG_FORM_FIELD_STORAGE_SKIP (0):
240 * ignore the value of this field
241 * MG_FORM_FIELD_STORAGE_GET (1):
242 * read the data and call the get callback function
243 * MG_FORM_FIELD_STORAGE_STORE (2):
244 * store the data in a file
245 * MG_FORM_FIELD_STORAGE_READ (3):
246 * let the user read the data (for parsing long data on the fly)
247 * MG_FORM_FIELD_STORAGE_ABORT (flag):
248 * stop parsing
249 */
250 memset(path, 0, sizeof(path));
251 field_count++;
252 field_storage = url_encoded_field_found(conn,
253 data,
254 (size_t)keylen,
255 NULL,
256 0,
257 path,
258 sizeof(path) - 1,
259 fdh);
260
261 val++;
262 next = strchr(val, '&');
263 if (next) {
264 vallen = next - val;
265 next++;
266 } else {
267 vallen = (ptrdiff_t)strlen(val);
268 next = val + vallen;
269 }
270
271 if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
272 /* Call callback */
274 conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
276 /* Stop request handling */
277 break;
278 }
280 /* Skip to next field */
281 field_storage = MG_FORM_FIELD_STORAGE_SKIP;
282 }
283 }
284 if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
285 /* Store the content to a file */
286 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
287 fstore.access.fp = NULL;
288 }
289 file_size = 0;
290 if (fstore.access.fp != NULL) {
291 size_t n = (size_t)
292 fwrite(val, 1, (size_t)vallen, fstore.access.fp);
293 if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
294 mg_cry_internal(conn,
295 "%s: Cannot write file %s",
296 __func__,
297 path);
298 (void)mg_fclose(&fstore.access);
299 remove_bad_file(conn, path);
300 }
301 file_size += (int64_t)n;
302
303 if (fstore.access.fp) {
304 r = mg_fclose(&fstore.access);
305 if (r == 0) {
306 /* stored successfully */
307 r = field_stored(conn, path, file_size, fdh);
309 /* Stop request handling */
310 break;
311 }
312
313 } else {
314 mg_cry_internal(conn,
315 "%s: Error saving file %s",
316 __func__,
317 path);
318 remove_bad_file(conn, path);
319 }
320 fstore.access.fp = NULL;
321 }
322
323 } else {
324 mg_cry_internal(conn,
325 "%s: Cannot create file %s",
326 __func__,
327 path);
328 }
329 }
330
331 /* if (field_storage == MG_FORM_FIELD_STORAGE_READ) { */
332 /* The idea of "field_storage=read" is to let the API user read
333 * data chunk by chunk and to some data processing on the fly.
334 * This should avoid the need to store data in the server:
335 * It should neither be stored in memory, like
336 * "field_storage=get" does, nor in a file like
337 * "field_storage=store".
338 * However, for a "GET" request this does not make any much
339 * sense, since the data is already stored in memory, as it is
340 * part of the query string.
341 */
342 /* } */
343
344 if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
346 /* Stop parsing the request */
347 break;
348 }
349
350 /* Proceed to next entry */
351 data = next;
352 }
353
354 return field_count;
355 }
356
357 content_type = mg_get_header(conn, "Content-Type");
358
359 if (!content_type
360 || !mg_strncasecmp(content_type,
361 "APPLICATION/X-WWW-FORM-URLENCODED",
362 33)
363 || !mg_strncasecmp(content_type,
364 "APPLICATION/WWW-FORM-URLENCODED",
365 31)) {
366 /* The form data is in the request body data, encoded in key/value
367 * pairs. */
368 int all_data_read = 0;
369
370 /* Read body data and split it in keys and values.
371 * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
372 * Here we use "POST", and read the data from the request body.
373 * The data read on the fly, so it is not required to buffer the
374 * entire request in memory before processing it. */
375 for (;;) {
376 const char *val;
377 const char *next;
378 ptrdiff_t keylen, vallen;
379 ptrdiff_t used;
380 int end_of_key_value_pair_found = 0;
381 int get_block;
382
383 if ((size_t)buf_fill < (sizeof(buf) - 1)) {
384
385 size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
386 r = mg_read(conn, buf + (size_t)buf_fill, to_read);
387 if (r < 0) {
388 /* read error */
389 return -1;
390 }
391 if (r != (int)to_read) {
392 /* TODO: Create a function to get "all_data_read" from
393 * the conn object. All data is read if the Content-Length
394 * has been reached, or if chunked encoding is used and
395 * the end marker has been read, or if the connection has
396 * been closed. */
397 all_data_read = 1;
398 }
399 buf_fill += r;
400 buf[buf_fill] = 0;
401 if (buf_fill < 1) {
402 break;
403 }
404 }
405
406 val = strchr(buf, '=');
407
408 if (!val) {
409 break;
410 }
411 keylen = val - buf;
412 val++;
413
414 /* Call callback */
415 memset(path, 0, sizeof(path));
416 field_count++;
417 field_storage = url_encoded_field_found(conn,
418 buf,
419 (size_t)keylen,
420 NULL,
421 0,
422 path,
423 sizeof(path) - 1,
424 fdh);
425
426 if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
428 /* Stop parsing the request */
429 break;
430 }
431
432 if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
433 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
434 fstore.access.fp = NULL;
435 }
436 file_size = 0;
437 if (!fstore.access.fp) {
438 mg_cry_internal(conn,
439 "%s: Cannot create file %s",
440 __func__,
441 path);
442 }
443 }
444
445 get_block = 0;
446 /* Loop to read values larger than sizeof(buf)-keylen-2 */
447 do {
448 next = strchr(val, '&');
449 if (next) {
450 vallen = next - val;
451 next++;
452 end_of_key_value_pair_found = 1;
453 } else {
454 vallen = (ptrdiff_t)strlen(val);
455 next = val + vallen;
456 end_of_key_value_pair_found = all_data_read;
457 }
458
459 if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
460#if 0
461 if (!end_of_key_value_pair_found && !all_data_read) {
462 /* This callback will deliver partial contents */
463 }
464#endif
465
466 /* Call callback */
468 ((get_block > 0) ? NULL : buf),
469 ((get_block > 0)
470 ? 0
471 : (size_t)keylen),
472 val,
473 (size_t)vallen,
474 fdh);
475 get_block++;
477 /* Stop request handling */
478 break;
479 }
481 /* Skip to next field */
482 field_storage = MG_FORM_FIELD_STORAGE_SKIP;
483 }
484 }
485 if (fstore.access.fp) {
486 size_t n = (size_t)
487 fwrite(val, 1, (size_t)vallen, fstore.access.fp);
488 if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
489 mg_cry_internal(conn,
490 "%s: Cannot write file %s",
491 __func__,
492 path);
493 mg_fclose(&fstore.access);
494 remove_bad_file(conn, path);
495 }
496 file_size += (int64_t)n;
497 }
498
499 if (!end_of_key_value_pair_found) {
500 used = next - buf;
501 memmove(buf,
502 buf + (size_t)used,
503 sizeof(buf) - (size_t)used);
504 next = buf;
505 buf_fill -= (int)used;
506 if ((size_t)buf_fill < (sizeof(buf) - 1)) {
507
508 size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
509 r = mg_read(conn, buf + (size_t)buf_fill, to_read);
510 if (r < 0) {
511 /* read error */
512 if (fstore.access.fp) {
513 mg_fclose(&fstore.access);
514 remove_bad_file(conn, path);
515 }
516 return -1;
517 }
518 if (r != (int)to_read) {
519 /* TODO: Create a function to get "all_data_read"
520 * from the conn object. All data is read if the
521 * Content-Length has been reached, or if chunked
522 * encoding is used and the end marker has been
523 * read, or if the connection has been closed. */
524 all_data_read = 1;
525 }
526 buf_fill += r;
527 buf[buf_fill] = 0;
528 if (buf_fill < 1) {
529 break;
530 }
531 val = buf;
532 }
533 }
534
535 } while (!end_of_key_value_pair_found);
536
537 if (fstore.access.fp) {
538 r = mg_fclose(&fstore.access);
539 if (r == 0) {
540 /* stored successfully */
541 r = field_stored(conn, path, file_size, fdh);
543 /* Stop request handling */
544 break;
545 }
546 } else {
547 mg_cry_internal(conn,
548 "%s: Error saving file %s",
549 __func__,
550 path);
551 remove_bad_file(conn, path);
552 }
553 fstore.access.fp = NULL;
554 }
555
556 if (all_data_read && (buf_fill == 0)) {
557 /* nothing more to process */
558 break;
559 }
560
561 /* Proceed to next entry */
562 used = next - buf;
563 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
564 buf_fill -= (int)used;
565 }
566
567 return field_count;
568 }
569
570 if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
571 /* The form data is in the request body data, encoded as multipart
572 * content (see https://www.ietf.org/rfc/rfc1867.txt,
573 * https://www.ietf.org/rfc/rfc2388.txt). */
574 char *boundary;
575 size_t bl;
576 ptrdiff_t used;
577 struct mg_request_info part_header;
578 char *hbuf;
579 const char *content_disp, *hend, *fbeg, *fend, *nbeg, *nend;
580 const char *next;
581 unsigned part_no;
582
583 memset(&part_header, 0, sizeof(part_header));
584
585 /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
586 bl = 20;
587 while (content_type[bl] == ' ') {
588 bl++;
589 }
590
591 /* There has to be a BOUNDARY definition in the Content-Type header */
592 if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
593 /* Malformed request */
594 return -1;
595 }
596
597 /* Copy boundary string to variable "boundary" */
598 fbeg = content_type + bl + 9;
599 bl = strlen(fbeg);
600 boundary = (char *)mg_malloc(bl + 1);
601 if (!boundary) {
602 /* Out of memory */
603 mg_cry_internal(conn,
604 "%s: Cannot allocate memory for boundary [%lu]",
605 __func__,
606 (unsigned long)bl);
607 return -1;
608 }
609 memcpy(boundary, fbeg, bl);
610 boundary[bl] = 0;
611
612 /* RFC 2046 permits the boundary string to be quoted. */
613 /* If the boundary is quoted, trim the quotes */
614 if (boundary[0] == '"') {
615 hbuf = strchr(boundary + 1, '"');
616 if ((!hbuf) || (*hbuf != '"')) {
617 /* Malformed request */
618 mg_free(boundary);
619 return -1;
620 }
621 *hbuf = 0;
622 memmove(boundary, boundary + 1, bl);
623 bl = strlen(boundary);
624 }
625
626 /* Do some sanity checks for boundary lengths */
627 if (bl > 70) {
628 /* From RFC 2046:
629 * Boundary delimiters must not appear within the
630 * encapsulated material, and must be no longer
631 * than 70 characters, not counting the two
632 * leading hyphens.
633 */
634
635 /* The algorithm can not work if bl >= sizeof(buf), or if buf
636 * can not hold the multipart header plus the boundary.
637 * Requests with long boundaries are not RFC compliant, maybe they
638 * are intended attacks to interfere with this algorithm. */
639 mg_free(boundary);
640 return -1;
641 }
642 if (bl < 4) {
643 /* Sanity check: A boundary string of less than 4 bytes makes
644 * no sense either. */
645 mg_free(boundary);
646 return -1;
647 }
648
649 for (part_no = 0;; part_no++) {
650 size_t towrite, fnlen, n;
651 int get_block;
652
653 r = mg_read(conn,
654 buf + (size_t)buf_fill,
655 sizeof(buf) - 1 - (size_t)buf_fill);
656 if (r < 0) {
657 /* read error */
658 mg_free(boundary);
659 return -1;
660 }
661 buf_fill += r;
662 buf[buf_fill] = 0;
663 if (buf_fill < 1) {
664 /* No data */
665 mg_free(boundary);
666 return -1;
667 }
668
669 if (part_no == 0) {
670 int d = 0;
671 while ((buf[d] != '-') && (d < buf_fill)) {
672 d++;
673 }
674 if ((d > 0) && (buf[d] == '-')) {
675 memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d);
676 buf_fill -= d;
677 buf[buf_fill] = 0;
678 }
679 }
680
681 if (buf[0] != '-' || buf[1] != '-') {
682 /* Malformed request */
683 mg_free(boundary);
684 return -1;
685 }
686 if (0 != strncmp(buf + 2, boundary, bl)) {
687 /* Malformed request */
688 mg_free(boundary);
689 return -1;
690 }
691 if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
692 /* Every part must end with \r\n, if there is another part.
693 * The end of the request has an extra -- */
694 if (((size_t)buf_fill != (size_t)(bl + 6))
695 || (strncmp(buf + bl + 2, "--\r\n", 4))) {
696 /* Malformed request */
697 mg_free(boundary);
698 return -1;
699 }
700 /* End of the request */
701 break;
702 }
703
704 /* Next, we need to get the part header: Read until \r\n\r\n */
705 hbuf = buf + bl + 4;
706 hend = strstr(hbuf, "\r\n\r\n");
707 if (!hend) {
708 /* Malformed request */
709 mg_free(boundary);
710 return -1;
711 }
712
713 part_header.num_headers =
714 parse_http_headers(&hbuf, part_header.http_headers);
715 if ((hend + 2) != hbuf) {
716 /* Malformed request */
717 mg_free(boundary);
718 return -1;
719 }
720
721 /* Skip \r\n\r\n */
722 hend += 4;
723
724 /* According to the RFC, every part has to have a header field like:
725 * Content-Disposition: form-data; name="..." */
726 content_disp = get_header(part_header.http_headers,
727 part_header.num_headers,
728 "Content-Disposition");
729 if (!content_disp) {
730 /* Malformed request */
731 mg_free(boundary);
732 return -1;
733 }
734
735 /* Get the mandatory name="..." part of the Content-Disposition
736 * header. */
737 nbeg = strstr(content_disp, "name=\"");
738 while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
739 /* It could be somethingname= instead of name= */
740 nbeg = strstr(nbeg + 1, "name=\"");
741 }
742
743 /* This line is not required, but otherwise some compilers
744 * generate spurious warnings. */
745 nend = nbeg;
746 /* And others complain, the result is unused. */
747 (void)nend;
748
749 /* If name=" is found, search for the closing " */
750 if (nbeg) {
751 nbeg += 6;
752 nend = strchr(nbeg, '\"');
753 if (!nend) {
754 /* Malformed request */
755 mg_free(boundary);
756 return -1;
757 }
758 } else {
759 /* name= without quotes is also allowed */
760 nbeg = strstr(content_disp, "name=");
761 while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
762 /* It could be somethingname= instead of name= */
763 nbeg = strstr(nbeg + 1, "name=");
764 }
765 if (!nbeg) {
766 /* Malformed request */
767 mg_free(boundary);
768 return -1;
769 }
770 nbeg += 5;
771
772 /* RFC 2616 Sec. 2.2 defines a list of allowed
773 * separators, but many of them make no sense
774 * here, e.g. various brackets or slashes.
775 * If they are used, probably someone is
776 * trying to attack with curious hand made
777 * requests. Only ; , space and tab seem to be
778 * reasonable here. Ignore everything else. */
779 nend = nbeg + strcspn(nbeg, ",; \t");
780 }
781
782 /* Get the optional filename="..." part of the Content-Disposition
783 * header. */
784 fbeg = strstr(content_disp, "filename=\"");
785 while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
786 /* It could be somethingfilename= instead of filename= */
787 fbeg = strstr(fbeg + 1, "filename=\"");
788 }
789
790 /* This line is not required, but otherwise some compilers
791 * generate spurious warnings. */
792 fend = fbeg;
793
794 /* If filename=" is found, search for the closing " */
795 if (fbeg) {
796 fbeg += 10;
797 fend = strchr(fbeg, '\"');
798
799 if (!fend) {
800 /* Malformed request (the filename field is optional, but if
801 * it exists, it needs to be terminated correctly). */
802 mg_free(boundary);
803 return -1;
804 }
805
806 /* TODO: check Content-Type */
807 /* Content-Type: application/octet-stream */
808 }
809 if (!fbeg) {
810 /* Try the same without quotes */
811 fbeg = strstr(content_disp, "filename=");
812 while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
813 /* It could be somethingfilename= instead of filename= */
814 fbeg = strstr(fbeg + 1, "filename=");
815 }
816 if (fbeg) {
817 fbeg += 9;
818 fend = fbeg + strcspn(fbeg, ",; \t");
819 }
820 }
821
822 if (!fbeg || !fend) {
823 fbeg = NULL;
824 fend = NULL;
825 fnlen = 0;
826 } else {
827 fnlen = (size_t)(fend - fbeg);
828 }
829
830 /* In theory, it could be possible that someone crafts
831 * a request like name=filename=xyz. Check if name and
832 * filename do not overlap. */
833 if (!(((ptrdiff_t)fbeg > (ptrdiff_t)nend)
834 || ((ptrdiff_t)nbeg > (ptrdiff_t)fend))) {
835 mg_free(boundary);
836 return -1;
837 }
838
839 /* Call callback for new field */
840 memset(path, 0, sizeof(path));
841 field_count++;
842 field_storage = url_encoded_field_found(conn,
843 nbeg,
844 (size_t)(nend - nbeg),
845 ((fnlen > 0) ? fbeg : NULL),
846 fnlen,
847 path,
848 sizeof(path) - 1,
849 fdh);
850
851 /* If the boundary is already in the buffer, get the address,
852 * otherwise next will be NULL. */
853 next = search_boundary(hbuf,
854 (size_t)((buf - hbuf) + buf_fill),
855 boundary,
856 bl);
857
858 if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
859 /* Store the content to a file */
860 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
861 fstore.access.fp = NULL;
862 }
863 file_size = 0;
864
865 if (!fstore.access.fp) {
866 mg_cry_internal(conn,
867 "%s: Cannot create file %s",
868 __func__,
869 path);
870 }
871 }
872
873 get_block = 0;
874 while (!next) {
875 /* Set "towrite" to the number of bytes available
876 * in the buffer */
877 towrite = (size_t)(buf - hend + buf_fill);
878 /* Subtract the boundary length, to deal with
879 * cases the boundary is only partially stored
880 * in the buffer. */
881 towrite -= bl + 4;
882
883 if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
884 r = unencoded_field_get(conn,
885 ((get_block > 0) ? NULL : nbeg),
886 ((get_block > 0)
887 ? 0
888 : (size_t)(nend - nbeg)),
889 hend,
890 towrite,
891 fdh);
892 get_block++;
894 /* Stop request handling */
895 break;
896 }
898 /* Skip to next field */
899 field_storage = MG_FORM_FIELD_STORAGE_SKIP;
900 }
901 }
902
903 if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
904 if (fstore.access.fp) {
905
906 /* Store the content of the buffer. */
907 n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
908 if ((n != towrite) || (ferror(fstore.access.fp))) {
909 mg_cry_internal(conn,
910 "%s: Cannot write file %s",
911 __func__,
912 path);
913 mg_fclose(&fstore.access);
914 remove_bad_file(conn, path);
915 }
916 file_size += (int64_t)n;
917 }
918 }
919
920 memmove(buf, hend + towrite, bl + 4);
921 buf_fill = (int)(bl + 4);
922 hend = buf;
923
924 /* Read new data */
925 r = mg_read(conn,
926 buf + (size_t)buf_fill,
927 sizeof(buf) - 1 - (size_t)buf_fill);
928 if (r < 0) {
929 /* read error */
930 if (fstore.access.fp) {
931 mg_fclose(&fstore.access);
932 remove_bad_file(conn, path);
933 }
934 mg_free(boundary);
935 return -1;
936 }
937 buf_fill += r;
938 buf[buf_fill] = 0;
939 /* buf_fill is at least 8 here */
940
941 /* Find boundary */
942 next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
943 }
944
945 towrite = (size_t)(next - hend);
946
947 if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
948 /* Call callback */
949 r = unencoded_field_get(conn,
950 ((get_block > 0) ? NULL : nbeg),
951 ((get_block > 0)
952 ? 0
953 : (size_t)(nend - nbeg)),
954 hend,
955 towrite,
956 fdh);
958 /* Stop request handling */
959 break;
960 }
962 /* Skip to next field */
963 field_storage = MG_FORM_FIELD_STORAGE_SKIP;
964 }
965 }
966
967 if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
968
969 if (fstore.access.fp) {
970 n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
971 if ((n != towrite) || (ferror(fstore.access.fp))) {
972 mg_cry_internal(conn,
973 "%s: Cannot write file %s",
974 __func__,
975 path);
976 mg_fclose(&fstore.access);
977 remove_bad_file(conn, path);
978 } else {
979 file_size += (int64_t)n;
980 r = mg_fclose(&fstore.access);
981 if (r == 0) {
982 /* stored successfully */
983 r = field_stored(conn, path, file_size, fdh);
985 /* Stop request handling */
986 break;
987 }
988 } else {
989 mg_cry_internal(conn,
990 "%s: Error saving file %s",
991 __func__,
992 path);
993 remove_bad_file(conn, path);
994 }
995 }
996 fstore.access.fp = NULL;
997 }
998 }
999
1000 if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
1002 /* Stop parsing the request */
1003 break;
1004 }
1005
1006 /* Remove from the buffer */
1007 used = next - buf + 2;
1008 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
1009 buf_fill -= (int)used;
1010 }
1011
1012 /* All parts handled */
1013 mg_free(boundary);
1014 return field_count;
1015 }
1016
1017 /* Unknown Content-Type */
1018 return -1;
1019}
1020
1021
1022/* End of handle_form.inl */
ROOT::R::TRInterface & r
Definition: Object.C:4
#define d(i)
Definition: RSha256.hxx:102
typedef void((*Func_t)())
int mg_strncasecmp(const char *s1, const char *s2, size_t len)
Definition: civetweb.c:3267
#define mg_malloc_ctx(a, c)
Definition: civetweb.c:1494
static void remove_bad_file(const struct mg_connection *conn, const char *path)
Definition: civetweb.c:9940
static __inline void * mg_malloc(size_t a)
Definition: civetweb.c:1471
static int mg_fopen(const struct mg_connection *conn, const char *path, int mode, struct mg_file *filep)
Definition: civetweb.c:3146
static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
Definition: civetweb.c:10055
static const char * get_header(const struct mg_header *hdr, int num_hdr, const char *name)
Definition: civetweb.c:3979
const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition: civetweb.c:4016
#define mg_cry_internal(conn, fmt,...)
Definition: civetweb.c:2774
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
Definition: civetweb.c:6993
#define MG_FOPEN_MODE_WRITE
Definition: civetweb.c:3042
#define MG_BUF_LEN
Definition: civetweb.c:422
int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition: civetweb.c:6655
static int mg_fclose(struct mg_file_access *fileacc)
Definition: civetweb.c:3231
static __inline void mg_free(void *a)
Definition: civetweb.c:1489
#define STRUCT_FILE_INITIALIZER
Definition: civetweb.c:2279
@ MG_FORM_FIELD_STORAGE_GET
Definition: civetweb.h:1241
@ MG_FORM_FIELD_STORAGE_ABORT
Definition: civetweb.h:1245
@ MG_FORM_FIELD_STORAGE_STORE
Definition: civetweb.h:1243
@ MG_FORM_FIELD_STORAGE_SKIP
Definition: civetweb.h:1239
@ MG_FORM_FIELD_HANDLE_NEXT
Definition: civetweb.h:1254
@ MG_FORM_FIELD_HANDLE_ABORT
Definition: civetweb.h:1256
static int field_stored(const struct mg_connection *conn, const char *path, long long file_size, struct mg_form_data_handler *fdh)
static int url_encoded_field_get(const struct mg_connection *conn, const char *key, size_t key_len, const char *value, size_t value_len, struct mg_form_data_handler *fdh)
Definition: handle_form.inl:88
int mg_handle_form_request(struct mg_connection *conn, struct mg_form_data_handler *fdh)
static const char * search_boundary(const char *buf, size_t buf_len, const char *boundary, size_t boundary_len)
static int url_encoded_field_found(const struct mg_connection *conn, const char *key, size_t key_len, const char *filename, size_t filename_len, char *path, size_t path_len, struct mg_form_data_handler *fdh)
Definition: handle_form.inl:24
static int unencoded_field_get(const struct mg_connection *conn, const char *key, size_t key_len, const char *value, size_t value_len, struct mg_form_data_handler *fdh)
const Int_t n
Definition: legend1.C:16
int(* field_get)(const char *key, const char *value, size_t valuelen, void *user_data)
Definition: civetweb.h:1191
int(* field_found)(const char *key, const char *filename, char *path, size_t pathlen, void *user_data)
Definition: civetweb.h:1173
int(* field_store)(const char *path, long long file_size, void *user_data)
Definition: civetweb.h:1213
struct mg_header http_headers[MG_MAX_HEADERS]
Definition: civetweb.h:170