GNU libmicrohttpd 0.9.77
Loading...
Searching...
No Matches
postprocessor.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2021 Daniel Pittman, Christian Grothoff, and Evgeny Grin
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
27#include "internal.h"
28#include "mhd_str.h"
29#include "mhd_compat.h"
30#include "mhd_assert.h"
31
37#define XBUF_SIZE 512
38
43{
44 /* general states */
49
50 /* url encoding-states */
54
55 /* post encoding-states */
60
61 /* nested post-encoding states */
67
68};
69
70
72{
77
83
89
95
99 RN_Dash2 = 4
101
102
109{
116
117
122struct MHD_PostProcessor
123{
124
129 struct MHD_Connection *connection;
130
135
139 void *cls;
140
144 const char *encoding;
145
149 const char *boundary;
150
154 char *nested_boundary;
155
159 char *content_name;
160
164 char *content_type;
165
169 char *content_filename;
170
174 char *content_transfer_encoding;
175
179 char xbuf[2];
180
184 size_t buffer_size;
185
189 size_t buffer_pos;
190
194 size_t xbuf_pos;
195
199 uint64_t value_offset;
200
204 size_t blen;
205
209 size_t nlen;
210
219 bool must_ikvi;
220
225 bool must_unescape_key;
226
230 enum PP_State state;
231
238 enum RN_State skip_rn;
239
244 enum PP_State dash_state;
245
250 enum NE_State have;
251
252};
253
254
255struct MHD_PostProcessor *
257 size_t buffer_size,
259 void *iter_cls)
260{
261 struct MHD_PostProcessor *ret;
262 const char *encoding;
263 const char *boundary;
264 size_t blen;
265
266 if ( (buffer_size < 256) ||
267 (NULL == connection) ||
268 (NULL == iter))
270 __FILE__,
271 __LINE__,
272 NULL);
273 if (MHD_NO == MHD_lookup_connection_value_n (connection,
278 &encoding,
279 NULL))
280 return NULL;
281 boundary = NULL;
283 encoding,
286 {
288 encoding,
291 return NULL;
292 boundary =
294 /* Q: should this be "strcasestr"? */
295 boundary = strstr (boundary, "boundary=");
296 if (NULL == boundary)
297 return NULL; /* failed to determine boundary */
298 boundary += MHD_STATICSTR_LEN_ ("boundary=");
299 blen = strlen (boundary);
300 if ( (blen < 2) ||
301 (blen * 2 + 2 > buffer_size) )
302 return NULL; /* (will be) out of memory or invalid boundary */
303 if ( (boundary[0] == '"') &&
304 (boundary[blen - 1] == '"') )
305 {
306 /* remove enclosing quotes */
307 ++boundary;
308 blen -= 2;
309 }
310 }
311 else
312 blen = 0;
313 buffer_size += 4; /* round up to get nice block sizes despite boundary search */
314
315 /* add +1 to ensure we ALWAYS have a zero-termination at the end */
316 if (NULL == (ret = MHD_calloc_ (1, sizeof (struct MHD_PostProcessor)
317 + buffer_size + 1)))
318 return NULL;
319 ret->connection = connection;
320 ret->ikvi = iter;
321 ret->cls = iter_cls;
322 ret->encoding = encoding;
323 ret->buffer_size = buffer_size;
324 ret->state = PP_Init;
325 ret->blen = blen;
326 ret->boundary = boundary;
327 ret->skip_rn = RN_Inactive;
328 return ret;
329}
330
331
349static void
350process_value (struct MHD_PostProcessor *pp,
351 const char *value_start,
352 const char *value_end,
353 const char *last_escape)
354{
355 char xbuf[XBUF_SIZE + 1];
356 size_t xoff;
357
358 mhd_assert (pp->xbuf_pos < sizeof (xbuf));
359 /* 'value_start' and 'value_end' must be either both non-NULL or both NULL */
360 mhd_assert ( (NULL == value_start) || (NULL != value_end) );
361 mhd_assert ( (NULL != value_start) || (NULL == value_end) );
362 mhd_assert ( (NULL == last_escape) || (NULL != value_start) );
363 /* move remaining input from previous round into processing buffer */
364 if (0 != pp->xbuf_pos)
365 memcpy (xbuf,
366 pp->xbuf,
367 pp->xbuf_pos);
368 xoff = pp->xbuf_pos;
369 pp->xbuf_pos = 0;
370 if ( (NULL != last_escape) &&
371 (((size_t) (value_end - last_escape)) < sizeof (pp->xbuf)) )
372 {
373 pp->xbuf_pos = value_end - last_escape;
374 memcpy (pp->xbuf,
375 last_escape,
376 value_end - last_escape);
377 value_end = last_escape;
378 }
379 while ( (value_start != value_end) ||
380 (pp->must_ikvi) ||
381 (xoff > 0) )
382 {
383 size_t delta = value_end - value_start;
384 bool cut = false;
385 size_t clen = 0;
386
387 if (delta > XBUF_SIZE - xoff)
388 delta = XBUF_SIZE - xoff;
389 /* move (additional) input into processing buffer */
390 if (0 != delta)
391 {
392 memcpy (&xbuf[xoff],
393 value_start,
394 delta);
395 xoff += delta;
396 value_start += delta;
397 }
398 /* find if escape sequence is at the end of the processing buffer;
399 if so, exclude those from processing (reduce delta to point at
400 end of processed region) */
401 if ( (xoff > 0) &&
402 ('%' == xbuf[xoff - 1]) )
403 {
404 cut = (xoff != XBUF_SIZE);
405 xoff--;
406 if (cut)
407 {
408 /* move escape sequence into buffer for next function invocation */
409 pp->xbuf[0] = '%';
410 pp->xbuf_pos = 1;
411 }
412 else
413 {
414 /* just skip escape sequence for next loop iteration */
415 delta = xoff;
416 clen = 1;
417 }
418 }
419 else if ( (xoff > 1) &&
420 ('%' == xbuf[xoff - 2]) )
421 {
422 cut = (xoff != XBUF_SIZE);
423 xoff -= 2;
424 if (cut)
425 {
426 /* move escape sequence into buffer for next function invocation */
427 memcpy (pp->xbuf,
428 &xbuf[xoff],
429 2);
430 pp->xbuf_pos = 2;
431 }
432 else
433 {
434 /* just skip escape sequence for next loop iteration */
435 delta = xoff;
436 clen = 2;
437 }
438 }
439 mhd_assert (xoff < sizeof (xbuf));
440 /* unescape */
441 xbuf[xoff] = '\0'; /* 0-terminate in preparation */
442 if (0 != xoff)
443 {
444 MHD_unescape_plus (xbuf);
445 xoff = MHD_http_unescape (xbuf);
446 }
447 /* finally: call application! */
448 if (pp->must_ikvi || (0 != xoff) )
449 {
450 pp->must_ikvi = false;
451 if (MHD_NO == pp->ikvi (pp->cls,
453 (const char *) &pp[1], /* key */
454 NULL,
455 NULL,
456 NULL,
457 xbuf,
458 pp->value_offset,
459 xoff))
460 {
461 pp->state = PP_Error;
462 return;
463 }
464 }
465 pp->value_offset += xoff;
466 if (cut)
467 break;
468 if (0 != clen)
469 {
470 xbuf[delta] = '%'; /* undo 0-termination */
471 memmove (xbuf,
472 &xbuf[delta],
473 clen);
474 }
475 xoff = clen;
476 }
477}
478
479
488static enum MHD_Result
489post_process_urlencoded (struct MHD_PostProcessor *pp,
490 const char *post_data,
491 size_t post_data_len)
492{
493 char *kbuf = (char *) &pp[1];
494 size_t poff;
495 const char *start_key = NULL;
496 const char *end_key = NULL;
497 const char *start_value = NULL;
498 const char *end_value = NULL;
499 const char *last_escape = NULL;
500
501 mhd_assert (PP_Callback != pp->state);
502
503 poff = 0;
504 while ( ( (poff < post_data_len) ||
505 (pp->state == PP_Callback) ) &&
506 (pp->state != PP_Error) )
507 {
508 switch (pp->state)
509 {
510 case PP_Error:
511 /* clearly impossible as per while loop invariant */
512 abort ();
513 break; /* Unreachable */
514 case PP_Init:
515 /* initial phase */
516 mhd_assert (NULL == start_key);
517 mhd_assert (NULL == end_key);
518 mhd_assert (NULL == start_value);
519 mhd_assert (NULL == end_value);
520 switch (post_data[poff])
521 {
522 case '=':
523 /* Case: (no key)'=' */
524 /* Empty key with value */
525 pp->state = PP_Error;
526 continue;
527 case '&':
528 /* Case: (no key)'&' */
529 /* Empty key without value */
530 poff++;
531 continue;
532 case '\n':
533 case '\r':
534 /* Case: (no key)'\n' or (no key)'\r' */
535 pp->state = PP_Done;
536 poff++;
537 break;
538 default:
539 /* normal character, key start, advance! */
540 pp->state = PP_ProcessKey;
541 start_key = &post_data[poff];
542 pp->must_ikvi = true;
543 poff++;
544 continue;
545 }
546 break; /* end PP_Init */
547 case PP_ProcessKey:
548 /* key phase */
549 mhd_assert (NULL == start_value);
550 mhd_assert (NULL == end_value);
551 mhd_assert (NULL != start_key || 0 == poff);
552 mhd_assert (0 != poff || NULL == start_key);
553 mhd_assert (NULL == end_key);
554 switch (post_data[poff])
555 {
556 case '=':
557 /* Case: 'key=' */
558 if (0 != poff)
559 end_key = &post_data[poff];
560 poff++;
561 pp->state = PP_ProcessValue;
562 break;
563 case '&':
564 /* Case: 'key&' */
565 if (0 != poff)
566 end_key = &post_data[poff];
567 poff++;
568 pp->state = PP_Callback;
569 break;
570 case '\n':
571 case '\r':
572 /* Case: 'key\n' or 'key\r' */
573 if (0 != poff)
574 end_key = &post_data[poff];
575 /* No advance here, 'PP_Done' will be selected by next 'PP_Init' phase */
576 pp->state = PP_Callback;
577 break;
578 default:
579 /* normal character, advance! */
580 if (0 == poff)
581 start_key = post_data;
582 poff++;
583 break;
584 }
585 mhd_assert (NULL == end_key || NULL != start_key);
586 break; /* end PP_ProcessKey */
587 case PP_ProcessValue:
588 if (NULL == start_value)
589 start_value = &post_data[poff];
590 switch (post_data[poff])
591 {
592 case '=':
593 /* case 'key==' */
594 pp->state = PP_Error;
595 continue;
596 case '&':
597 /* case 'value&' */
598 end_value = &post_data[poff];
599 poff++;
600 if (pp->must_ikvi ||
601 (start_value != end_value) )
602 {
603 pp->state = PP_Callback;
604 }
605 else
606 {
607 pp->buffer_pos = 0;
608 pp->value_offset = 0;
609 pp->state = PP_Init;
610 start_value = NULL;
611 end_value = NULL;
612 }
613 continue;
614 case '\n':
615 case '\r':
616 /* Case: 'value\n' or 'value\r' */
617 end_value = &post_data[poff];
618 if (pp->must_ikvi ||
619 (start_value != end_value) )
620 pp->state = PP_Callback; /* No poff advance here to set PP_Done in the next iteration */
621 else
622 {
623 poff++;
624 pp->state = PP_Done;
625 }
626 break;
627 case '%':
628 last_escape = &post_data[poff];
629 poff++;
630 break;
631 case '0':
632 case '1':
633 case '2':
634 case '3':
635 case '4':
636 case '5':
637 case '6':
638 case '7':
639 case '8':
640 case '9':
641 /* character, may be part of escaping */
642 poff++;
643 continue;
644 default:
645 /* normal character, no more escaping! */
646 last_escape = NULL;
647 poff++;
648 continue;
649 }
650 break; /* end PP_ProcessValue */
651 case PP_Done:
652 switch (post_data[poff])
653 {
654 case '\n':
655 case '\r':
656 poff++;
657 continue;
658 }
659 /* unexpected data at the end, fail! */
660 pp->state = PP_Error;
661 break;
662 case PP_Callback:
663 mhd_assert ((NULL != end_key) || (NULL == start_key));
664 if (1)
665 {
666 const size_t key_len = end_key - start_key;
667 if (0 != key_len)
668 {
669 if ( (pp->buffer_pos + key_len >= pp->buffer_size) ||
670 (pp->buffer_pos + key_len < pp->buffer_pos) )
671 {
672 /* key too long, cannot parse! */
673 pp->state = PP_Error;
674 continue;
675 }
676 /* compute key, if we have not already */
677 memcpy (&kbuf[pp->buffer_pos],
678 start_key,
679 key_len);
680 pp->buffer_pos += key_len;
681 start_key = NULL;
682 end_key = NULL;
683 pp->must_unescape_key = true;
684 }
685 }
686#ifdef _DEBUG
687 else
688 mhd_assert (0 != pp->buffer_pos);
689#endif /* _DEBUG */
690 if (pp->must_unescape_key)
691 {
692 kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
693 MHD_unescape_plus (kbuf);
694 MHD_http_unescape (kbuf);
695 pp->must_unescape_key = false;
696 }
697 process_value (pp,
698 start_value,
699 end_value,
700 NULL);
701 if (PP_Error == pp->state)
702 continue;
703 pp->value_offset = 0;
704 start_value = NULL;
705 end_value = NULL;
706 pp->buffer_pos = 0;
707 pp->state = PP_Init;
708 break;
709 default:
711 __FILE__,
712 __LINE__,
713 NULL); /* should never happen! */
714 }
715 mhd_assert ((end_key == NULL) || (start_key != NULL));
716 mhd_assert ((end_value == NULL) || (start_value != NULL));
717 }
718
719 mhd_assert (PP_Callback != pp->state);
720
721 if (PP_Error == pp->state)
722 {
723 /* State in error, returning failure */
724 return MHD_NO;
725 }
726
727 /* save remaining data for next iteration */
728 if (NULL != start_key)
729 {
730 size_t key_len;
731 mhd_assert ((PP_ProcessKey == pp->state) || (NULL != end_key));
732 if (NULL == end_key)
733 end_key = &post_data[poff];
734 key_len = end_key - start_key;
735 mhd_assert (0 != key_len); /* it must be always non-zero here */
736 if (pp->buffer_pos + key_len >= pp->buffer_size)
737 {
738 pp->state = PP_Error;
739 return MHD_NO;
740 }
741 memcpy (&kbuf[pp->buffer_pos],
742 start_key,
743 key_len);
744 pp->buffer_pos += key_len;
745 pp->must_unescape_key = true;
746 start_key = NULL;
747 end_key = NULL;
748 }
749 if ( (NULL != start_value) &&
750 (PP_ProcessValue == pp->state) )
751 {
752 /* compute key, if we have not already */
753 if (pp->must_unescape_key)
754 {
755 kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
756 MHD_unescape_plus (kbuf);
757 MHD_http_unescape (kbuf);
758 pp->must_unescape_key = false;
759 }
760 if (NULL == end_value)
761 end_value = &post_data[poff];
762 if ( (NULL != last_escape) &&
763 (2 < (end_value - last_escape)) )
764 last_escape = NULL;
765 process_value (pp,
766 start_value,
767 end_value,
768 last_escape);
769 pp->must_ikvi = false;
770 }
771 if (PP_Error == pp->state)
772 {
773 /* State in error, returning failure */
774 return MHD_NO;
775 }
776 return MHD_YES;
777}
778
779
790static int
791try_match_header (const char *prefix,
792 size_t prefix_len,
793 char *line,
794 char **suffix)
795{
796 if (NULL != *suffix)
797 return MHD_NO;
798 while (0 != *line)
799 {
800 if (MHD_str_equal_caseless_n_ (prefix,
801 line,
802 prefix_len))
803 {
804 *suffix = strdup (&line[prefix_len]);
805 return MHD_YES;
806 }
807 ++line;
808 }
809 return MHD_NO;
810}
811
812
826static int
827find_boundary (struct MHD_PostProcessor *pp,
828 const char *boundary,
829 size_t blen,
830 size_t *ioffptr,
831 enum PP_State next_state,
832 enum PP_State next_dash_state)
833{
834 char *buf = (char *) &pp[1];
835 const char *dash;
836
837 if (pp->buffer_pos < 2 + blen)
838 {
839 if (pp->buffer_pos == pp->buffer_size)
840 pp->state = PP_Error; /* out of memory */
841 /* ++(*ioffptr); */
842 return MHD_NO; /* not enough data */
843 }
844 if ( (0 != memcmp ("--",
845 buf,
846 2)) ||
847 (0 != memcmp (&buf[2],
848 boundary,
849 blen)))
850 {
851 if (pp->state != PP_Init)
852 {
853 /* garbage not allowed */
854 pp->state = PP_Error;
855 }
856 else
857 {
858 /* skip over garbage (RFC 2046, 5.1.1) */
859 dash = memchr (buf,
860 '-',
861 pp->buffer_pos);
862 if (NULL == dash)
863 (*ioffptr) += pp->buffer_pos; /* skip entire buffer */
864 else if (dash == buf)
865 (*ioffptr)++; /* at least skip one byte */
866 else
867 (*ioffptr) += dash - buf; /* skip to first possible boundary */
868 }
869 return MHD_NO; /* expected boundary */
870 }
871 /* remove boundary from buffer */
872 (*ioffptr) += 2 + blen;
873 /* next: start with headers */
874 pp->skip_rn = RN_Dash;
875 pp->state = next_state;
876 pp->dash_state = next_dash_state;
877 return MHD_YES;
878}
879
880
887static void
888try_get_value (const char *buf,
889 const char *key,
890 char **destination)
891{
892 const char *spos;
893 const char *bpos;
894 const char *endv;
895 size_t klen;
896 size_t vlen;
897
898 if (NULL != *destination)
899 return;
900 bpos = buf;
901 klen = strlen (key);
902 while (NULL != (spos = strstr (bpos, key)))
903 {
904 if ( (spos[klen] != '=') ||
905 ( (spos != buf) &&
906 (spos[-1] != ' ') ) )
907 {
908 /* no match */
909 bpos = spos + 1;
910 continue;
911 }
912 if (spos[klen + 1] != '"')
913 return; /* not quoted */
914 if (NULL == (endv = strchr (&spos[klen + 2],
915 '\"')))
916 return; /* no end-quote */
917 vlen = endv - spos - klen - 1;
918 *destination = malloc (vlen);
919 if (NULL == *destination)
920 return; /* out of memory */
921 (*destination)[vlen - 1] = '\0';
922 memcpy (*destination,
923 &spos[klen + 2],
924 vlen - 1);
925 return; /* success */
926 }
927}
928
929
945static int
946process_multipart_headers (struct MHD_PostProcessor *pp,
947 size_t *ioffptr,
948 enum PP_State next_state)
949{
950 char *buf = (char *) &pp[1];
951 size_t newline;
952
953 newline = 0;
954 while ( (newline < pp->buffer_pos) &&
955 (buf[newline] != '\r') &&
956 (buf[newline] != '\n') )
957 newline++;
958 if (newline == pp->buffer_size)
959 {
960 pp->state = PP_Error;
961 return MHD_NO; /* out of memory */
962 }
963 if (newline == pp->buffer_pos)
964 return MHD_NO; /* will need more data */
965 if (0 == newline)
966 {
967 /* empty line - end of headers */
968 pp->skip_rn = RN_Full;
969 pp->state = next_state;
970 return MHD_YES;
971 }
972 /* got an actual header */
973 if (buf[newline] == '\r')
974 pp->skip_rn = RN_OptN;
975 buf[newline] = '\0';
976 if (MHD_str_equal_caseless_n_ ("Content-disposition: ",
977 buf,
978 MHD_STATICSTR_LEN_ ("Content-disposition: ")))
979 {
980 try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
981 "name",
982 &pp->content_name);
983 try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
984 "filename",
985 &pp->content_filename);
986 }
987 else
988 {
989 try_match_header ("Content-type: ",
990 MHD_STATICSTR_LEN_ ("Content-type: "),
991 buf,
992 &pp->content_type);
993 try_match_header ("Content-Transfer-Encoding: ",
994 MHD_STATICSTR_LEN_ ("Content-Transfer-Encoding: "),
995 buf,
996 &pp->content_transfer_encoding);
997 }
998 (*ioffptr) += newline + 1;
999 return MHD_YES;
1000}
1001
1002
1019static int
1020process_value_to_boundary (struct MHD_PostProcessor *pp,
1021 size_t *ioffptr,
1022 const char *boundary,
1023 size_t blen,
1024 enum PP_State next_state,
1025 enum PP_State next_dash_state)
1026{
1027 char *buf = (char *) &pp[1];
1028 size_t newline;
1029 const char *r;
1030
1031 /* all data in buf until the boundary
1032 (\r\n--+boundary) is part of the value */
1033 newline = 0;
1034 while (1)
1035 {
1036 while (newline + 4 < pp->buffer_pos)
1037 {
1038 r = memchr (&buf[newline],
1039 '\r',
1040 pp->buffer_pos - newline - 4);
1041 if (NULL == r)
1042 {
1043 newline = pp->buffer_pos - 4;
1044 break;
1045 }
1046 newline = r - buf;
1047 if (0 == memcmp ("\r\n--",
1048 &buf[newline],
1049 4))
1050 break;
1051 newline++;
1052 }
1053 if (newline + blen + 4 <= pp->buffer_pos)
1054 {
1055 /* can check boundary */
1056 if (0 != memcmp (&buf[newline + 4],
1057 boundary,
1058 blen))
1059 {
1060 /* no boundary, "\r\n--" is part of content, skip */
1061 newline += 4;
1062 continue;
1063 }
1064 else
1065 {
1066 /* boundary found, process until newline then
1067 skip boundary and go back to init */
1068 pp->skip_rn = RN_Dash;
1069 pp->state = next_state;
1070 pp->dash_state = next_dash_state;
1071 (*ioffptr) += blen + 4; /* skip boundary as well */
1072 buf[newline] = '\0';
1073 break;
1074 }
1075 }
1076 else
1077 {
1078 /* cannot check for boundary, process content that
1079 we have and check again later; except, if we have
1080 no content, abort (out of memory) */
1081 if ( (0 == newline) &&
1082 (pp->buffer_pos == pp->buffer_size) )
1083 {
1084 pp->state = PP_Error;
1085 return MHD_NO;
1086 }
1087 break;
1088 }
1089 }
1090 /* newline is either at beginning of boundary or
1091 at least at the last character that we are sure
1092 is not part of the boundary */
1093 if ( ( (pp->must_ikvi) ||
1094 (0 != newline) ) &&
1095 (MHD_NO == pp->ikvi (pp->cls,
1097 pp->content_name,
1098 pp->content_filename,
1099 pp->content_type,
1100 pp->content_transfer_encoding,
1101 buf,
1102 pp->value_offset,
1103 newline)) )
1104 {
1105 pp->state = PP_Error;
1106 return MHD_NO;
1107 }
1108 pp->must_ikvi = false;
1109 pp->value_offset += newline;
1110 (*ioffptr) += newline;
1111 return MHD_YES;
1112}
1113
1114
1119static void
1120free_unmarked (struct MHD_PostProcessor *pp)
1121{
1122 if ( (NULL != pp->content_name) &&
1123 (0 == (pp->have & NE_content_name)) )
1124 {
1125 free (pp->content_name);
1126 pp->content_name = NULL;
1127 }
1128 if ( (NULL != pp->content_type) &&
1129 (0 == (pp->have & NE_content_type)) )
1130 {
1131 free (pp->content_type);
1132 pp->content_type = NULL;
1133 }
1134 if ( (NULL != pp->content_filename) &&
1135 (0 == (pp->have & NE_content_filename)) )
1136 {
1137 free (pp->content_filename);
1138 pp->content_filename = NULL;
1139 }
1140 if ( (NULL != pp->content_transfer_encoding) &&
1141 (0 == (pp->have & NE_content_transfer_encoding)) )
1142 {
1143 free (pp->content_transfer_encoding);
1144 pp->content_transfer_encoding = NULL;
1145 }
1146}
1147
1148
1157static enum MHD_Result
1158post_process_multipart (struct MHD_PostProcessor *pp,
1159 const char *post_data,
1160 size_t post_data_len)
1161{
1162 char *buf;
1163 size_t max;
1164 size_t ioff;
1165 size_t poff;
1166 int state_changed;
1167
1168 buf = (char *) &pp[1];
1169 ioff = 0;
1170 poff = 0;
1171 state_changed = 1;
1172 while ( (poff < post_data_len) ||
1173 ( (pp->buffer_pos > 0) &&
1174 (0 != state_changed) ) )
1175 {
1176 /* first, move as much input data
1177 as possible to our internal buffer */
1178 max = pp->buffer_size - pp->buffer_pos;
1179 if (max > post_data_len - poff)
1180 max = post_data_len - poff;
1181 memcpy (&buf[pp->buffer_pos],
1182 &post_data[poff],
1183 max);
1184 poff += max;
1185 pp->buffer_pos += max;
1186 if ( (0 == max) &&
1187 (0 == state_changed) &&
1188 (poff < post_data_len) )
1189 {
1190 pp->state = PP_Error;
1191 return MHD_NO; /* out of memory */
1192 }
1193 state_changed = 0;
1194
1195 /* first state machine for '\r'-'\n' and '--' handling */
1196 switch (pp->skip_rn)
1197 {
1198 case RN_Inactive:
1199 break;
1200 case RN_OptN:
1201 if (buf[0] == '\n')
1202 {
1203 ioff++;
1204 pp->skip_rn = RN_Inactive;
1205 goto AGAIN;
1206 }
1207 /* fall-through! */
1208 case RN_Dash:
1209 if (buf[0] == '-')
1210 {
1211 ioff++;
1212 pp->skip_rn = RN_Dash2;
1213 goto AGAIN;
1214 }
1215 pp->skip_rn = RN_Full;
1216 /* fall-through! */
1217 case RN_Full:
1218 if (buf[0] == '\r')
1219 {
1220 if ( (pp->buffer_pos > 1) &&
1221 ('\n' == buf[1]) )
1222 {
1223 pp->skip_rn = RN_Inactive;
1224 ioff += 2;
1225 }
1226 else
1227 {
1228 pp->skip_rn = RN_OptN;
1229 ioff++;
1230 }
1231 goto AGAIN;
1232 }
1233 if (buf[0] == '\n')
1234 {
1235 ioff++;
1236 pp->skip_rn = RN_Inactive;
1237 goto AGAIN;
1238 }
1239 pp->skip_rn = RN_Inactive;
1240 pp->state = PP_Error;
1241 return MHD_NO; /* no '\r\n' */
1242 case RN_Dash2:
1243 if (buf[0] == '-')
1244 {
1245 ioff++;
1246 pp->skip_rn = RN_Full;
1247 pp->state = pp->dash_state;
1248 goto AGAIN;
1249 }
1250 pp->state = PP_Error;
1251 break;
1252 }
1253
1254 /* main state engine */
1255 switch (pp->state)
1256 {
1257 case PP_Error:
1258 return MHD_NO;
1259 case PP_Done:
1260 /* did not expect to receive more data */
1261 pp->state = PP_Error;
1262 return MHD_NO;
1263 case PP_Init:
1275 (void) find_boundary (pp,
1276 pp->boundary,
1277 pp->blen,
1278 &ioff,
1280 PP_Done);
1281 break;
1282 case PP_NextBoundary:
1283 if (MHD_NO == find_boundary (pp,
1284 pp->boundary,
1285 pp->blen,
1286 &ioff,
1288 PP_Done))
1289 {
1290 if (pp->state == PP_Error)
1291 return MHD_NO;
1292 goto END;
1293 }
1294 break;
1296 pp->must_ikvi = true;
1297 if (MHD_NO ==
1299 &ioff,
1301 {
1302 if (pp->state == PP_Error)
1303 return MHD_NO;
1304 else
1305 goto END;
1306 }
1307 state_changed = 1;
1308 break;
1310 if ( (NULL != pp->content_type) &&
1311 (MHD_str_equal_caseless_n_ (pp->content_type,
1312 "multipart/mixed",
1313 MHD_STATICSTR_LEN_ ("multipart/mixed"))))
1314 {
1315 pp->nested_boundary = strstr (pp->content_type,
1316 "boundary=");
1317 if (NULL == pp->nested_boundary)
1318 {
1319 pp->state = PP_Error;
1320 return MHD_NO;
1321 }
1322 pp->nested_boundary =
1323 strdup (&pp->nested_boundary[MHD_STATICSTR_LEN_ ("boundary=")]);
1324 if (NULL == pp->nested_boundary)
1325 {
1326 /* out of memory */
1327 pp->state = PP_Error;
1328 return MHD_NO;
1329 }
1330 /* free old content type, we will need that field
1331 for the content type of the nested elements */
1332 free (pp->content_type);
1333 pp->content_type = NULL;
1334 pp->nlen = strlen (pp->nested_boundary);
1335 pp->state = PP_Nested_Init;
1336 state_changed = 1;
1337 break;
1338 }
1339 pp->state = PP_ProcessValueToBoundary;
1340 pp->value_offset = 0;
1341 state_changed = 1;
1342 break;
1345 &ioff,
1346 pp->boundary,
1347 pp->blen,
1349 PP_Done))
1350 {
1351 if (pp->state == PP_Error)
1352 return MHD_NO;
1353 break;
1354 }
1355 break;
1356 case PP_PerformCleanup:
1357 /* clean up state of one multipart form-data element! */
1358 pp->have = NE_none;
1359 free_unmarked (pp);
1360 if (NULL != pp->nested_boundary)
1361 {
1362 free (pp->nested_boundary);
1363 pp->nested_boundary = NULL;
1364 }
1365 pp->state = PP_ProcessEntryHeaders;
1366 state_changed = 1;
1367 break;
1368 case PP_Nested_Init:
1369 if (NULL == pp->nested_boundary)
1370 {
1371 pp->state = PP_Error;
1372 return MHD_NO;
1373 }
1374 if (MHD_NO == find_boundary (pp,
1375 pp->nested_boundary,
1376 pp->nlen,
1377 &ioff,
1379 PP_NextBoundary /* or PP_Error? */))
1380 {
1381 if (pp->state == PP_Error)
1382 return MHD_NO;
1383 goto END;
1384 }
1385 break;
1387 /* remember what headers were given
1388 globally */
1389 pp->have = NE_none;
1390 if (NULL != pp->content_name)
1391 pp->have |= NE_content_name;
1392 if (NULL != pp->content_type)
1393 pp->have |= NE_content_type;
1394 if (NULL != pp->content_filename)
1395 pp->have |= NE_content_filename;
1396 if (NULL != pp->content_transfer_encoding)
1397 pp->have |= NE_content_transfer_encoding;
1399 state_changed = 1;
1400 break;
1402 pp->value_offset = 0;
1403 if (MHD_NO ==
1405 &ioff,
1407 {
1408 if (pp->state == PP_Error)
1409 return MHD_NO;
1410 else
1411 goto END;
1412 }
1413 state_changed = 1;
1414 break;
1417 &ioff,
1418 pp->nested_boundary,
1419 pp->nlen,
1422 {
1423 if (pp->state == PP_Error)
1424 return MHD_NO;
1425 break;
1426 }
1427 break;
1429 free_unmarked (pp);
1431 state_changed = 1;
1432 break;
1433 default:
1435 __FILE__,
1436 __LINE__,
1437 NULL); /* should never happen! */
1438 }
1439AGAIN:
1440 if (ioff > 0)
1441 {
1442 memmove (buf,
1443 &buf[ioff],
1444 pp->buffer_pos - ioff);
1445 pp->buffer_pos -= ioff;
1446 ioff = 0;
1447 state_changed = 1;
1448 }
1449 }
1450END:
1451 if (0 != ioff)
1452 {
1453 memmove (buf,
1454 &buf[ioff],
1455 pp->buffer_pos - ioff);
1456 pp->buffer_pos -= ioff;
1457 }
1458 if (poff < post_data_len)
1459 {
1460 pp->state = PP_Error;
1461 return MHD_NO; /* serious error */
1462 }
1463 return MHD_YES;
1464}
1465
1466
1467enum MHD_Result
1468MHD_post_process (struct MHD_PostProcessor *pp,
1469 const char *post_data,
1470 size_t post_data_len)
1471{
1472 if (0 == post_data_len)
1473 return MHD_YES;
1474 if (NULL == pp)
1475 return MHD_NO;
1477 pp->encoding,
1480 return post_process_urlencoded (pp,
1481 post_data,
1482 post_data_len);
1484 pp->encoding,
1487 return post_process_multipart (pp,
1488 post_data,
1489 post_data_len);
1490 /* this should never be reached */
1491 return MHD_NO;
1492}
1493
1494
1495enum MHD_Result
1496MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
1497{
1498 enum MHD_Result ret;
1499
1500 if (NULL == pp)
1501 return MHD_YES;
1502 if (PP_ProcessValue == pp->state)
1503 {
1504 /* key without terminated value left at the end of the
1505 buffer; fake receiving a termination character to
1506 ensure it is also processed */
1508 "\n",
1509 1);
1510 }
1511 /* These internal strings need cleaning up since
1512 the post-processing may have been interrupted
1513 at any stage */
1514 if ( (pp->xbuf_pos > 0) ||
1515 ( (pp->state != PP_Done) &&
1516 (pp->state != PP_Init) ) )
1517 ret = MHD_NO;
1518 else
1519 ret = MHD_YES;
1520 pp->have = NE_none;
1521 free_unmarked (pp);
1522 if (NULL != pp->nested_boundary)
1523 free (pp->nested_boundary);
1524 free (pp);
1525 return ret;
1526}
1527
1528
1529/* end of postprocessor.c */
#define MHD_HTTP_HEADER_CONTENT_TYPE
Definition microhttpd.h:618
#define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
#define MHD_HTTP_POST_ENCODING_FORM_URLENCODED
enum MHD_Result MHD_destroy_post_processor(struct MHD_PostProcessor *pp)
enum MHD_Result MHD_post_process(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
struct MHD_PostProcessor * MHD_create_post_processor(struct MHD_Connection *connection, size_t buffer_size, MHD_PostDataIterator iter, void *iter_cls)
_MHD_EXTERN enum MHD_Result MHD_lookup_connection_value_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char **value_ptr, size_t *value_size_ptr)
Definition connection.c:649
void MHD_unescape_plus(char *arg)
Definition internal.c:123
MHD_PanicCallback mhd_panic
Definition panic.c:31
void * mhd_panic_cls
Definition panic.c:36
#define mhd_assert(CHK)
Definition mhd_assert.h:39
void * MHD_calloc_(size_t nelem, size_t elsize)
Definition mhd_compat.c:98
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:378
#define MHD_STATICSTR_LEN_(macro)
Definition mhd_str.h:45
#define NULL
MHD internal shared structures.
macros for mhd_assert()
Header for platform missing functions.
Header for string manipulating helpers.
MHD_Result
Definition microhttpd.h:158
@ MHD_YES
Definition microhttpd.h:167
@ MHD_NO
Definition microhttpd.h:162
_MHD_EXTERN size_t MHD_http_unescape(char *val)
Definition internal.c:142
enum MHD_Result(* MHD_PostDataIterator)(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *data, uint64_t off, size_t size)
@ MHD_POSTDATA_KIND
@ MHD_HEADER_KIND
static enum MHD_Result post_process_multipart(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
static int try_match_header(const char *prefix, size_t prefix_len, char *line, char **suffix)
static int find_boundary(struct MHD_PostProcessor *pp, const char *boundary, size_t blen, size_t *ioffptr, enum PP_State next_state, enum PP_State next_dash_state)
RN_State
@ RN_Dash
@ RN_Inactive
@ RN_Full
@ RN_OptN
@ RN_Dash2
static int process_value_to_boundary(struct MHD_PostProcessor *pp, size_t *ioffptr, const char *boundary, size_t blen, enum PP_State next_state, enum PP_State next_dash_state)
#define XBUF_SIZE
NE_State
@ NE_content_name
@ NE_content_type
@ NE_content_transfer_encoding
@ NE_none
@ NE_content_filename
static void process_value(struct MHD_PostProcessor *pp, const char *value_start, const char *value_end, const char *last_escape)
PP_State
@ PP_PerformCleanup
@ PP_Error
@ PP_Nested_PerformMarking
@ PP_ProcessKey
@ PP_Init
@ PP_PerformCheckMultipart
@ PP_Nested_Init
@ PP_ProcessValue
@ PP_Nested_ProcessEntryHeaders
@ PP_Nested_PerformCleanup
@ PP_NextBoundary
@ PP_ProcessEntryHeaders
@ PP_ProcessValueToBoundary
@ PP_Done
@ PP_Callback
@ PP_Nested_ProcessValueToBoundary
static void try_get_value(const char *buf, const char *key, char **destination)
static enum MHD_Result post_process_urlencoded(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
static void free_unmarked(struct MHD_PostProcessor *pp)
static int process_multipart_headers(struct MHD_PostProcessor *pp, size_t *ioffptr, enum PP_State next_state)