GNU libmicrohttpd 1.0.9
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 {
79 {
80 return NULL;
81 }
82 boundary =
84 /* Q: should this be "strcasestr"? */
85 boundary = strstr (boundary, "boundary=");
86 if (NULL == boundary)
87 return NULL; /* failed to determine boundary */
88 boundary += MHD_STATICSTR_LEN_ ("boundary=");
89 blen = strlen (boundary);
90 if ( (blen < 2) ||
91 (blen * 2 + 2 > buffer_size) )
92 return NULL; /* (will be) out of memory or invalid boundary */
93 if ( (boundary[0] == '"') &&
94 (boundary[blen - 1] == '"') )
95 {
96 /* remove enclosing quotes */
97 ++boundary;
98 blen -= 2;
99 }
100 }
101 else
102 blen = 0;
103 buffer_size += 4; /* round up to get nice block sizes despite boundary search */
104
105 /* add +1 to ensure we ALWAYS have a zero-termination at the end */
106 if (NULL == (ret = MHD_calloc_ (1, sizeof (struct MHD_PostProcessor)
107 + buffer_size + 1)))
108 return NULL;
109 ret->connection = connection;
110 ret->ikvi = iter;
111 ret->cls = iter_cls;
112 ret->encoding = encoding;
114 ret->state = PP_Init;
115 ret->blen = blen;
116 ret->boundary = boundary;
117 ret->skip_rn = RN_Inactive;
118 return ret;
119}
120
121
139static void
141 const char *value_start,
142 const char *value_end,
143 const char *last_escape)
144{
145 char xbuf[XBUF_SIZE + 1];
146 size_t xoff;
147
148 mhd_assert (pp->xbuf_pos < sizeof (xbuf));
149 /* 'value_start' and 'value_end' must be either both non-NULL or both NULL */
150 mhd_assert ( (NULL == value_start) || (NULL != value_end) );
151 mhd_assert ( (NULL != value_start) || (NULL == value_end) );
152 mhd_assert ( (NULL == last_escape) || (NULL != value_start) );
153 /* move remaining input from previous round into processing buffer */
154 if (0 != pp->xbuf_pos)
155 memcpy (xbuf,
156 pp->xbuf,
157 pp->xbuf_pos);
158 xoff = pp->xbuf_pos;
159 pp->xbuf_pos = 0;
160 if ( (NULL != last_escape) &&
161 (((size_t) (value_end - last_escape)) < sizeof (pp->xbuf)) )
162 {
163 mhd_assert (value_end >= last_escape);
164 pp->xbuf_pos = (size_t) (value_end - last_escape);
165 memcpy (pp->xbuf,
166 last_escape,
167 (size_t) (value_end - last_escape));
168 value_end = last_escape;
169 }
170 while ( (value_start != value_end) ||
171 (pp->must_ikvi) ||
172 (xoff > 0) )
173 {
174 size_t delta = (size_t) (value_end - value_start);
175 bool cut = false;
176 size_t clen = 0;
177
178 mhd_assert (value_end >= value_start);
179
180 if (delta > XBUF_SIZE - xoff)
181 delta = XBUF_SIZE - xoff;
182 /* move (additional) input into processing buffer */
183 if (0 != delta)
184 {
185 memcpy (&xbuf[xoff],
186 value_start,
187 delta);
188 xoff += delta;
189 value_start += delta;
190 }
191 /* find if escape sequence is at the end of the processing buffer;
192 if so, exclude those from processing (reduce delta to point at
193 end of processed region) */
194 if ( (xoff > 0) &&
195 ('%' == xbuf[xoff - 1]) )
196 {
197 cut = (xoff != XBUF_SIZE);
198 xoff--;
199 if (cut)
200 {
201 /* move escape sequence into buffer for next function invocation */
202 pp->xbuf[0] = '%';
203 pp->xbuf_pos = 1;
204 }
205 else
206 {
207 /* just skip escape sequence for next loop iteration */
208 delta = xoff;
209 clen = 1;
210 }
211 }
212 else if ( (xoff > 1) &&
213 ('%' == xbuf[xoff - 2]) )
214 {
215 cut = (xoff != XBUF_SIZE);
216 xoff -= 2;
217 if (cut)
218 {
219 /* move escape sequence into buffer for next function invocation */
220 memcpy (pp->xbuf,
221 &xbuf[xoff],
222 2);
223 pp->xbuf_pos = 2;
224 }
225 else
226 {
227 /* just skip escape sequence for next loop iteration */
228 delta = xoff;
229 clen = 2;
230 }
231 }
232 mhd_assert (xoff < sizeof (xbuf));
233 /* unescape */
234 xbuf[xoff] = '\0'; /* 0-terminate in preparation */
235 if (0 != xoff)
236 {
238 xoff = MHD_http_unescape (xbuf);
239 }
240 /* finally: call application! */
241 if (pp->must_ikvi || (0 != xoff) )
242 {
243 pp->must_ikvi = false;
244 if (MHD_NO == pp->ikvi (pp->cls,
246 (const char *) &pp[1], /* key */
247 NULL,
248 NULL,
249 NULL,
250 xbuf,
251 pp->value_offset,
252 xoff))
253 {
254 pp->state = PP_Error;
255 return;
256 }
257 }
258 pp->value_offset += xoff;
259 if (cut)
260 break;
261 if (0 != clen)
262 {
263 xbuf[delta] = '%'; /* undo 0-termination */
264 memmove (xbuf,
265 &xbuf[delta],
266 clen);
267 }
268 xoff = clen;
269 }
270}
271
272
281static enum MHD_Result
283 const char *post_data,
284 size_t post_data_len)
285{
286 char *kbuf = (char *) &pp[1];
287 size_t poff;
288 const char *start_key = NULL;
289 const char *end_key = NULL;
290 const char *start_value = NULL;
291 const char *end_value = NULL;
292 const char *last_escape = NULL;
293
295
296 poff = 0;
297 while ( ( (poff < post_data_len) ||
298 (pp->state == PP_Callback) ) &&
299 (pp->state != PP_Error) )
300 {
301 switch (pp->state)
302 {
303 case PP_Error:
304 /* clearly impossible as per while loop invariant */
305 abort ();
306 break; /* Unreachable */
307 case PP_Init:
308 /* initial phase */
309 mhd_assert (NULL == start_key);
310 mhd_assert (NULL == end_key);
311 mhd_assert (NULL == start_value);
312 mhd_assert (NULL == end_value);
313 switch (post_data[poff])
314 {
315 case '=':
316 /* Case: (no key)'=' */
317 /* Empty key with value */
318 pp->state = PP_Error;
319 continue;
320 case '&':
321 /* Case: (no key)'&' */
322 /* Empty key without value */
323 poff++;
324 continue;
325 case '\n':
326 case '\r':
327 /* Case: (no key)'\n' or (no key)'\r' */
328 pp->state = PP_Done;
329 poff++;
330 break;
331 default:
332 /* normal character, key start, advance! */
333 pp->state = PP_ProcessKey;
334 start_key = &post_data[poff];
335 pp->must_ikvi = true;
336 poff++;
337 continue;
338 }
339 break; /* end PP_Init */
340 case PP_ProcessKey:
341 /* key phase */
342 mhd_assert (NULL == start_value);
343 mhd_assert (NULL == end_value);
344 mhd_assert (NULL != start_key || 0 == poff);
345 mhd_assert (0 != poff || NULL == start_key);
346 mhd_assert (NULL == end_key);
347 switch (post_data[poff])
348 {
349 case '=':
350 /* Case: 'key=' */
351 if (0 != poff)
352 end_key = &post_data[poff];
353 poff++;
355 break;
356 case '&':
357 /* Case: 'key&' */
358 if (0 != poff)
359 end_key = &post_data[poff];
360 poff++;
361 pp->state = PP_Callback;
362 break;
363 case '\n':
364 case '\r':
365 /* Case: 'key\n' or 'key\r' */
366 if (0 != poff)
367 end_key = &post_data[poff];
368 /* No advance here, 'PP_Done' will be selected by next 'PP_Init' phase */
369 pp->state = PP_Callback;
370 break;
371 default:
372 /* normal character, advance! */
373 if (0 == poff)
374 start_key = post_data;
375 poff++;
376 break;
377 }
378 mhd_assert (NULL == end_key || NULL != start_key);
379 break; /* end PP_ProcessKey */
380 case PP_ProcessValue:
381 if (NULL == start_value)
382 start_value = &post_data[poff];
383 switch (post_data[poff])
384 {
385 case '=':
386 /* case 'key==' */
387 pp->state = PP_Error;
388 continue;
389 case '&':
390 /* case 'value&' */
391 end_value = &post_data[poff];
392 poff++;
393 if (pp->must_ikvi ||
394 (start_value != end_value) )
395 {
396 pp->state = PP_Callback;
397 }
398 else
399 {
400 pp->buffer_pos = 0;
401 pp->value_offset = 0;
402 pp->state = PP_Init;
403 start_value = NULL;
404 end_value = NULL;
405 }
406 continue;
407 case '\n':
408 case '\r':
409 /* Case: 'value\n' or 'value\r' */
410 end_value = &post_data[poff];
411 if (pp->must_ikvi ||
412 (start_value != end_value) )
413 pp->state = PP_Callback; /* No poff advance here to set PP_Done in the next iteration */
414 else
415 {
416 poff++;
417 pp->state = PP_Done;
418 }
419 break;
420 case '%':
421 last_escape = &post_data[poff];
422 poff++;
423 break;
424 case '0':
425 case '1':
426 case '2':
427 case '3':
428 case '4':
429 case '5':
430 case '6':
431 case '7':
432 case '8':
433 case '9':
434 /* character, may be part of escaping */
435 poff++;
436 continue;
437 default:
438 /* normal character, no more escaping! */
439 last_escape = NULL;
440 poff++;
441 continue;
442 }
443 break; /* end PP_ProcessValue */
444 case PP_Done:
445 switch (post_data[poff])
446 {
447 case '\n':
448 case '\r':
449 poff++;
450 continue;
451 }
452 /* unexpected data at the end, fail! */
453 pp->state = PP_Error;
454 break;
455 case PP_Callback:
456 mhd_assert ((NULL != end_key) || (NULL == start_key));
457 if (1)
458 {
459 const size_t key_len = (size_t) (end_key - start_key);
460 mhd_assert (end_key >= start_key);
461 if (0 != key_len)
462 {
463 if ( (pp->buffer_pos + key_len >= pp->buffer_size) ||
464 (pp->buffer_pos + key_len < pp->buffer_pos) )
465 {
466 /* key too long, cannot parse! */
467 pp->state = PP_Error;
468 continue;
469 }
470 /* compute key, if we have not already */
471 memcpy (&kbuf[pp->buffer_pos],
472 start_key,
473 key_len);
474 pp->buffer_pos += key_len;
475 start_key = NULL;
476 end_key = NULL;
477 pp->must_unescape_key = true;
478 }
479 }
480#ifdef _DEBUG
481 else
482 mhd_assert (0 != pp->buffer_pos);
483#endif /* _DEBUG */
484 if (pp->must_unescape_key)
485 {
486 kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
487 MHD_unescape_plus (kbuf);
488 MHD_http_unescape (kbuf);
489 pp->must_unescape_key = false;
490 }
491 process_value (pp,
492 start_value,
493 end_value,
494 NULL);
495 if (PP_Error == pp->state)
496 continue;
497 pp->value_offset = 0;
498 start_value = NULL;
499 end_value = NULL;
500 pp->buffer_pos = 0;
501 pp->state = PP_Init;
502 break;
503 case PP_NextBoundary:
508 case PP_Nested_Init:
513 default:
514 MHD_PANIC (_ ("internal error.\n")); /* should never happen! */
515 }
516 mhd_assert ((end_key == NULL) || (start_key != NULL));
517 mhd_assert ((end_value == NULL) || (start_value != NULL));
518 }
519
521
522 if (PP_Error == pp->state)
523 {
524 /* State in error, returning failure */
525 return MHD_NO;
526 }
527
528 /* save remaining data for next iteration */
529 if (NULL != start_key)
530 {
531 size_t key_len;
532 mhd_assert ((PP_ProcessKey == pp->state) || (NULL != end_key));
533 if (NULL == end_key)
534 end_key = &post_data[poff];
535 mhd_assert (end_key >= start_key);
536 key_len = (size_t) (end_key - start_key);
537 mhd_assert (0 != key_len); /* it must be always non-zero here */
538 if ( (pp->buffer_pos + key_len >= pp->buffer_size) ||
539 (pp->buffer_pos + key_len < pp->buffer_pos) )
540 {
541 pp->state = PP_Error;
542 return MHD_NO;
543 }
544 memcpy (&kbuf[pp->buffer_pos],
545 start_key,
546 key_len);
547 pp->buffer_pos += key_len;
548 pp->must_unescape_key = true;
549 start_key = NULL;
550 end_key = NULL;
551 }
552 if ( (NULL != start_value) &&
553 (PP_ProcessValue == pp->state) )
554 {
555 /* compute key, if we have not already */
556 if (pp->must_unescape_key)
557 {
558 kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
559 MHD_unescape_plus (kbuf);
560 MHD_http_unescape (kbuf);
561 pp->must_unescape_key = false;
562 }
563 if (NULL == end_value)
564 end_value = &post_data[poff];
565 if ( (NULL != last_escape) &&
566 (2 < (end_value - last_escape)) )
567 last_escape = NULL;
568 process_value (pp,
569 start_value,
570 end_value,
571 last_escape);
572 pp->must_ikvi = false;
573 }
574 if (PP_Error == pp->state)
575 {
576 /* State in error, returning failure */
577 return MHD_NO;
578 }
579 return MHD_YES;
580}
581
582
593static int
594try_match_header (const char *prefix,
595 size_t prefix_len,
596 char *line,
597 char **suffix)
598{
599 if (NULL != *suffix)
600 return MHD_NO;
601 if (MHD_str_equal_caseless_n_ (prefix,
602 line,
603 prefix_len))
604 {
605 *suffix = strdup (&line[prefix_len]);
606 return MHD_YES;
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 const char *bnd;
1115 char *nested;
1116
1117 bnd = strstr (pp->content_type,
1118 "boundary=");
1119 if (NULL == bnd)
1120 {
1121 pp->state = PP_Error;
1122 return MHD_NO;
1123 }
1124 /* Note: 'bnd' points into 'pp->content_type', which is released
1125 below, so the copy must be taken first. It must also be
1126 taken into a local: assigning the 'strstr()' result to
1127 'pp->nested_boundary' first would overwrite -- and leak -- a
1128 boundary already owned by this post processor. That happens
1129 whenever this state is reached twice without passing through
1130 PP_PerformCleanup, i.e. for every additional nested
1131 "multipart/mixed" part, and the leaked block is as long as
1132 the boundary the client chose. */
1133 nested = strdup (&bnd[MHD_STATICSTR_LEN_ ("boundary=")]);
1134 if (NULL == nested)
1135 {
1136 /* out of memory */
1137 pp->state = PP_Error;
1138 return MHD_NO;
1139 }
1140 if (NULL != pp->nested_boundary)
1141 free (pp->nested_boundary);
1142 pp->nested_boundary = nested;
1143 /* free old content type, we will need that field
1144 for the content type of the nested elements */
1145 free (pp->content_type);
1146 pp->content_type = NULL;
1147 pp->nlen = strlen (pp->nested_boundary);
1148 pp->state = PP_Nested_Init;
1149 state_changed = 1;
1150 break;
1151 }
1153 pp->value_offset = 0;
1154 state_changed = 1;
1155 break;
1158 &ioff,
1159 pp->boundary,
1160 pp->blen,
1162 PP_Done))
1163 {
1164 if (pp->state == PP_Error)
1165 return MHD_NO;
1166 break;
1167 }
1168 break;
1169 case PP_PerformCleanup:
1170 /* clean up state of one multipart form-data element! */
1171 pp->have = NE_none;
1172 free_unmarked (pp);
1173 if (NULL != pp->nested_boundary)
1174 {
1175 free (pp->nested_boundary);
1176 pp->nested_boundary = NULL;
1177 }
1179 state_changed = 1;
1180 break;
1181 case PP_Nested_Init:
1182 if (NULL == pp->nested_boundary)
1183 {
1184 pp->state = PP_Error;
1185 return MHD_NO;
1186 }
1187 if (MHD_NO == find_boundary (pp,
1188 pp->nested_boundary,
1189 pp->nlen,
1190 &ioff,
1192 PP_NextBoundary /* or PP_Error? */))
1193 {
1194 if (pp->state == PP_Error)
1195 return MHD_NO;
1196 goto END;
1197 }
1198 break;
1200 /* remember what headers were given
1201 globally */
1202 pp->have = NE_none;
1203 if (NULL != pp->content_name)
1204 pp->have |= NE_content_name;
1205 if (NULL != pp->content_type)
1206 pp->have |= NE_content_type;
1207 if (NULL != pp->content_filename)
1212 state_changed = 1;
1213 break;
1215 pp->value_offset = 0;
1216 if (MHD_NO ==
1218 &ioff,
1220 {
1221 if (pp->state == PP_Error)
1222 return MHD_NO;
1223 else
1224 goto END;
1225 }
1226 state_changed = 1;
1227 break;
1230 &ioff,
1231 pp->nested_boundary,
1232 pp->nlen,
1235 {
1236 if (pp->state == PP_Error)
1237 return MHD_NO;
1238 break;
1239 }
1240 break;
1242 free_unmarked (pp);
1244 state_changed = 1;
1245 break;
1246 case PP_ProcessKey:
1247 case PP_ProcessValue:
1248 case PP_Callback:
1249 default:
1250 MHD_PANIC (_ ("internal error.\n")); /* should never happen! */
1251 }
1252AGAIN:
1253 if (ioff > 0)
1254 {
1255 memmove (buf,
1256 &buf[ioff],
1257 pp->buffer_pos - ioff);
1258 pp->buffer_pos -= ioff;
1259 ioff = 0;
1260 state_changed = 1;
1261 }
1262 }
1263END:
1264 if (0 != ioff)
1265 {
1266 memmove (buf,
1267 &buf[ioff],
1268 pp->buffer_pos - ioff);
1269 pp->buffer_pos -= ioff;
1270 }
1271 if (poff < post_data_len)
1272 {
1273 pp->state = PP_Error;
1274 return MHD_NO; /* serious error */
1275 }
1276 return MHD_YES;
1277}
1278
1279
1282 const char *post_data,
1283 size_t post_data_len)
1284{
1285 if (0 == post_data_len)
1286 return MHD_YES;
1287 if (NULL == pp)
1288 return MHD_NO;
1290 pp->encoding,
1293 return post_process_urlencoded (pp,
1294 post_data,
1295 post_data_len);
1297 pp->encoding,
1300 return post_process_multipart (pp,
1301 post_data,
1302 post_data_len);
1303 /* this should never be reached */
1304 return MHD_NO;
1305}
1306
1307
1310{
1311 enum MHD_Result ret;
1312
1313 if (NULL == pp)
1314 return MHD_YES;
1315 if (PP_ProcessValue == pp->state)
1316 {
1317 /* key without terminated value left at the end of the
1318 buffer; fake receiving a termination character to
1319 ensure it is also processed */
1321 "\n",
1322 1);
1323 }
1324 /* These internal strings need cleaning up since
1325 the post-processing may have been interrupted
1326 at any stage */
1327 if ( (pp->xbuf_pos > 0) ||
1328 ( (pp->state != PP_Done) &&
1329 (pp->state != PP_Init) ) )
1330 ret = MHD_NO;
1331 else
1332 ret = MHD_YES;
1333 pp->have = NE_none;
1334 free_unmarked (pp);
1335 if (NULL != pp->nested_boundary)
1336 free (pp->nested_boundary);
1337 free (pp);
1338 return ret;
1339}
1340
1341
1342/* end of postprocessor.c */
#define MHD_HTTP_HEADER_CONTENT_TYPE
Definition microhttpd.h:602
#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:129
MHD internal shared structures.
macros for mhd_assert()
#define mhd_assert(ignore)
Definition mhd_assert.h:45
void * MHD_calloc_(size_t nelem, size_t elsize)
Definition mhd_compat.c:98
Header for platform missing functions.
#define MHD_PANIC(msg)
Definition mhd_itc.h:45
#define NULL
#define _(String)
Definition mhd_options.h:42
#define _MHD_EXTERN
Definition mhd_options.h:53
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:718
Header for string manipulating helpers.
#define MHD_STATICSTR_LEN_(macro)
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
_MHD_EXTERN size_t MHD_http_unescape(char *val)
Definition internal.c:148
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