GNU libmicrohttpd 1.0.1
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 and Christian Grothoff
4 Copyright (C) 2014-2022 Karlson2k (Evgeny Grin)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
28#include "postprocessor.h"
29#include "internal.h"
30#include "mhd_str.h"
31#include "mhd_compat.h"
32#include "mhd_assert.h"
33
39#define XBUF_SIZE 512
40
41
44 size_t buffer_size,
46 void *iter_cls)
47{
48 struct MHD_PostProcessor *ret;
49 const char *encoding;
50 const char *boundary;
51 size_t blen;
52
53 if ( (buffer_size < 256) ||
54 (NULL == connection) ||
55 (NULL == iter))
56 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
57 encoding = NULL;
58 if (MHD_NO ==
64 &encoding,
65 NULL))
66 return NULL;
68 boundary = NULL;
73 {
78 return NULL;
79 boundary =
81 /* Q: should this be "strcasestr"? */
82 boundary = strstr (boundary, "boundary=");
83 if (NULL == boundary)
84 return NULL; /* failed to determine boundary */
85 boundary += MHD_STATICSTR_LEN_ ("boundary=");
86 blen = strlen (boundary);
87 if ( (blen < 2) ||
88 (blen * 2 + 2 > buffer_size) )
89 return NULL; /* (will be) out of memory or invalid boundary */
90 if ( (boundary[0] == '"') &&
91 (boundary[blen - 1] == '"') )
92 {
93 /* remove enclosing quotes */
94 ++boundary;
95 blen -= 2;
96 }
97 }
98 else
99 blen = 0;
100 buffer_size += 4; /* round up to get nice block sizes despite boundary search */
101
102 /* add +1 to ensure we ALWAYS have a zero-termination at the end */
103 if (NULL == (ret = MHD_calloc_ (1, sizeof (struct MHD_PostProcessor)
104 + buffer_size + 1)))
105 return NULL;
106 ret->connection = connection;
107 ret->ikvi = iter;
108 ret->cls = iter_cls;
109 ret->encoding = encoding;
111 ret->state = PP_Init;
112 ret->blen = blen;
113 ret->boundary = boundary;
114 ret->skip_rn = RN_Inactive;
115 return ret;
116}
117
118
136static void
138 const char *value_start,
139 const char *value_end,
140 const char *last_escape)
141{
142 char xbuf[XBUF_SIZE + 1];
143 size_t xoff;
144
145 mhd_assert (pp->xbuf_pos < sizeof (xbuf));
146 /* 'value_start' and 'value_end' must be either both non-NULL or both NULL */
147 mhd_assert ( (NULL == value_start) || (NULL != value_end) );
148 mhd_assert ( (NULL != value_start) || (NULL == value_end) );
149 mhd_assert ( (NULL == last_escape) || (NULL != value_start) );
150 /* move remaining input from previous round into processing buffer */
151 if (0 != pp->xbuf_pos)
152 memcpy (xbuf,
153 pp->xbuf,
154 pp->xbuf_pos);
155 xoff = pp->xbuf_pos;
156 pp->xbuf_pos = 0;
157 if ( (NULL != last_escape) &&
158 (((size_t) (value_end - last_escape)) < sizeof (pp->xbuf)) )
159 {
160 mhd_assert (value_end >= last_escape);
161 pp->xbuf_pos = (size_t) (value_end - last_escape);
162 memcpy (pp->xbuf,
163 last_escape,
164 (size_t) (value_end - last_escape));
165 value_end = last_escape;
166 }
167 while ( (value_start != value_end) ||
168 (pp->must_ikvi) ||
169 (xoff > 0) )
170 {
171 size_t delta = (size_t) (value_end - value_start);
172 bool cut = false;
173 size_t clen = 0;
174
175 mhd_assert (value_end >= value_start);
176
177 if (delta > XBUF_SIZE - xoff)
178 delta = XBUF_SIZE - xoff;
179 /* move (additional) input into processing buffer */
180 if (0 != delta)
181 {
182 memcpy (&xbuf[xoff],
183 value_start,
184 delta);
185 xoff += delta;
186 value_start += delta;
187 }
188 /* find if escape sequence is at the end of the processing buffer;
189 if so, exclude those from processing (reduce delta to point at
190 end of processed region) */
191 if ( (xoff > 0) &&
192 ('%' == xbuf[xoff - 1]) )
193 {
194 cut = (xoff != XBUF_SIZE);
195 xoff--;
196 if (cut)
197 {
198 /* move escape sequence into buffer for next function invocation */
199 pp->xbuf[0] = '%';
200 pp->xbuf_pos = 1;
201 }
202 else
203 {
204 /* just skip escape sequence for next loop iteration */
205 delta = xoff;
206 clen = 1;
207 }
208 }
209 else if ( (xoff > 1) &&
210 ('%' == xbuf[xoff - 2]) )
211 {
212 cut = (xoff != XBUF_SIZE);
213 xoff -= 2;
214 if (cut)
215 {
216 /* move escape sequence into buffer for next function invocation */
217 memcpy (pp->xbuf,
218 &xbuf[xoff],
219 2);
220 pp->xbuf_pos = 2;
221 }
222 else
223 {
224 /* just skip escape sequence for next loop iteration */
225 delta = xoff;
226 clen = 2;
227 }
228 }
229 mhd_assert (xoff < sizeof (xbuf));
230 /* unescape */
231 xbuf[xoff] = '\0'; /* 0-terminate in preparation */
232 if (0 != xoff)
233 {
235 xoff = MHD_http_unescape (xbuf);
236 }
237 /* finally: call application! */
238 if (pp->must_ikvi || (0 != xoff) )
239 {
240 pp->must_ikvi = false;
241 if (MHD_NO == pp->ikvi (pp->cls,
243 (const char *) &pp[1], /* key */
244 NULL,
245 NULL,
246 NULL,
247 xbuf,
248 pp->value_offset,
249 xoff))
250 {
251 pp->state = PP_Error;
252 return;
253 }
254 }
255 pp->value_offset += xoff;
256 if (cut)
257 break;
258 if (0 != clen)
259 {
260 xbuf[delta] = '%'; /* undo 0-termination */
261 memmove (xbuf,
262 &xbuf[delta],
263 clen);
264 }
265 xoff = clen;
266 }
267}
268
269
278static enum MHD_Result
280 const char *post_data,
281 size_t post_data_len)
282{
283 char *kbuf = (char *) &pp[1];
284 size_t poff;
285 const char *start_key = NULL;
286 const char *end_key = NULL;
287 const char *start_value = NULL;
288 const char *end_value = NULL;
289 const char *last_escape = NULL;
290
292
293 poff = 0;
294 while ( ( (poff < post_data_len) ||
295 (pp->state == PP_Callback) ) &&
296 (pp->state != PP_Error) )
297 {
298 switch (pp->state)
299 {
300 case PP_Error:
301 /* clearly impossible as per while loop invariant */
302 abort ();
303 break; /* Unreachable */
304 case PP_Init:
305 /* initial phase */
306 mhd_assert (NULL == start_key);
307 mhd_assert (NULL == end_key);
308 mhd_assert (NULL == start_value);
309 mhd_assert (NULL == end_value);
310 switch (post_data[poff])
311 {
312 case '=':
313 /* Case: (no key)'=' */
314 /* Empty key with value */
315 pp->state = PP_Error;
316 continue;
317 case '&':
318 /* Case: (no key)'&' */
319 /* Empty key without value */
320 poff++;
321 continue;
322 case '\n':
323 case '\r':
324 /* Case: (no key)'\n' or (no key)'\r' */
325 pp->state = PP_Done;
326 poff++;
327 break;
328 default:
329 /* normal character, key start, advance! */
330 pp->state = PP_ProcessKey;
331 start_key = &post_data[poff];
332 pp->must_ikvi = true;
333 poff++;
334 continue;
335 }
336 break; /* end PP_Init */
337 case PP_ProcessKey:
338 /* key phase */
339 mhd_assert (NULL == start_value);
340 mhd_assert (NULL == end_value);
341 mhd_assert (NULL != start_key || 0 == poff);
342 mhd_assert (0 != poff || NULL == start_key);
343 mhd_assert (NULL == end_key);
344 switch (post_data[poff])
345 {
346 case '=':
347 /* Case: 'key=' */
348 if (0 != poff)
349 end_key = &post_data[poff];
350 poff++;
352 break;
353 case '&':
354 /* Case: 'key&' */
355 if (0 != poff)
356 end_key = &post_data[poff];
357 poff++;
358 pp->state = PP_Callback;
359 break;
360 case '\n':
361 case '\r':
362 /* Case: 'key\n' or 'key\r' */
363 if (0 != poff)
364 end_key = &post_data[poff];
365 /* No advance here, 'PP_Done' will be selected by next 'PP_Init' phase */
366 pp->state = PP_Callback;
367 break;
368 default:
369 /* normal character, advance! */
370 if (0 == poff)
371 start_key = post_data;
372 poff++;
373 break;
374 }
375 mhd_assert (NULL == end_key || NULL != start_key);
376 break; /* end PP_ProcessKey */
377 case PP_ProcessValue:
378 if (NULL == start_value)
379 start_value = &post_data[poff];
380 switch (post_data[poff])
381 {
382 case '=':
383 /* case 'key==' */
384 pp->state = PP_Error;
385 continue;
386 case '&':
387 /* case 'value&' */
388 end_value = &post_data[poff];
389 poff++;
390 if (pp->must_ikvi ||
391 (start_value != end_value) )
392 {
393 pp->state = PP_Callback;
394 }
395 else
396 {
397 pp->buffer_pos = 0;
398 pp->value_offset = 0;
399 pp->state = PP_Init;
400 start_value = NULL;
401 end_value = NULL;
402 }
403 continue;
404 case '\n':
405 case '\r':
406 /* Case: 'value\n' or 'value\r' */
407 end_value = &post_data[poff];
408 if (pp->must_ikvi ||
409 (start_value != end_value) )
410 pp->state = PP_Callback; /* No poff advance here to set PP_Done in the next iteration */
411 else
412 {
413 poff++;
414 pp->state = PP_Done;
415 }
416 break;
417 case '%':
418 last_escape = &post_data[poff];
419 poff++;
420 break;
421 case '0':
422 case '1':
423 case '2':
424 case '3':
425 case '4':
426 case '5':
427 case '6':
428 case '7':
429 case '8':
430 case '9':
431 /* character, may be part of escaping */
432 poff++;
433 continue;
434 default:
435 /* normal character, no more escaping! */
436 last_escape = NULL;
437 poff++;
438 continue;
439 }
440 break; /* end PP_ProcessValue */
441 case PP_Done:
442 switch (post_data[poff])
443 {
444 case '\n':
445 case '\r':
446 poff++;
447 continue;
448 }
449 /* unexpected data at the end, fail! */
450 pp->state = PP_Error;
451 break;
452 case PP_Callback:
453 mhd_assert ((NULL != end_key) || (NULL == start_key));
454 if (1)
455 {
456 const size_t key_len = (size_t) (end_key - start_key);
457 mhd_assert (end_key >= start_key);
458 if (0 != key_len)
459 {
460 if ( (pp->buffer_pos + key_len >= pp->buffer_size) ||
461 (pp->buffer_pos + key_len < pp->buffer_pos) )
462 {
463 /* key too long, cannot parse! */
464 pp->state = PP_Error;
465 continue;
466 }
467 /* compute key, if we have not already */
468 memcpy (&kbuf[pp->buffer_pos],
469 start_key,
470 key_len);
471 pp->buffer_pos += key_len;
472 start_key = NULL;
473 end_key = NULL;
474 pp->must_unescape_key = true;
475 }
476 }
477#ifdef _DEBUG
478 else
479 mhd_assert (0 != pp->buffer_pos);
480#endif /* _DEBUG */
481 if (pp->must_unescape_key)
482 {
483 kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
484 MHD_unescape_plus (kbuf);
485 MHD_http_unescape (kbuf);
486 pp->must_unescape_key = false;
487 }
488 process_value (pp,
489 start_value,
490 end_value,
491 NULL);
492 if (PP_Error == pp->state)
493 continue;
494 pp->value_offset = 0;
495 start_value = NULL;
496 end_value = NULL;
497 pp->buffer_pos = 0;
498 pp->state = PP_Init;
499 break;
500 case PP_NextBoundary:
505 case PP_Nested_Init:
510 default:
511 MHD_PANIC (_ ("internal error.\n")); /* should never happen! */
512 }
513 mhd_assert ((end_key == NULL) || (start_key != NULL));
514 mhd_assert ((end_value == NULL) || (start_value != NULL));
515 }
516
518
519 if (PP_Error == pp->state)
520 {
521 /* State in error, returning failure */
522 return MHD_NO;
523 }
524
525 /* save remaining data for next iteration */
526 if (NULL != start_key)
527 {
528 size_t key_len;
529 mhd_assert ((PP_ProcessKey == pp->state) || (NULL != end_key));
530 if (NULL == end_key)
531 end_key = &post_data[poff];
532 mhd_assert (end_key >= start_key);
533 key_len = (size_t) (end_key - start_key);
534 mhd_assert (0 != key_len); /* it must be always non-zero here */
535 if (pp->buffer_pos + key_len >= pp->buffer_size)
536 {
537 pp->state = PP_Error;
538 return MHD_NO;
539 }
540 memcpy (&kbuf[pp->buffer_pos],
541 start_key,
542 key_len);
543 pp->buffer_pos += key_len;
544 pp->must_unescape_key = true;
545 start_key = NULL;
546 end_key = NULL;
547 }
548 if ( (NULL != start_value) &&
549 (PP_ProcessValue == pp->state) )
550 {
551 /* compute key, if we have not already */
552 if (pp->must_unescape_key)
553 {
554 kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
555 MHD_unescape_plus (kbuf);
556 MHD_http_unescape (kbuf);
557 pp->must_unescape_key = false;
558 }
559 if (NULL == end_value)
560 end_value = &post_data[poff];
561 if ( (NULL != last_escape) &&
562 (2 < (end_value - last_escape)) )
563 last_escape = NULL;
564 process_value (pp,
565 start_value,
566 end_value,
567 last_escape);
568 pp->must_ikvi = false;
569 }
570 if (PP_Error == pp->state)
571 {
572 /* State in error, returning failure */
573 return MHD_NO;
574 }
575 return MHD_YES;
576}
577
578
589static int
590try_match_header (const char *prefix,
591 size_t prefix_len,
592 char *line,
593 char **suffix)
594{
595 if (NULL != *suffix)
596 return MHD_NO;
597 while (0 != *line)
598 {
599 if (MHD_str_equal_caseless_n_ (prefix,
600 line,
601 prefix_len))
602 {
603 *suffix = strdup (&line[prefix_len]);
604 return MHD_YES;
605 }
606 ++line;
607 }
608 return MHD_NO;
609}
610
611
625static int
627 const char *boundary,
628 size_t blen,
629 size_t *ioffptr,
630 enum PP_State next_state,
631 enum PP_State next_dash_state)
632{
633 char *buf = (char *) &pp[1];
634 const char *dash;
635
636 if (pp->buffer_pos < 2 + blen)
637 {
638 if (pp->buffer_pos == pp->buffer_size)
639 pp->state = PP_Error; /* out of memory */
640 /* ++(*ioffptr); */
641 return MHD_NO; /* not enough data */
642 }
643 if ( (0 != memcmp ("--",
644 buf,
645 2)) ||
646 (0 != memcmp (&buf[2],
647 boundary,
648 blen)))
649 {
650 if (pp->state != PP_Init)
651 {
652 /* garbage not allowed */
653 pp->state = PP_Error;
654 }
655 else
656 {
657 /* skip over garbage (RFC 2046, 5.1.1) */
658 dash = memchr (buf,
659 '-',
660 pp->buffer_pos);
661 if (NULL == dash)
662 (*ioffptr) += pp->buffer_pos; /* skip entire buffer */
663 else if (dash == buf)
664 (*ioffptr)++; /* at least skip one byte */
665 else
666 (*ioffptr) += (size_t) (dash - buf); /* skip to first possible boundary */
667 }
668 return MHD_NO; /* expected boundary */
669 }
670 /* remove boundary from buffer */
671 (*ioffptr) += 2 + blen;
672 /* next: start with headers */
673 pp->skip_rn = RN_Dash;
674 pp->state = next_state;
675 pp->dash_state = next_dash_state;
676 return MHD_YES;
677}
678
679
686static void
687try_get_value (const char *buf,
688 const char *key,
689 char **destination)
690{
691 const char *spos;
692 const char *bpos;
693 const char *endv;
694 size_t klen;
695 size_t vlen;
696
697 if (NULL != *destination)
698 return;
699 bpos = buf;
700 klen = strlen (key);
701 while (NULL != (spos = strstr (bpos, key)))
702 {
703 if ( (spos[klen] != '=') ||
704 ( (spos != buf) &&
705 (spos[-1] != ' ') ) )
706 {
707 /* no match */
708 bpos = spos + 1;
709 continue;
710 }
711 if (spos[klen + 1] != '"')
712 return; /* not quoted */
713 if (NULL == (endv = strchr (&spos[klen + 2],
714 '\"')))
715 return; /* no end-quote */
716 vlen = (size_t) (endv - spos) - klen - 1;
717 *destination = malloc (vlen);
718 if (NULL == *destination)
719 return; /* out of memory */
720 (*destination)[vlen - 1] = '\0';
721 memcpy (*destination,
722 &spos[klen + 2],
723 vlen - 1);
724 return; /* success */
725 }
726}
727
728
744static int
746 size_t *ioffptr,
747 enum PP_State next_state)
748{
749 char *buf = (char *) &pp[1];
750 size_t newline;
751
752 newline = 0;
753 while ( (newline < pp->buffer_pos) &&
754 (buf[newline] != '\r') &&
755 (buf[newline] != '\n') )
756 newline++;
757 if (newline == pp->buffer_size)
758 {
759 pp->state = PP_Error;
760 return MHD_NO; /* out of memory */
761 }
762 if (newline == pp->buffer_pos)
763 return MHD_NO; /* will need more data */
764 if (0 == newline)
765 {
766 /* empty line - end of headers */
767 pp->skip_rn = RN_Full;
768 pp->state = next_state;
769 return MHD_YES;
770 }
771 /* got an actual header */
772 if (buf[newline] == '\r')
773 pp->skip_rn = RN_OptN;
774 buf[newline] = '\0';
775 if (MHD_str_equal_caseless_n_ ("Content-disposition: ",
776 buf,
777 MHD_STATICSTR_LEN_ ("Content-disposition: ")))
778 {
779 try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
780 "name",
781 &pp->content_name);
782 try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
783 "filename",
784 &pp->content_filename);
785 }
786 else
787 {
788 try_match_header ("Content-type: ",
789 MHD_STATICSTR_LEN_ ("Content-type: "),
790 buf,
791 &pp->content_type);
792 try_match_header ("Content-Transfer-Encoding: ",
793 MHD_STATICSTR_LEN_ ("Content-Transfer-Encoding: "),
794 buf,
796 }
797 (*ioffptr) += newline + 1;
798 return MHD_YES;
799}
800
801
818static int
820 size_t *ioffptr,
821 const char *boundary,
822 size_t blen,
823 enum PP_State next_state,
824 enum PP_State next_dash_state)
825{
826 char *buf = (char *) &pp[1];
827 size_t newline;
828 const char *r;
829
830 /* all data in buf until the boundary
831 (\r\n--+boundary) is part of the value */
832 newline = 0;
833 while (1)
834 {
835 while (newline + 4 < pp->buffer_pos)
836 {
837 r = memchr (&buf[newline],
838 '\r',
839 pp->buffer_pos - newline - 4);
840 if (NULL == r)
841 {
842 newline = pp->buffer_pos - 4;
843 break;
844 }
845 newline = (size_t) (r - buf);
846 if (0 == memcmp ("\r\n--",
847 &buf[newline],
848 4))
849 break;
850 newline++;
851 }
852 if (newline + blen + 4 <= pp->buffer_pos)
853 {
854 /* can check boundary */
855 if (0 != memcmp (&buf[newline + 4],
856 boundary,
857 blen))
858 {
859 /* no boundary, "\r\n--" is part of content, skip */
860 newline += 4;
861 continue;
862 }
863 else
864 {
865 /* boundary found, process until newline then
866 skip boundary and go back to init */
867 pp->skip_rn = RN_Dash;
868 pp->state = next_state;
869 pp->dash_state = next_dash_state;
870 (*ioffptr) += blen + 4; /* skip boundary as well */
871 buf[newline] = '\0';
872 break;
873 }
874 }
875 else
876 {
877 /* cannot check for boundary, process content that
878 we have and check again later; except, if we have
879 no content, abort (out of memory) */
880 if ( (0 == newline) &&
881 (pp->buffer_pos == pp->buffer_size) )
882 {
883 pp->state = PP_Error;
884 return MHD_NO;
885 }
886 break;
887 }
888 }
889 /* newline is either at beginning of boundary or
890 at least at the last character that we are sure
891 is not part of the boundary */
892 if ( ( (pp->must_ikvi) ||
893 (0 != newline) ) &&
894 (MHD_NO == pp->ikvi (pp->cls,
896 pp->content_name,
898 pp->content_type,
900 buf,
901 pp->value_offset,
902 newline)) )
903 {
904 pp->state = PP_Error;
905 return MHD_NO;
906 }
907 pp->must_ikvi = false;
908 pp->value_offset += newline;
909 (*ioffptr) += newline;
910 return MHD_YES;
911}
912
913
918static void
920{
921 if ( (NULL != pp->content_name) &&
922 (0 == (pp->have & NE_content_name)) )
923 {
924 free (pp->content_name);
925 pp->content_name = NULL;
926 }
927 if ( (NULL != pp->content_type) &&
928 (0 == (pp->have & NE_content_type)) )
929 {
930 free (pp->content_type);
931 pp->content_type = NULL;
932 }
933 if ( (NULL != pp->content_filename) &&
934 (0 == (pp->have & NE_content_filename)) )
935 {
936 free (pp->content_filename);
938 }
939 if ( (NULL != pp->content_transfer_encoding) &&
940 (0 == (pp->have & NE_content_transfer_encoding)) )
941 {
942 free (pp->content_transfer_encoding);
944 }
945}
946
947
956static enum MHD_Result
958 const char *post_data,
959 size_t post_data_len)
960{
961 char *buf;
962 size_t max;
963 size_t ioff;
964 size_t poff;
965 int state_changed;
966
967 buf = (char *) &pp[1];
968 ioff = 0;
969 poff = 0;
970 state_changed = 1;
971 while ( (poff < post_data_len) ||
972 ( (pp->buffer_pos > 0) &&
973 (0 != state_changed) ) )
974 {
975 /* first, move as much input data
976 as possible to our internal buffer */
977 max = pp->buffer_size - pp->buffer_pos;
978 if (max > post_data_len - poff)
979 max = post_data_len - poff;
980 memcpy (&buf[pp->buffer_pos],
981 &post_data[poff],
982 max);
983 poff += max;
984 pp->buffer_pos += max;
985 if ( (0 == max) &&
986 (0 == state_changed) &&
987 (poff < post_data_len) )
988 {
989 pp->state = PP_Error;
990 return MHD_NO; /* out of memory */
991 }
992 state_changed = 0;
993
994 /* first state machine for '\r'-'\n' and '--' handling */
995 switch (pp->skip_rn)
996 {
997 case RN_Inactive:
998 break;
999 case RN_OptN:
1000 if (buf[0] == '\n')
1001 {
1002 ioff++;
1003 pp->skip_rn = RN_Inactive;
1004 goto AGAIN;
1005 }
1006 /* fall-through! */
1007 case RN_Dash:
1008 if (buf[0] == '-')
1009 {
1010 ioff++;
1011 pp->skip_rn = RN_Dash2;
1012 goto AGAIN;
1013 }
1014 pp->skip_rn = RN_Full;
1015 /* fall-through! */
1016 case RN_Full:
1017 if (buf[0] == '\r')
1018 {
1019 if ( (pp->buffer_pos > 1) &&
1020 ('\n' == buf[1]) )
1021 {
1022 pp->skip_rn = RN_Inactive;
1023 ioff += 2;
1024 }
1025 else
1026 {
1027 pp->skip_rn = RN_OptN;
1028 ioff++;
1029 }
1030 goto AGAIN;
1031 }
1032 if (buf[0] == '\n')
1033 {
1034 ioff++;
1035 pp->skip_rn = RN_Inactive;
1036 goto AGAIN;
1037 }
1038 pp->skip_rn = RN_Inactive;
1039 pp->state = PP_Error;
1040 return MHD_NO; /* no '\r\n' */
1041 case RN_Dash2:
1042 if (buf[0] == '-')
1043 {
1044 ioff++;
1045 pp->skip_rn = RN_Full;
1046 pp->state = pp->dash_state;
1047 goto AGAIN;
1048 }
1049 pp->state = PP_Error;
1050 break;
1051 }
1052
1053 /* main state engine */
1054 switch (pp->state)
1055 {
1056 case PP_Error:
1057 return MHD_NO;
1058 case PP_Done:
1059 /* did not expect to receive more data */
1060 pp->state = PP_Error;
1061 return MHD_NO;
1062 case PP_Init:
1074 (void) find_boundary (pp,
1075 pp->boundary,
1076 pp->blen,
1077 &ioff,
1079 PP_Done);
1080 break;
1081 case PP_NextBoundary:
1082 if (MHD_NO == find_boundary (pp,
1083 pp->boundary,
1084 pp->blen,
1085 &ioff,
1087 PP_Done))
1088 {
1089 if (pp->state == PP_Error)
1090 return MHD_NO;
1091 goto END;
1092 }
1093 break;
1095 pp->must_ikvi = true;
1096 if (MHD_NO ==
1098 &ioff,
1100 {
1101 if (pp->state == PP_Error)
1102 return MHD_NO;
1103 else
1104 goto END;
1105 }
1106 state_changed = 1;
1107 break;
1109 if ( (NULL != pp->content_type) &&
1111 "multipart/mixed",
1112 MHD_STATICSTR_LEN_ ("multipart/mixed"))))
1113 {
1114 pp->nested_boundary = strstr (pp->content_type,
1115 "boundary=");
1116 if (NULL == pp->nested_boundary)
1117 {
1118 pp->state = PP_Error;
1119 return MHD_NO;
1120 }
1121 pp->nested_boundary =
1122 strdup (&pp->nested_boundary[MHD_STATICSTR_LEN_ ("boundary=")]);
1123 if (NULL == pp->nested_boundary)
1124 {
1125 /* out of memory */
1126 pp->state = PP_Error;
1127 return MHD_NO;
1128 }
1129 /* free old content type, we will need that field
1130 for the content type of the nested elements */
1131 free (pp->content_type);
1132 pp->content_type = NULL;
1133 pp->nlen = strlen (pp->nested_boundary);
1134 pp->state = PP_Nested_Init;
1135 state_changed = 1;
1136 break;
1137 }
1139 pp->value_offset = 0;
1140 state_changed = 1;
1141 break;
1144 &ioff,
1145 pp->boundary,
1146 pp->blen,
1148 PP_Done))
1149 {
1150 if (pp->state == PP_Error)
1151 return MHD_NO;
1152 break;
1153 }
1154 break;
1155 case PP_PerformCleanup:
1156 /* clean up state of one multipart form-data element! */
1157 pp->have = NE_none;
1158 free_unmarked (pp);
1159 if (NULL != pp->nested_boundary)
1160 {
1161 free (pp->nested_boundary);
1162 pp->nested_boundary = NULL;
1163 }
1165 state_changed = 1;
1166 break;
1167 case PP_Nested_Init:
1168 if (NULL == pp->nested_boundary)
1169 {
1170 pp->state = PP_Error;
1171 return MHD_NO;
1172 }
1173 if (MHD_NO == find_boundary (pp,
1174 pp->nested_boundary,
1175 pp->nlen,
1176 &ioff,
1178 PP_NextBoundary /* or PP_Error? */))
1179 {
1180 if (pp->state == PP_Error)
1181 return MHD_NO;
1182 goto END;
1183 }
1184 break;
1186 /* remember what headers were given
1187 globally */
1188 pp->have = NE_none;
1189 if (NULL != pp->content_name)
1190 pp->have |= NE_content_name;
1191 if (NULL != pp->content_type)
1192 pp->have |= NE_content_type;
1193 if (NULL != pp->content_filename)
1198 state_changed = 1;
1199 break;
1201 pp->value_offset = 0;
1202 if (MHD_NO ==
1204 &ioff,
1206 {
1207 if (pp->state == PP_Error)
1208 return MHD_NO;
1209 else
1210 goto END;
1211 }
1212 state_changed = 1;
1213 break;
1216 &ioff,
1217 pp->nested_boundary,
1218 pp->nlen,
1221 {
1222 if (pp->state == PP_Error)
1223 return MHD_NO;
1224 break;
1225 }
1226 break;
1228 free_unmarked (pp);
1230 state_changed = 1;
1231 break;
1232 case PP_ProcessKey:
1233 case PP_ProcessValue:
1234 case PP_Callback:
1235 default:
1236 MHD_PANIC (_ ("internal error.\n")); /* should never happen! */
1237 }
1238AGAIN:
1239 if (ioff > 0)
1240 {
1241 memmove (buf,
1242 &buf[ioff],
1243 pp->buffer_pos - ioff);
1244 pp->buffer_pos -= ioff;
1245 ioff = 0;
1246 state_changed = 1;
1247 }
1248 }
1249END:
1250 if (0 != ioff)
1251 {
1252 memmove (buf,
1253 &buf[ioff],
1254 pp->buffer_pos - ioff);
1255 pp->buffer_pos -= ioff;
1256 }
1257 if (poff < post_data_len)
1258 {
1259 pp->state = PP_Error;
1260 return MHD_NO; /* serious error */
1261 }
1262 return MHD_YES;
1263}
1264
1265
1268 const char *post_data,
1269 size_t post_data_len)
1270{
1271 if (0 == post_data_len)
1272 return MHD_YES;
1273 if (NULL == pp)
1274 return MHD_NO;
1276 pp->encoding,
1279 return post_process_urlencoded (pp,
1280 post_data,
1281 post_data_len);
1283 pp->encoding,
1286 return post_process_multipart (pp,
1287 post_data,
1288 post_data_len);
1289 /* this should never be reached */
1290 return MHD_NO;
1291}
1292
1293
1296{
1297 enum MHD_Result ret;
1298
1299 if (NULL == pp)
1300 return MHD_YES;
1301 if (PP_ProcessValue == pp->state)
1302 {
1303 /* key without terminated value left at the end of the
1304 buffer; fake receiving a termination character to
1305 ensure it is also processed */
1307 "\n",
1308 1);
1309 }
1310 /* These internal strings need cleaning up since
1311 the post-processing may have been interrupted
1312 at any stage */
1313 if ( (pp->xbuf_pos > 0) ||
1314 ( (pp->state != PP_Done) &&
1315 (pp->state != PP_Init) ) )
1316 ret = MHD_NO;
1317 else
1318 ret = MHD_YES;
1319 pp->have = NE_none;
1320 free_unmarked (pp);
1321 if (NULL != pp->nested_boundary)
1322 free (pp->nested_boundary);
1323 free (pp);
1324 return ret;
1325}
1326
1327
1328/* end of postprocessor.c */
#define MHD_HTTP_HEADER_CONTENT_TYPE
Definition microhttpd.h:596
#define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
#define MHD_HTTP_POST_ENCODING_FORM_URLENCODED
_MHD_EXTERN enum MHD_Result MHD_destroy_post_processor(struct MHD_PostProcessor *pp)
_MHD_EXTERN enum MHD_Result MHD_post_process(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
_MHD_EXTERN 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)
void MHD_unescape_plus(char *arg)
Definition internal.c:123
#define MHD_PANIC(msg)
Definition internal.h:69
#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
#define _(String)
Definition mhd_options.h:42
#define _MHD_EXTERN
Definition mhd_options.h:53
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)
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
static void process_value(struct MHD_PostProcessor *pp, const char *value_start, const char *value_end, const char *last_escape)
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)
Declarations for parsing POST data.
@ RN_Dash
@ RN_Inactive
@ RN_Full
@ RN_OptN
@ RN_Dash2
@ NE_content_name
@ NE_content_type
@ NE_content_transfer_encoding
@ NE_none
@ NE_content_filename
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
enum PP_State dash_state
MHD_PostDataIterator ikvi
struct MHD_Connection * connection
const char * boundary
char * content_transfer_encoding
enum NE_State have
enum RN_State skip_rn
enum PP_State state
const char * encoding