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