GNU libmicrohttpd 1.0.6
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
27
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 (pp->buffer_pos + key_len < pp->buffer_pos) )
537 {
538 pp->state = PP_Error;
539 return MHD_NO;
540 }
541 memcpy (&kbuf[pp->buffer_pos],
542 start_key,
543 key_len);
544 pp->buffer_pos += key_len;
545 pp->must_unescape_key = true;
546 start_key = NULL;
547 end_key = NULL;
548 }
549 if ( (NULL != start_value) &&
550 (PP_ProcessValue == pp->state) )
551 {
552 /* compute key, if we have not already */
553 if (pp->must_unescape_key)
554 {
555 kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
556 MHD_unescape_plus (kbuf);
557 MHD_http_unescape (kbuf);
558 pp->must_unescape_key = false;
559 }
560 if (NULL == end_value)
561 end_value = &post_data[poff];
562 if ( (NULL != last_escape) &&
563 (2 < (end_value - last_escape)) )
564 last_escape = NULL;
565 process_value (pp,
566 start_value,
567 end_value,
568 last_escape);
569 pp->must_ikvi = false;
570 }
571 if (PP_Error == pp->state)
572 {
573 /* State in error, returning failure */
574 return MHD_NO;
575 }
576 return MHD_YES;
577}
578
579
590static int
591try_match_header (const char *prefix,
592 size_t prefix_len,
593 char *line,
594 char **suffix)
595{
596 if (NULL != *suffix)
597 return MHD_NO;
598 if (MHD_str_equal_caseless_n_ (prefix,
599 line,
600 prefix_len))
601 {
602 *suffix = strdup (&line[prefix_len]);
603 return MHD_YES;
604 }
605 return MHD_NO;
606}
607
608
622static int
624 const char *boundary,
625 size_t blen,
626 size_t *ioffptr,
627 enum PP_State next_state,
628 enum PP_State next_dash_state)
629{
630 char *buf = (char *) &pp[1];
631 const char *dash;
632
633 if (pp->buffer_pos < 2 + blen)
634 {
635 if (pp->buffer_pos == pp->buffer_size)
636 pp->state = PP_Error; /* out of memory */
637 /* ++(*ioffptr); */
638 return MHD_NO; /* not enough data */
639 }
640 if ( (0 != memcmp ("--",
641 buf,
642 2)) ||
643 (0 != memcmp (&buf[2],
644 boundary,
645 blen)))
646 {
647 if (pp->state != PP_Init)
648 {
649 /* garbage not allowed */
650 pp->state = PP_Error;
651 }
652 else
653 {
654 /* skip over garbage (RFC 2046, 5.1.1) */
655 dash = memchr (buf,
656 '-',
657 pp->buffer_pos);
658 if (NULL == dash)
659 (*ioffptr) += pp->buffer_pos; /* skip entire buffer */
660 else if (dash == buf)
661 (*ioffptr)++; /* at least skip one byte */
662 else
663 (*ioffptr) += (size_t) (dash - buf); /* skip to first possible boundary */
664 }
665 return MHD_NO; /* expected boundary */
666 }
667 /* remove boundary from buffer */
668 (*ioffptr) += 2 + blen;
669 /* next: start with headers */
670 pp->skip_rn = RN_Dash;
671 pp->state = next_state;
672 pp->dash_state = next_dash_state;
673 return MHD_YES;
674}
675
676
683static void
684try_get_value (const char *buf,
685 const char *key,
686 char **destination)
687{
688 const char *spos;
689 const char *bpos;
690 const char *endv;
691 size_t klen;
692 size_t vlen;
693
694 if (NULL != *destination)
695 return;
696 bpos = buf;
697 klen = strlen (key);
698 while (NULL != (spos = strstr (bpos, key)))
699 {
700 if ( (spos[klen] != '=') ||
701 ( (spos != buf) &&
702 (spos[-1] != ' ') ) )
703 {
704 /* no match */
705 bpos = spos + 1;
706 continue;
707 }
708 if (spos[klen + 1] != '"')
709 return; /* not quoted */
710 if (NULL == (endv = strchr (&spos[klen + 2],
711 '\"')))
712 return; /* no end-quote */
713 vlen = (size_t) (endv - spos) - klen - 1;
714 *destination = malloc (vlen);
715 if (NULL == *destination)
716 return; /* out of memory */
717 (*destination)[vlen - 1] = '\0';
718 memcpy (*destination,
719 &spos[klen + 2],
720 vlen - 1);
721 return; /* success */
722 }
723}
724
725
741static int
743 size_t *ioffptr,
744 enum PP_State next_state)
745{
746 char *buf = (char *) &pp[1];
747 size_t newline;
748
749 newline = 0;
750 while ( (newline < pp->buffer_pos) &&
751 (buf[newline] != '\r') &&
752 (buf[newline] != '\n') )
753 newline++;
754 if (newline == pp->buffer_size)
755 {
756 pp->state = PP_Error;
757 return MHD_NO; /* out of memory */
758 }
759 if (newline == pp->buffer_pos)
760 return MHD_NO; /* will need more data */
761 if (0 == newline)
762 {
763 /* empty line - end of headers */
764 pp->skip_rn = RN_Full;
765 pp->state = next_state;
766 return MHD_YES;
767 }
768 /* got an actual header */
769 if (buf[newline] == '\r')
770 pp->skip_rn = RN_OptN;
771 buf[newline] = '\0';
772 if (MHD_str_equal_caseless_n_ ("Content-disposition: ",
773 buf,
774 MHD_STATICSTR_LEN_ ("Content-disposition: ")))
775 {
776 try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
777 "name",
778 &pp->content_name);
779 try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
780 "filename",
781 &pp->content_filename);
782 }
783 else
784 {
785 try_match_header ("Content-type: ",
786 MHD_STATICSTR_LEN_ ("Content-type: "),
787 buf,
788 &pp->content_type);
789 try_match_header ("Content-Transfer-Encoding: ",
790 MHD_STATICSTR_LEN_ ("Content-Transfer-Encoding: "),
791 buf,
793 }
794 (*ioffptr) += newline + 1;
795 return MHD_YES;
796}
797
798
815static int
817 size_t *ioffptr,
818 const char *boundary,
819 size_t blen,
820 enum PP_State next_state,
821 enum PP_State next_dash_state)
822{
823 char *buf = (char *) &pp[1];
824 size_t newline;
825 const char *r;
826
827 /* all data in buf until the boundary
828 (\r\n--+boundary) is part of the value */
829 newline = 0;
830 while (1)
831 {
832 while (newline + 4 < pp->buffer_pos)
833 {
834 r = memchr (&buf[newline],
835 '\r',
836 pp->buffer_pos - newline - 4);
837 if (NULL == r)
838 {
839 newline = pp->buffer_pos - 4;
840 break;
841 }
842 newline = (size_t) (r - buf);
843 if (0 == memcmp ("\r\n--",
844 &buf[newline],
845 4))
846 break;
847 newline++;
848 }
849 if (newline + blen + 4 <= pp->buffer_pos)
850 {
851 /* can check boundary */
852 if (0 != memcmp (&buf[newline + 4],
853 boundary,
854 blen))
855 {
856 /* no boundary, "\r\n--" is part of content, skip */
857 newline += 4;
858 continue;
859 }
860 else
861 {
862 /* boundary found, process until newline then
863 skip boundary and go back to init */
864 pp->skip_rn = RN_Dash;
865 pp->state = next_state;
866 pp->dash_state = next_dash_state;
867 (*ioffptr) += blen + 4; /* skip boundary as well */
868 buf[newline] = '\0';
869 break;
870 }
871 }
872 else
873 {
874 /* cannot check for boundary, process content that
875 we have and check again later; except, if we have
876 no content, abort (out of memory) */
877 if ( (0 == newline) &&
878 (pp->buffer_pos == pp->buffer_size) )
879 {
880 pp->state = PP_Error;
881 return MHD_NO;
882 }
883 break;
884 }
885 }
886 /* newline is either at beginning of boundary or
887 at least at the last character that we are sure
888 is not part of the boundary */
889 if ( ( (pp->must_ikvi) ||
890 (0 != newline) ) &&
891 (MHD_NO == pp->ikvi (pp->cls,
893 pp->content_name,
895 pp->content_type,
897 buf,
898 pp->value_offset,
899 newline)) )
900 {
901 pp->state = PP_Error;
902 return MHD_NO;
903 }
904 pp->must_ikvi = false;
905 pp->value_offset += newline;
906 (*ioffptr) += newline;
907 return MHD_YES;
908}
909
910
915static void
917{
918 if ( (NULL != pp->content_name) &&
919 (0 == (pp->have & NE_content_name)) )
920 {
921 free (pp->content_name);
922 pp->content_name = NULL;
923 }
924 if ( (NULL != pp->content_type) &&
925 (0 == (pp->have & NE_content_type)) )
926 {
927 free (pp->content_type);
928 pp->content_type = NULL;
929 }
930 if ( (NULL != pp->content_filename) &&
931 (0 == (pp->have & NE_content_filename)) )
932 {
933 free (pp->content_filename);
935 }
936 if ( (NULL != pp->content_transfer_encoding) &&
937 (0 == (pp->have & NE_content_transfer_encoding)) )
938 {
939 free (pp->content_transfer_encoding);
941 }
942}
943
944
953static enum MHD_Result
955 const char *post_data,
956 size_t post_data_len)
957{
958 char *buf;
959 size_t max;
960 size_t ioff;
961 size_t poff;
962 int state_changed;
963
964 buf = (char *) &pp[1];
965 ioff = 0;
966 poff = 0;
967 state_changed = 1;
968 while ( (poff < post_data_len) ||
969 ( (pp->buffer_pos > 0) &&
970 (0 != state_changed) ) )
971 {
972 /* first, move as much input data
973 as possible to our internal buffer */
974 max = pp->buffer_size - pp->buffer_pos;
975 if (max > post_data_len - poff)
976 max = post_data_len - poff;
977 memcpy (&buf[pp->buffer_pos],
978 &post_data[poff],
979 max);
980 poff += max;
981 pp->buffer_pos += max;
982 if ( (0 == max) &&
983 (0 == state_changed) &&
984 (poff < post_data_len) )
985 {
986 pp->state = PP_Error;
987 return MHD_NO; /* out of memory */
988 }
989 state_changed = 0;
990
991 /* first state machine for '\r'-'\n' and '--' handling */
992 switch (pp->skip_rn)
993 {
994 case RN_Inactive:
995 break;
996 case RN_OptN:
997 if (buf[0] == '\n')
998 {
999 ioff++;
1000 pp->skip_rn = RN_Inactive;
1001 goto AGAIN;
1002 }
1003 /* fall-through! */
1004 case RN_Dash:
1005 if (buf[0] == '-')
1006 {
1007 ioff++;
1008 pp->skip_rn = RN_Dash2;
1009 goto AGAIN;
1010 }
1011 pp->skip_rn = RN_Full;
1012 /* fall-through! */
1013 case RN_Full:
1014 if (buf[0] == '\r')
1015 {
1016 if ( (pp->buffer_pos > 1) &&
1017 ('\n' == buf[1]) )
1018 {
1019 pp->skip_rn = RN_Inactive;
1020 ioff += 2;
1021 }
1022 else
1023 {
1024 pp->skip_rn = RN_OptN;
1025 ioff++;
1026 }
1027 goto AGAIN;
1028 }
1029 if (buf[0] == '\n')
1030 {
1031 ioff++;
1032 pp->skip_rn = RN_Inactive;
1033 goto AGAIN;
1034 }
1035 pp->skip_rn = RN_Inactive;
1036 pp->state = PP_Error;
1037 return MHD_NO; /* no '\r\n' */
1038 case RN_Dash2:
1039 if (buf[0] == '-')
1040 {
1041 ioff++;
1042 pp->skip_rn = RN_Full;
1043 pp->state = pp->dash_state;
1044 goto AGAIN;
1045 }
1046 pp->state = PP_Error;
1047 break;
1048 }
1049
1050 /* main state engine */
1051 switch (pp->state)
1052 {
1053 case PP_Error:
1054 return MHD_NO;
1055 case PP_Done:
1056 /* did not expect to receive more data */
1057 pp->state = PP_Error;
1058 return MHD_NO;
1059 case PP_Init:
1071 (void) find_boundary (pp,
1072 pp->boundary,
1073 pp->blen,
1074 &ioff,
1076 PP_Done);
1077 break;
1078 case PP_NextBoundary:
1079 if (MHD_NO == find_boundary (pp,
1080 pp->boundary,
1081 pp->blen,
1082 &ioff,
1084 PP_Done))
1085 {
1086 if (pp->state == PP_Error)
1087 return MHD_NO;
1088 goto END;
1089 }
1090 break;
1092 pp->must_ikvi = true;
1093 if (MHD_NO ==
1095 &ioff,
1097 {
1098 if (pp->state == PP_Error)
1099 return MHD_NO;
1100 else
1101 goto END;
1102 }
1103 state_changed = 1;
1104 break;
1106 if ( (NULL != pp->content_type) &&
1108 "multipart/mixed",
1109 MHD_STATICSTR_LEN_ ("multipart/mixed"))))
1110 {
1111 pp->nested_boundary = strstr (pp->content_type,
1112 "boundary=");
1113 if (NULL == pp->nested_boundary)
1114 {
1115 pp->state = PP_Error;
1116 return MHD_NO;
1117 }
1118 pp->nested_boundary =
1119 strdup (&pp->nested_boundary[MHD_STATICSTR_LEN_ ("boundary=")]);
1120 if (NULL == pp->nested_boundary)
1121 {
1122 /* out of memory */
1123 pp->state = PP_Error;
1124 return MHD_NO;
1125 }
1126 /* free old content type, we will need that field
1127 for the content type of the nested elements */
1128 free (pp->content_type);
1129 pp->content_type = NULL;
1130 pp->nlen = strlen (pp->nested_boundary);
1131 pp->state = PP_Nested_Init;
1132 state_changed = 1;
1133 break;
1134 }
1136 pp->value_offset = 0;
1137 state_changed = 1;
1138 break;
1141 &ioff,
1142 pp->boundary,
1143 pp->blen,
1145 PP_Done))
1146 {
1147 if (pp->state == PP_Error)
1148 return MHD_NO;
1149 break;
1150 }
1151 break;
1152 case PP_PerformCleanup:
1153 /* clean up state of one multipart form-data element! */
1154 pp->have = NE_none;
1155 free_unmarked (pp);
1156 if (NULL != pp->nested_boundary)
1157 {
1158 free (pp->nested_boundary);
1159 pp->nested_boundary = NULL;
1160 }
1162 state_changed = 1;
1163 break;
1164 case PP_Nested_Init:
1165 if (NULL == pp->nested_boundary)
1166 {
1167 pp->state = PP_Error;
1168 return MHD_NO;
1169 }
1170 if (MHD_NO == find_boundary (pp,
1171 pp->nested_boundary,
1172 pp->nlen,
1173 &ioff,
1175 PP_NextBoundary /* or PP_Error? */))
1176 {
1177 if (pp->state == PP_Error)
1178 return MHD_NO;
1179 goto END;
1180 }
1181 break;
1183 /* remember what headers were given
1184 globally */
1185 pp->have = NE_none;
1186 if (NULL != pp->content_name)
1187 pp->have |= NE_content_name;
1188 if (NULL != pp->content_type)
1189 pp->have |= NE_content_type;
1190 if (NULL != pp->content_filename)
1195 state_changed = 1;
1196 break;
1198 pp->value_offset = 0;
1199 if (MHD_NO ==
1201 &ioff,
1203 {
1204 if (pp->state == PP_Error)
1205 return MHD_NO;
1206 else
1207 goto END;
1208 }
1209 state_changed = 1;
1210 break;
1213 &ioff,
1214 pp->nested_boundary,
1215 pp->nlen,
1218 {
1219 if (pp->state == PP_Error)
1220 return MHD_NO;
1221 break;
1222 }
1223 break;
1225 free_unmarked (pp);
1227 state_changed = 1;
1228 break;
1229 case PP_ProcessKey:
1230 case PP_ProcessValue:
1231 case PP_Callback:
1232 default:
1233 MHD_PANIC (_ ("internal error.\n")); /* should never happen! */
1234 }
1235AGAIN:
1236 if (ioff > 0)
1237 {
1238 memmove (buf,
1239 &buf[ioff],
1240 pp->buffer_pos - ioff);
1241 pp->buffer_pos -= ioff;
1242 ioff = 0;
1243 state_changed = 1;
1244 }
1245 }
1246END:
1247 if (0 != ioff)
1248 {
1249 memmove (buf,
1250 &buf[ioff],
1251 pp->buffer_pos - ioff);
1252 pp->buffer_pos -= ioff;
1253 }
1254 if (poff < post_data_len)
1255 {
1256 pp->state = PP_Error;
1257 return MHD_NO; /* serious error */
1258 }
1259 return MHD_YES;
1260}
1261
1262
1265 const char *post_data,
1266 size_t post_data_len)
1267{
1268 if (0 == post_data_len)
1269 return MHD_YES;
1270 if (NULL == pp)
1271 return MHD_NO;
1273 pp->encoding,
1276 return post_process_urlencoded (pp,
1277 post_data,
1278 post_data_len);
1280 pp->encoding,
1283 return post_process_multipart (pp,
1284 post_data,
1285 post_data_len);
1286 /* this should never be reached */
1287 return MHD_NO;
1288}
1289
1290
1293{
1294 enum MHD_Result ret;
1295
1296 if (NULL == pp)
1297 return MHD_YES;
1298 if (PP_ProcessValue == pp->state)
1299 {
1300 /* key without terminated value left at the end of the
1301 buffer; fake receiving a termination character to
1302 ensure it is also processed */
1304 "\n",
1305 1);
1306 }
1307 /* These internal strings need cleaning up since
1308 the post-processing may have been interrupted
1309 at any stage */
1310 if ( (pp->xbuf_pos > 0) ||
1311 ( (pp->state != PP_Done) &&
1312 (pp->state != PP_Init) ) )
1313 ret = MHD_NO;
1314 else
1315 ret = MHD_YES;
1316 pp->have = NE_none;
1317 free_unmarked (pp);
1318 if (NULL != pp->nested_boundary)
1319 free (pp->nested_boundary);
1320 free (pp);
1321 return ret;
1322}
1323
1324
1325/* 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:717
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