Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
handle_form.inl
Go to the documentation of this file.
1/* Copyright (c) 2016-2021 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
22static int
24 const char *key,
25 size_t key_len,
26 const char *filename,
27 size_t filename_len,
28 char *path,
29 size_t path_len,
31{
32 char key_dec[1024];
33 char filename_dec[1024];
34 int key_dec_len;
36 int ret;
37
39 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
40
41 if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
43 }
44
45 if (filename) {
47 (int)filename_len,
49 (int)sizeof(filename_dec),
50 1);
51
52 if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
53 || (filename_dec_len < 0)) {
54 /* Log error message and skip this field. */
55 mg_cry_internal(conn, "%s: Cannot decode filename", __func__);
57 }
59
60 } else {
61 filename_dec[0] = 0;
62 }
63
64 ret =
65 fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
66
67 if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_GET) {
68 if (fdh->field_get == NULL) {
69 mg_cry_internal(conn,
70 "%s: Function \"Get\" not available",
71 __func__);
73 }
74 }
75 if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_STORE) {
76 if (fdh->field_store == NULL) {
77 mg_cry_internal(conn,
78 "%s: Function \"Store\" not available",
79 __func__);
81 }
82 }
83
84 return ret;
85}
86
87static int
89 const struct mg_connection *conn,
90 const char *key,
91 size_t key_len,
92 const char *value,
93 size_t *value_len, /* IN: number of bytes available in "value", OUT: number
94 of bytes processed */
96{
97 char key_dec[1024];
98
99 char *value_dec = (char *)mg_malloc_ctx(*value_len + 1, conn->phys_ctx);
100 int value_dec_len, ret;
101
102 if (!value_dec) {
103 /* Log error message and stop parsing the form data. */
104 mg_cry_internal(conn,
105 "%s: Not enough memory (required: %lu)",
106 __func__,
107 (unsigned long)(*value_len + 1));
109 }
110
111 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
112
113 if (*value_len >= 2 && value[*value_len - 2] == '%')
114 *value_len -= 2;
115 else if (*value_len >= 1 && value[*value_len - 1] == '%')
116 (*value_len)--;
118 value, (int)*value_len, value_dec, ((int)*value_len) + 1, 1);
119
120 ret = fdh->field_get(key_dec,
121 value_dec,
122 (size_t)value_dec_len,
123 fdh->user_data);
124
126
127 return ret;
128}
129
130static int
132 const char *key,
133 size_t key_len,
134 const char *value,
135 size_t value_len,
137{
138 char key_dec[1024];
139 (void)conn;
140
141 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
142
143 return fdh->field_get(key_dec, value, value_len, fdh->user_data);
144}
145
146static int
147field_stored(const struct mg_connection *conn,
148 const char *path,
149 long long file_size,
151{
152 /* Equivalent to "upload" callback of "mg_upload". */
153
154 (void)conn; /* we do not need mg_cry here, so conn is currently unused */
155
156 return fdh->field_store(path, file_size, fdh->user_data);
157}
158
159static const char *
160search_boundary(const char *buf,
161 size_t buf_len,
162 const char *boundary,
163 size_t boundary_len)
164{
165 char *boundary_start = "\r\n--";
167
168 /* We must do a binary search here, not a string search, since the
169 * buffer may contain '\x00' bytes, if binary data is transferred. */
171 int i;
172
173 for (i = 0; i <= clen; i++) {
174 if (!memcmp(buf + i, boundary_start, boundary_start_len)) {
176 return buf + i;
177 }
178 }
179 }
180 return NULL;
181}
182
183int
186{
187 const char *content_type;
188 char path[512];
189 char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */
190 int field_storage;
191 size_t buf_fill = 0;
192 int r;
193 int field_count = 0;
195 int64_t file_size = 0; /* init here, to a avoid a false positive
196 "uninitialized variable used" warning */
197
198 int has_body_data =
199 (conn->request_info.content_length > 0) || (conn->is_chunked);
200
201 /* Unused without filesystems */
202 (void)fstore;
203 (void)file_size;
204
205 /* There are three ways to encode data from a HTML form:
206 * 1) method: GET (default)
207 * The form data is in the HTTP query string.
208 * 2) method: POST, enctype: "application/x-www-form-urlencoded"
209 * The form data is in the request body.
210 * The body is url encoded (the default encoding for POST).
211 * 3) method: POST, enctype: "multipart/form-data".
212 * The form data is in the request body of a multipart message.
213 * This is the typical way to handle file upload from a form.
214 */
215
216 if (!has_body_data) {
217 const char *data;
218
219 if (0 != strcmp(conn->request_info.request_method, "GET")) {
220 /* No body data, but not a GET request.
221 * This is not a valid form request. */
222 return -1;
223 }
224
225 /* GET request: form data is in the query string. */
226 /* The entire data has already been loaded, so there is no need to
227 * call mg_read. We just need to split the query string into key-value
228 * pairs. */
229 data = conn->request_info.query_string;
230 if (!data) {
231 /* No query string. */
232 return -1;
233 }
234
235 /* Split data in a=1&b=xy&c=3&c=4 ... */
236 while (*data) {
237 const char *val = strchr(data, '=');
238 const char *next;
239 ptrdiff_t keylen, vallen;
240
241 if (!val) {
242 break;
243 }
244 keylen = val - data;
245
246 /* In every "field_found" callback we ask what to do with the
247 * data ("field_storage"). This could be:
248 * MG_FORM_FIELD_STORAGE_SKIP (0):
249 * ignore the value of this field
250 * MG_FORM_FIELD_STORAGE_GET (1):
251 * read the data and call the get callback function
252 * MG_FORM_FIELD_STORAGE_STORE (2):
253 * store the data in a file
254 * MG_FORM_FIELD_STORAGE_READ (3):
255 * let the user read the data (for parsing long data on the fly)
256 * MG_FORM_FIELD_STORAGE_ABORT (flag):
257 * stop parsing
258 */
259 memset(path, 0, sizeof(path));
260 field_count++;
262 data,
263 (size_t)keylen,
264 NULL,
265 0,
266 path,
267 sizeof(path) - 1,
268 fdh);
269
270 val++;
271 next = strchr(val, '&');
272 if (next) {
273 vallen = next - val;
274 } else {
275 vallen = (ptrdiff_t)strlen(val);
276 }
277
279 /* Call callback */
281 conn, data, (size_t)keylen, val, (size_t *)&vallen, fdh);
283 /* Stop request handling */
284 break;
285 }
287 /* Skip to next field */
289 }
290 }
291
292 if (next) {
293 next++;
294 } else {
295 /* vallen may have been modified by url_encoded_field_get */
296 next = val + vallen;
297 }
298
299#if !defined(NO_FILESYSTEMS)
301 /* Store the content to a file */
302 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
303 fstore.access.fp = NULL;
304 }
305 file_size = 0;
306 if (fstore.access.fp != NULL) {
307 size_t n = (size_t)
308 fwrite(val, 1, (size_t)vallen, fstore.access.fp);
309 if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
310 mg_cry_internal(conn,
311 "%s: Cannot write file %s",
312 __func__,
313 path);
314 (void)mg_fclose(&fstore.access);
315 remove_bad_file(conn, path);
316 }
317 file_size += (int64_t)n;
318
319 if (fstore.access.fp) {
320 r = mg_fclose(&fstore.access);
321 if (r == 0) {
322 /* stored successfully */
323 r = field_stored(conn, path, file_size, fdh);
325 /* Stop request handling */
326 break;
327 }
328
329 } else {
330 mg_cry_internal(conn,
331 "%s: Error saving file %s",
332 __func__,
333 path);
334 remove_bad_file(conn, path);
335 }
336 fstore.access.fp = NULL;
337 }
338
339 } else {
340 mg_cry_internal(conn,
341 "%s: Cannot create file %s",
342 __func__,
343 path);
344 }
345 }
346#endif /* NO_FILESYSTEMS */
347
348 /* if (field_storage == MG_FORM_FIELD_STORAGE_READ) { */
349 /* The idea of "field_storage=read" is to let the API user read
350 * data chunk by chunk and to some data processing on the fly.
351 * This should avoid the need to store data in the server:
352 * It should neither be stored in memory, like
353 * "field_storage=get" does, nor in a file like
354 * "field_storage=store".
355 * However, for a "GET" request this does not make any much
356 * sense, since the data is already stored in memory, as it is
357 * part of the query string.
358 */
359 /* } */
360
363 /* Stop parsing the request */
364 break;
365 }
366
367 /* Proceed to next entry */
368 data = next;
369 }
370
371 return field_count;
372 }
373
374 content_type = mg_get_header(conn, "Content-Type");
375
376 if (!content_type
378 "APPLICATION/X-WWW-FORM-URLENCODED",
379 33)
381 "APPLICATION/WWW-FORM-URLENCODED",
382 31)) {
383 /* The form data is in the request body data, encoded in key/value
384 * pairs. */
385 int all_data_read = 0;
386
387 /* Read body data and split it in keys and values.
388 * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
389 * Here we use "POST", and read the data from the request body.
390 * The data read on the fly, so it is not required to buffer the
391 * entire request in memory before processing it. */
392 for (;;) {
393 const char *val;
394 const char *next;
395 ptrdiff_t keylen, vallen;
396 ptrdiff_t used;
398 int get_block;
399
400 if (buf_fill < (sizeof(buf) - 1)) {
401
402 size_t to_read = sizeof(buf) - 1 - buf_fill;
403 r = mg_read(conn, buf + buf_fill, to_read);
404 if ((r < 0) || ((r == 0) && all_data_read)) {
405 /* read error */
406 return -1;
407 }
408 if (r == 0) {
409 /* TODO: Create a function to get "all_data_read" from
410 * the conn object. All data is read if the Content-Length
411 * has been reached, or if chunked encoding is used and
412 * the end marker has been read, or if the connection has
413 * been closed. */
414 all_data_read = (buf_fill == 0);
415 }
416 buf_fill += r;
417 buf[buf_fill] = 0;
418 if (buf_fill < 1) {
419 break;
420 }
421 }
422
423 val = strchr(buf, '=');
424
425 if (!val) {
426 break;
427 }
428 keylen = val - buf;
429 val++;
430
431 /* Call callback */
432 memset(path, 0, sizeof(path));
433 field_count++;
435 buf,
436 (size_t)keylen,
437 NULL,
438 0,
439 path,
440 sizeof(path) - 1,
441 fdh);
442
445 /* Stop parsing the request */
446 break;
447 }
448
449#if !defined(NO_FILESYSTEMS)
451 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
452 fstore.access.fp = NULL;
453 }
454 file_size = 0;
455 if (!fstore.access.fp) {
456 mg_cry_internal(conn,
457 "%s: Cannot create file %s",
458 __func__,
459 path);
460 }
461 }
462#endif /* NO_FILESYSTEMS */
463
464 get_block = 0;
465 /* Loop to read values larger than sizeof(buf)-keylen-2 */
466 do {
467 next = strchr(val, '&');
468 if (next) {
469 vallen = next - val;
471 } else {
472 vallen = (ptrdiff_t)strlen(val);
474 }
475
477#if 0
479 /* This callback will deliver partial contents */
480 }
481#endif
482
483 /* Call callback */
485 ((get_block > 0) ? NULL : buf),
486 ((get_block > 0)
487 ? 0
488 : (size_t)keylen),
489 val,
490 (size_t *)&vallen,
491 fdh);
492 get_block++;
494 /* Stop request handling */
495 break;
496 }
498 /* Skip to next field */
500 }
501 }
502
503 if (next) {
504 next++;
505 } else {
506 /* vallen may have been modified by url_encoded_field_get */
507 next = val + vallen;
508 }
509
510#if !defined(NO_FILESYSTEMS)
511 if (fstore.access.fp) {
512 size_t n = (size_t)
513 fwrite(val, 1, (size_t)vallen, fstore.access.fp);
514 if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
515 mg_cry_internal(conn,
516 "%s: Cannot write file %s",
517 __func__,
518 path);
519 mg_fclose(&fstore.access);
520 remove_bad_file(conn, path);
521 }
522 file_size += (int64_t)n;
523 }
524#endif /* NO_FILESYSTEMS */
525
527 used = next - buf;
528 memmove(buf,
529 buf + (size_t)used,
530 sizeof(buf) - (size_t)used);
531 next = buf;
532 buf_fill -= used;
533 if (buf_fill < (sizeof(buf) - 1)) {
534
535 size_t to_read = sizeof(buf) - 1 - buf_fill;
536 r = mg_read(conn, buf + buf_fill, to_read);
537 if ((r < 0) || ((r == 0) && all_data_read)) {
538#if !defined(NO_FILESYSTEMS)
539 /* read error */
540 if (fstore.access.fp) {
541 mg_fclose(&fstore.access);
542 remove_bad_file(conn, path);
543 }
544 return -1;
545#endif /* NO_FILESYSTEMS */
546 }
547 if (r == 0) {
548 /* TODO: Create a function to get "all_data_read"
549 * from the conn object. All data is read if the
550 * Content-Length has been reached, or if chunked
551 * encoding is used and the end marker has been
552 * read, or if the connection has been closed. */
553 all_data_read = (buf_fill == 0);
554 }
555 buf_fill += r;
556 buf[buf_fill] = 0;
557 if (buf_fill < 1) {
558 break;
559 }
560 val = buf;
561 }
562 }
563
565
566#if !defined(NO_FILESYSTEMS)
567 if (fstore.access.fp) {
568 r = mg_fclose(&fstore.access);
569 if (r == 0) {
570 /* stored successfully */
571 r = field_stored(conn, path, file_size, fdh);
573 /* Stop request handling */
574 break;
575 }
576 } else {
577 mg_cry_internal(conn,
578 "%s: Error saving file %s",
579 __func__,
580 path);
581 remove_bad_file(conn, path);
582 }
583 fstore.access.fp = NULL;
584 }
585#endif /* NO_FILESYSTEMS */
586
587 if (all_data_read && (buf_fill == 0)) {
588 /* nothing more to process */
589 break;
590 }
591
592 /* Proceed to next entry */
593 used = next - buf;
594 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
595 buf_fill -= used;
596 }
597
598 return field_count;
599 }
600
601 if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
602 /* The form data is in the request body data, encoded as multipart
603 * content (see https://www.ietf.org/rfc/rfc1867.txt,
604 * https://www.ietf.org/rfc/rfc2388.txt). */
605 char *boundary;
606 size_t bl;
607 ptrdiff_t used;
609 char *hbuf;
610 const char *content_disp, *hend, *fbeg, *fend, *nbeg, *nend;
611 const char *next;
612 unsigned part_no;
613 int all_data_read = 0;
614
615 memset(&part_header, 0, sizeof(part_header));
616
617 /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
618 bl = 20;
619 while (content_type[bl] == ' ') {
620 bl++;
621 }
622
623 /* There has to be a BOUNDARY definition in the Content-Type header */
624 if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
625 /* Malformed request */
626 return -1;
627 }
628
629 /* Copy boundary string to variable "boundary" */
630 /* fbeg is pointer to start of value of boundary */
631 fbeg = content_type + bl + 9;
632 bl = strlen(fbeg);
633 boundary = (char *)mg_malloc(bl + 1);
634 if (!boundary) {
635 /* Out of memory */
636 mg_cry_internal(conn,
637 "%s: Cannot allocate memory for boundary [%lu]",
638 __func__,
639 (unsigned long)bl);
640 return -1;
641 }
643 boundary[bl] = 0;
644
645 /* RFC 2046 permits the boundary string to be quoted. */
646 /* If the boundary is quoted, trim the quotes */
647 if (boundary[0] == '"') {
648 hbuf = strchr(boundary + 1, '"');
649 if ((!hbuf) || (*hbuf != '"')) {
650 /* Malformed request */
652 return -1;
653 }
654 *hbuf = 0;
656 bl = strlen(boundary);
657 }
658
659 /* Do some sanity checks for boundary lengths */
660 if (bl > 70) {
661 /* From RFC 2046:
662 * Boundary delimiters must not appear within the
663 * encapsulated material, and must be no longer
664 * than 70 characters, not counting the two
665 * leading hyphens.
666 */
667
668 /* The algorithm can not work if bl >= sizeof(buf), or if buf
669 * can not hold the multipart header plus the boundary.
670 * Requests with long boundaries are not RFC compliant, maybe they
671 * are intended attacks to interfere with this algorithm. */
673 return -1;
674 }
675 if (bl < 4) {
676 /* Sanity check: A boundary string of less than 4 bytes makes
677 * no sense either. */
679 return -1;
680 }
681
682 for (part_no = 0;; part_no++) {
683 size_t towrite, fnlen, n;
684 int get_block;
685 size_t to_read = sizeof(buf) - 1 - buf_fill;
686
687 /* Unused without filesystems */
688 (void)n;
689
690 r = mg_read(conn, buf + buf_fill, to_read);
691 if ((r < 0) || ((r == 0) && all_data_read)) {
692 /* read error */
694 return -1;
695 }
696 if (r == 0) {
697 all_data_read = (buf_fill == 0);
698 }
699
700 buf_fill += r;
701 buf[buf_fill] = 0;
702 if (buf_fill < 1) {
703 /* No data */
705 return -1;
706 }
707
708 /* @see https://www.rfc-editor.org/rfc/rfc2046.html#section-5.1.1
709 *
710 * multipart-body := [preamble CRLF]
711 * dash-boundary transport-padding CRLF
712 * body-part *encapsulation
713 * close-delimiter transport-padding
714 * [CRLF epilogue]
715 */
716
717 if (part_no == 0) {
718 size_t preamble_length = 0;
719 /* skip over the preamble until we find a complete boundary
720 * limit the preamble length to prevent abuse */
721 /* +2 for the -- preceding the boundary */
722 while (preamble_length < 1024
724 && strncmp(buf + preamble_length + 2, boundary, bl)) {
726 }
727 /* reset the start of buf to remove the preamble */
728 if (0 == strncmp(buf + preamble_length + 2, boundary, bl)) {
729 memmove(buf,
730 buf + preamble_length,
731 (unsigned)buf_fill - (unsigned)preamble_length);
733 buf[buf_fill] = 0;
734 }
735 }
736
737 /* either it starts with a boundary and it's fine, or it's malformed
738 * because:
739 * - the preamble was longer than accepted
740 * - couldn't find a boundary at all in the body
741 * - didn't have a terminating boundary */
742 if (buf_fill < (bl + 2) || strncmp(buf, "--", 2)
743 || strncmp(buf + 2, boundary, bl)) {
744 /* Malformed request */
746 return -1;
747 }
748
749 /* skip the -- */
750 char *boundary_start = buf + 2;
751 size_t transport_padding = 0;
752 while (boundary_start[bl + transport_padding] == ' '
753 || boundary_start[bl + transport_padding] == '\t') {
755 }
757
758 /* after the transport padding, if the boundary isn't
759 * immediately followed by a \r\n then it is either... */
760 if (strncmp(boundary_end, "\r\n", 2)) {
761 /* ...the final boundary, and it is followed by --, (in which
762 * case it's the end of the request) or it's a malformed
763 * request */
764 if (strncmp(boundary_end, "--", 2)) {
765 /* Malformed request */
767 return -1;
768 }
769 /* Ingore any epilogue here */
770 break;
771 }
772
773 /* skip the \r\n */
774 hbuf = boundary_end + 2;
775 /* Next, we need to get the part header: Read until \r\n\r\n */
776 hend = strstr(hbuf, "\r\n\r\n");
777 if (!hend) {
778 /* Malformed request */
780 return -1;
781 }
782
783 part_header.num_headers =
784 parse_http_headers(&hbuf, part_header.http_headers);
785 if ((hend + 2) != hbuf) {
786 /* Malformed request */
788 return -1;
789 }
790
791 /* Skip \r\n\r\n */
792 hend += 4;
793
794 /* According to the RFC, every part has to have a header field like:
795 * Content-Disposition: form-data; name="..." */
796 content_disp = get_header(part_header.http_headers,
797 part_header.num_headers,
798 "Content-Disposition");
799 if (!content_disp) {
800 /* Malformed request */
802 return -1;
803 }
804
805 /* Get the mandatory name="..." part of the Content-Disposition
806 * header. */
807 nbeg = strstr(content_disp, "name=\"");
808 while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
809 /* It could be somethingname= instead of name= */
810 nbeg = strstr(nbeg + 1, "name=\"");
811 }
812
813 /* This line is not required, but otherwise some compilers
814 * generate spurious warnings. */
815 nend = nbeg;
816 /* And others complain, the result is unused. */
817 (void)nend;
818
819 /* If name=" is found, search for the closing " */
820 if (nbeg) {
821 nbeg += 6;
822 nend = strchr(nbeg, '\"');
823 if (!nend) {
824 /* Malformed request */
826 return -1;
827 }
828 } else {
829 /* name= without quotes is also allowed */
830 nbeg = strstr(content_disp, "name=");
831 while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
832 /* It could be somethingname= instead of name= */
833 nbeg = strstr(nbeg + 1, "name=");
834 }
835 if (!nbeg) {
836 /* Malformed request */
838 return -1;
839 }
840 nbeg += 5;
841
842 /* RFC 2616 Sec. 2.2 defines a list of allowed
843 * separators, but many of them make no sense
844 * here, e.g. various brackets or slashes.
845 * If they are used, probably someone is
846 * trying to attack with curious hand made
847 * requests. Only ; , space and tab seem to be
848 * reasonable here. Ignore everything else. */
849 nend = nbeg + strcspn(nbeg, ",; \t");
850 }
851
852 /* Get the optional filename="..." part of the Content-Disposition
853 * header. */
854 fbeg = strstr(content_disp, "filename=\"");
855 while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
856 /* It could be somethingfilename= instead of filename= */
857 fbeg = strstr(fbeg + 1, "filename=\"");
858 }
859
860 /* This line is not required, but otherwise some compilers
861 * generate spurious warnings. */
862 fend = fbeg;
863
864 /* If filename=" is found, search for the closing " */
865 if (fbeg) {
866 fbeg += 10;
867 fend = strchr(fbeg, '\"');
868
869 if (!fend) {
870 /* Malformed request (the filename field is optional, but if
871 * it exists, it needs to be terminated correctly). */
873 return -1;
874 }
875
876 /* TODO: check Content-Type */
877 /* Content-Type: application/octet-stream */
878 }
879 if (!fbeg) {
880 /* Try the same without quotes */
881 fbeg = strstr(content_disp, "filename=");
882 while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
883 /* It could be somethingfilename= instead of filename= */
884 fbeg = strstr(fbeg + 1, "filename=");
885 }
886 if (fbeg) {
887 fbeg += 9;
888 fend = fbeg + strcspn(fbeg, ",; \t");
889 }
890 }
891
892 if (!fbeg || !fend) {
893 fbeg = NULL;
894 fend = NULL;
895 fnlen = 0;
896 } else {
897 fnlen = (size_t)(fend - fbeg);
898 }
899
900 /* In theory, it could be possible that someone crafts
901 * a request like name=filename=xyz. Check if name and
902 * filename do not overlap. */
903 if (!(((ptrdiff_t)fbeg > (ptrdiff_t)nend)
904 || ((ptrdiff_t)nbeg > (ptrdiff_t)fend))) {
906 return -1;
907 }
908
909 /* Call callback for new field */
910 memset(path, 0, sizeof(path));
911 field_count++;
913 nbeg,
914 (size_t)(nend - nbeg),
915 ((fnlen > 0) ? fbeg : NULL),
916 fnlen,
917 path,
918 sizeof(path) - 1,
919 fdh);
920
921 /* If the boundary is already in the buffer, get the address,
922 * otherwise next will be NULL. */
923 next = search_boundary(hbuf,
924 (size_t)((buf - hbuf) + buf_fill),
925 boundary,
926 bl);
927
928#if !defined(NO_FILESYSTEMS)
930 /* Store the content to a file */
931 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
932 fstore.access.fp = NULL;
933 }
934 file_size = 0;
935
936 if (!fstore.access.fp) {
937 mg_cry_internal(conn,
938 "%s: Cannot create file %s",
939 __func__,
940 path);
941 }
942 }
943#endif /* NO_FILESYSTEMS */
944
945 get_block = 0;
946 while (!next) {
947 /* Set "towrite" to the number of bytes available
948 * in the buffer */
949 towrite = (size_t)(buf - hend + buf_fill);
950
951 if (towrite < bl + 4) {
952 /* Not enough data stored. */
953 /* Incomplete request. */
955 return -1;
956 }
957
958 /* Subtract the boundary length, to deal with
959 * cases the boundary is only partially stored
960 * in the buffer. */
961 towrite -= bl + 4;
962
964 r = unencoded_field_get(conn,
965 ((get_block > 0) ? NULL : nbeg),
966 ((get_block > 0)
967 ? 0
968 : (size_t)(nend - nbeg)),
969 hend,
970 towrite,
971 fdh);
972 get_block++;
974 /* Stop request handling */
975 break;
976 }
978 /* Skip to next field */
980 }
981 }
982
983#if !defined(NO_FILESYSTEMS)
985 if (fstore.access.fp) {
986
987 /* Store the content of the buffer. */
988 n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
989 if ((n != towrite) || (ferror(fstore.access.fp))) {
990 mg_cry_internal(conn,
991 "%s: Cannot write file %s",
992 __func__,
993 path);
994 mg_fclose(&fstore.access);
995 remove_bad_file(conn, path);
996 }
997 file_size += (int64_t)n;
998 }
999 }
1000#endif /* NO_FILESYSTEMS */
1001
1002 memmove(buf, hend + towrite, bl + 4);
1003 buf_fill = bl + 4;
1004 hend = buf;
1005
1006 /* Read new data */
1007 to_read = sizeof(buf) - 1 - buf_fill;
1008 r = mg_read(conn, buf + buf_fill, to_read);
1009 if ((r < 0) || ((r == 0) && all_data_read)) {
1010#if !defined(NO_FILESYSTEMS)
1011 /* read error */
1012 if (fstore.access.fp) {
1013 mg_fclose(&fstore.access);
1014 remove_bad_file(conn, path);
1015 }
1016#endif /* NO_FILESYSTEMS */
1018 return -1;
1019 }
1020 /* r==0 already handled, all_data_read is false here */
1021
1022 buf_fill += r;
1023 buf[buf_fill] = 0;
1024 /* buf_fill is at least 8 here */
1025
1026 /* Find boundary */
1027 next = search_boundary(buf, buf_fill, boundary, bl);
1028
1029 if (!next && (r == 0)) {
1030 /* incomplete request */
1031 all_data_read = 1;
1032 }
1033 }
1034
1035 towrite = (next ? (size_t)(next - hend) : 0);
1036
1038 /* Call callback */
1039 r = unencoded_field_get(conn,
1040 ((get_block > 0) ? NULL : nbeg),
1041 ((get_block > 0)
1042 ? 0
1043 : (size_t)(nend - nbeg)),
1044 hend,
1045 towrite,
1046 fdh);
1048 /* Stop request handling */
1049 break;
1050 }
1052 /* Skip to next field */
1054 }
1055 }
1056
1057#if !defined(NO_FILESYSTEMS)
1059
1060 if (fstore.access.fp) {
1061 n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
1062 if ((n != towrite) || (ferror(fstore.access.fp))) {
1063 mg_cry_internal(conn,
1064 "%s: Cannot write file %s",
1065 __func__,
1066 path);
1067 mg_fclose(&fstore.access);
1068 remove_bad_file(conn, path);
1069 } else {
1070 file_size += (int64_t)n;
1071 r = mg_fclose(&fstore.access);
1072 if (r == 0) {
1073 /* stored successfully */
1074 r = field_stored(conn, path, file_size, fdh);
1076 /* Stop request handling */
1077 break;
1078 }
1079 } else {
1080 mg_cry_internal(conn,
1081 "%s: Error saving file %s",
1082 __func__,
1083 path);
1084 remove_bad_file(conn, path);
1085 }
1086 }
1087 fstore.access.fp = NULL;
1088 }
1089 }
1090#endif /* NO_FILESYSTEMS */
1091
1094 /* Stop parsing the request */
1095 break;
1096 }
1097
1098 /* Remove from the buffer */
1099 if (next) {
1100 used = next - buf + 2;
1101 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
1102 buf_fill -= used;
1103 } else {
1104 buf_fill = 0;
1105 }
1106 }
1107
1108 /* All parts handled */
1110 return field_count;
1111 }
1112
1113 /* Unknown Content-Type */
1114 return -1;
1115}
1116
1117/* End of handle_form.inl */
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
int mg_strncasecmp(const char *s1, const char *s2, size_t len)
Definition civetweb.c:3091
#define mg_malloc_ctx(a, c)
Definition civetweb.c:1532
static void remove_bad_file(const struct mg_connection *conn, const char *path)
Definition civetweb.c:10920
static __inline void * mg_malloc(size_t a)
Definition civetweb.c:1509
static int mg_fopen(const struct mg_connection *conn, const char *path, int mode, struct mg_file *filep)
Definition civetweb.c:2987
static int parse_http_headers(char **buf, struct mg_header hdr[(64)])
Definition civetweb.c:11037
static const char * get_header(const struct mg_header *hdr, int num_hdr, const char *name)
Definition civetweb.c:3894
const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition civetweb.c:3929
#define mg_cry_internal(conn, fmt,...)
Definition civetweb.c:2655
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
Definition civetweb.c:7269
#define MG_FOPEN_MODE_WRITE
Definition civetweb.c:2918
static void remove_dot_segments(char *inout)
Definition civetweb.c:8362
#define MG_BUF_LEN
Definition civetweb.c:502
int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6835
static int mg_fclose(struct mg_file_access *fileacc)
Definition civetweb.c:3058
static __inline void mg_free(void *a)
Definition civetweb.c:1527
#define STRUCT_FILE_INITIALIZER
Definition civetweb.c:1937
@ MG_FORM_FIELD_STORAGE_GET
Definition civetweb.h:1275
@ MG_FORM_FIELD_STORAGE_ABORT
Definition civetweb.h:1279
@ MG_FORM_FIELD_STORAGE_STORE
Definition civetweb.h:1277
@ MG_FORM_FIELD_STORAGE_SKIP
Definition civetweb.h:1273
@ MG_FORM_FIELD_HANDLE_NEXT
Definition civetweb.h:1288
@ MG_FORM_FIELD_HANDLE_ABORT
Definition civetweb.h:1290
static int field_stored(const struct mg_connection *conn, const char *path, long long file_size, struct mg_form_data_handler *fdh)
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)
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)
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
struct mg_request_info request_info
Definition civetweb.c:2567
struct mg_context * phys_ctx
Definition civetweb.c:2570