aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2008-01-31 06:16:41 +0000
committerChristian Grothoff <christian@grothoff.org>2008-01-31 06:16:41 +0000
commitccad20a05fcf4ae866551ea792ebdceb2ffd5664 (patch)
tree069fee4d16e81114a9d16535b22cbad47b92fb09
parente7240b0c33a6bbdea113a0b3d24e4c71aa007592 (diff)
downloadlibmicrohttpd-ccad20a05fcf4ae866551ea792ebdceb2ffd5664.tar.gz
libmicrohttpd-ccad20a05fcf4ae866551ea792ebdceb2ffd5664.zip
finished support for nested multiparts
-rw-r--r--ChangeLog3
-rw-r--r--src/daemon/postprocessor.c951
-rw-r--r--src/daemon/postprocessor_test.c5
3 files changed, 662 insertions, 297 deletions
diff --git a/ChangeLog b/ChangeLog
index 154b28ce..a1156c6e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
1Wed Jan 30 23:15:44 MST 2008
2 Added support for nested multiparts to post processor. -CG
3
1Mon Jan 21 11:59:46 MST 2008 4Mon Jan 21 11:59:46 MST 2008
2 Added option to limit number of concurrent connections 5 Added option to limit number of concurrent connections
3 accepted from the same IP address. -CG 6 accepted from the same IP address. -CG
diff --git a/src/daemon/postprocessor.c b/src/daemon/postprocessor.c
index 2b978384..7147c114 100644
--- a/src/daemon/postprocessor.c
+++ b/src/daemon/postprocessor.c
@@ -26,28 +26,87 @@
26#include "internal.h" 26#include "internal.h"
27 27
28/** 28/**
29 * Size of on-stack buffer that we use for un-escaping of the value.
30 */
31#define XBUF_SIZE 1024
32
33/**
29 * States in the PP parser's state machine. 34 * States in the PP parser's state machine.
30 */ 35 */
31enum PP_State 36enum PP_State
32{ 37{
33 PP_Init = 0, 38 /* general states */
34 PP_HaveKey = 1, 39 PP_Error,
35 PP_ExpectNewLine = 2, 40 PP_Done,
36 PP_ExpectNewLineR = 3, 41 PP_Init,
37 PP_ExpectNewLineN = 4, 42
38 PP_ExpectNewLineNOPT = 5, 43 /* url encoding-states*/
39 PP_Headers = 6, 44 PP_ProcessValue,
40 PP_SkipRN = 7, 45 PP_ExpectNewLine,
41 PP_SkipN = 8, 46
42 PP_ValueToBoundary = 9, 47 /* post encoding-states */
43 PP_FinalDash = 10, 48 PP_ProcessEntryHeaders,
44 PP_FinalRN = 11, 49 PP_PerformCheckMultipart,
45 PP_FinalN = 12, 50 PP_ProcessValueToBoundary,
46 PP_Error = 9999, 51 PP_PerformCleanup,
52
53 /* nested post-encoding states */
54 PP_Nested_Init,
55 PP_Nested_PerformMarking,
56 PP_Nested_ProcessEntryHeaders,
57 PP_Nested_ProcessValueToBoundary,
58 PP_Nested_PerformCleanup,
59
60};
61
62enum RN_State
63{
64 /**
65 * No RN-preprocessing in this state.
66 */
67 RN_Inactive = 0,
68
69 /**
70 * If the next character is '\n', skip it. Otherwise,
71 * just go inactive.
72 */
73 RN_OptN = 1,
74
75 /**
76 * Expect '\r\n' (and only '\r\n'). As always, we also
77 * expect only '\r' or only '\n'.
78 */
79 RN_Full = 2,
80
81 /**
82 * Expect either '\r\n' or '--\r\n'. If '--\r\n', transition into dash-state
83 * for the main state machine
84 */
85 RN_Dash = 3,
86
87 /**
88 * Got a single dash, expect second dash.
89 */
90 RN_Dash2 = 4,
91};
92
93/**
94 * Bits for the globally known fields that
95 * should not be deleted when we exit the
96 * nested state.
97 */
98enum NE_State
99{
100 NE_none = 0,
101 NE_content_name = 1,
102 NE_content_type = 2,
103 NE_content_filename = 4,
104 NE_content_transfer_encoding = 8,
47}; 105};
48 106
49/** 107/**
50 * Internal state of the post-processor. 108 * Internal state of the post-processor. Note that the fields
109 * are sorted by type to enable optimal packing by the compiler.
51 */ 110 */
52struct MHD_PostProcessor 111struct MHD_PostProcessor
53{ 112{
@@ -75,9 +134,19 @@ struct MHD_PostProcessor
75 const char *encoding; 134 const char *encoding;
76 135
77 /** 136 /**
137 * Primary boundary (points into encoding string)
138 */
139 const char * boundary;
140
141 /**
142 * Nested boundary (if we have multipart/mixed encoding).
143 */
144 char * nested_boundary;
145
146 /**
78 * Pointer to the name given in disposition. 147 * Pointer to the name given in disposition.
79 */ 148 */
80 char *content_disposition; 149 char *content_name;
81 150
82 /** 151 /**
83 * Pointer to the (current) content type. 152 * Pointer to the (current) content type.
@@ -87,12 +156,12 @@ struct MHD_PostProcessor
87 /** 156 /**
88 * Pointer to the (current) filename. 157 * Pointer to the (current) filename.
89 */ 158 */
90 char *filename; 159 char *content_filename;
91 160
92 /** 161 /**
93 * Pointer to the (current) encoding. 162 * Pointer to the (current) encoding.
94 */ 163 */
95 char *transfer_encoding; 164 char *content_transfer_encoding;
96 165
97 /** 166 /**
98 * Unprocessed value bytes due to escape 167 * Unprocessed value bytes due to escape
@@ -121,10 +190,40 @@ struct MHD_PostProcessor
121 unsigned int value_offset; 190 unsigned int value_offset;
122 191
123 /** 192 /**
193 * strlen(boundary) -- if boundary != NULL.
194 */
195 size_t blen;
196
197 /**
198 * strlen(nested_boundary) -- if nested_boundary != NULL.
199 */
200 size_t nlen;
201
202 /**
124 * State of the parser. 203 * State of the parser.
125 */ 204 */
126 enum PP_State state; 205 enum PP_State state;
127 206
207 /**
208 * Side-state-machine: skip '\r\n' (or just '\n').
209 * Set to 0 if we are not in skip mode. Set to 2
210 * if a '\r\n' is expected, set to 1 if a '\n' should
211 * be skipped if it is the next character.
212 */
213 enum RN_State skip_rn;
214
215 /**
216 * If we are in skip_rn with "dash" mode and
217 * do find 2 dashes, what state do we go into?
218 */
219 enum PP_State dash_state;
220
221 /**
222 * Which headers are global? (used to tell which
223 * headers were only valid for the nested multipart).
224 */
225 enum NE_State have;
226
128}; 227};
129 228
130 229
@@ -153,6 +252,8 @@ MHD_create_post_processor (struct MHD_Connection *connection,
153{ 252{
154 struct MHD_PostProcessor *ret; 253 struct MHD_PostProcessor *ret;
155 const char *encoding; 254 const char *encoding;
255 const char *boundary;
256 size_t blen;
156 257
157 if ((buffer_size < 256) || (connection == NULL) || (ikvi == NULL)) 258 if ((buffer_size < 256) || (connection == NULL) || (ikvi == NULL))
158 abort (); 259 abort ();
@@ -161,30 +262,42 @@ MHD_create_post_processor (struct MHD_Connection *connection,
161 MHD_HTTP_HEADER_CONTENT_TYPE); 262 MHD_HTTP_HEADER_CONTENT_TYPE);
162 if (encoding == NULL) 263 if (encoding == NULL)
163 return NULL; 264 return NULL;
164 if ((0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, 265 boundary = NULL;
165 encoding)) && 266 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
166 (0 != strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding, 267 encoding))
167 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))) 268 {
168 return NULL; 269 if (0 != strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding,
270 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
271 return NULL;
272 boundary =
273 &encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
274 /* Q: should this be "strcasestr"? */
275 if (NULL != strstr (boundary, "boundary="))
276 boundary = strstr (boundary, "boundary=") + strlen ("boundary=");
277 else
278 return NULL; /* failed to determine boundary */
279 blen = strlen (boundary);
280 if ( (blen == 0) ||
281 (blen * 2 + 2 > buffer_size) )
282 return NULL; /* (will be) out of memory or invalid boundary */
283 }
169 ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size + 1); 284 ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size + 1);
170 if (ret == NULL) 285 if (ret == NULL)
171 return NULL; 286 return NULL;
172 memset (ret, 0, sizeof (struct MHD_PostProcessor)); 287 memset (ret, 0, sizeof (struct MHD_PostProcessor) + buffer_size + 1);
173 ret->connection = connection; 288 ret->connection = connection;
174 ret->ikvi = ikvi; 289 ret->ikvi = ikvi;
175 ret->cls = cls; 290 ret->cls = cls;
176 ret->encoding = encoding; 291 ret->encoding = encoding;
177 ret->buffer_size = buffer_size; 292 ret->buffer_size = buffer_size;
178 ret->state = PP_Init; 293 ret->state = PP_Init;
294 ret->blen = blen;
295 ret->boundary = boundary;
296 ret->skip_rn = RN_Inactive;
179 return ret; 297 return ret;
180} 298}
181 299
182/** 300/**
183 * On-stack buffer that we use for un-escaping of the value.
184 */
185#define XBUF_SIZE 1024
186
187/**
188 * Process url-encoded POST data. 301 * Process url-encoded POST data.
189 */ 302 */
190static int 303static int
@@ -205,6 +318,12 @@ post_process_urlencoded (struct MHD_PostProcessor *pp,
205 { 318 {
206 switch (pp->state) 319 switch (pp->state)
207 { 320 {
321 case PP_Error:
322 return MHD_NO;
323 case PP_Done:
324 /* did not expect to receive more data */
325 pp->state = PP_Error;
326 return MHD_NO;
208 case PP_Init: 327 case PP_Init:
209 equals = 0; 328 equals = 0;
210 while ((equals + poff < post_data_len) && 329 while ((equals + poff < post_data_len) &&
@@ -223,10 +342,10 @@ post_process_urlencoded (struct MHD_PostProcessor *pp,
223 pp->buffer_pos = 0; /* reset for next key */ 342 pp->buffer_pos = 0; /* reset for next key */
224 MHD_http_unescape (buf); 343 MHD_http_unescape (buf);
225 poff += equals + 1; 344 poff += equals + 1;
226 pp->state = PP_HaveKey; 345 pp->state = PP_ProcessValue;
227 pp->value_offset = 0; 346 pp->value_offset = 0;
228 break; 347 break;
229 case PP_HaveKey: 348 case PP_ProcessValue:
230 /* obtain rest of value from previous iteration */ 349 /* obtain rest of value from previous iteration */
231 memcpy (xbuf, pp->xbuf, pp->xbuf_pos); 350 memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
232 xoff = pp->xbuf_pos; 351 xoff = pp->xbuf_pos;
@@ -301,12 +420,10 @@ post_process_urlencoded (struct MHD_PostProcessor *pp,
301 { 420 {
302 poff++; 421 poff++;
303 /* we are done, report error if we receive any more... */ 422 /* we are done, report error if we receive any more... */
304 pp->state = PP_Error; 423 pp->state = PP_Done;
305 return MHD_YES; 424 return MHD_YES;
306 } 425 }
307 return MHD_NO; 426 return MHD_NO;
308 case PP_Error:
309 return MHD_NO;
310 default: 427 default:
311 abort (); /* should never happen! */ 428 abort (); /* should never happen! */
312 } 429 }
@@ -323,6 +440,8 @@ post_process_urlencoded (struct MHD_PostProcessor *pp,
323static int 440static int
324try_match_header (const char *prefix, char *line, char **suffix) 441try_match_header (const char *prefix, char *line, char **suffix)
325{ 442{
443 if (NULL != *suffix)
444 return MHD_NO;
326 while(*line != 0) 445 while(*line != 0)
327 { 446 {
328 if (0 == strncasecmp (prefix, line, strlen (prefix))) 447 if (0 == strncasecmp (prefix, line, strlen (prefix)))
@@ -335,290 +454,534 @@ try_match_header (const char *prefix, char *line, char **suffix)
335 return MHD_NO; 454 return MHD_NO;
336} 455}
337 456
457static int
458find_boundary(struct MHD_PostProcessor * pp,
459 const char * boundary,
460 size_t blen,
461 unsigned int * ioffptr,
462 enum PP_State next_state,
463 enum PP_State next_dash_state)
464{
465 char * buf = (char*) &pp[1];
466
467 if (pp->buffer_pos < 2 + blen)
468 {
469 if (pp->buffer_pos == pp->buffer_size)
470 pp->state = PP_Error; /* out of memory */
471 return MHD_NO; /* not enough data */
472 }
473 if ( (0 != memcmp ("--", buf, 2)) ||
474 (0 != memcmp (&buf[2], boundary, blen)))
475 {
476 pp->state = PP_Error;
477 return MHD_NO; /* expected boundary */
478 }
479 /* remove boundary from buffer */
480 (*ioffptr) += 2 + blen;
481 /* next: start with headers */
482 pp->skip_rn = RN_Dash;
483 pp->state = next_state;
484 pp->dash_state = next_dash_state;
485 return MHD_YES;
486}
487
338/** 488/**
339 * Decode multipart POST data. 489 * In buf, there maybe an expression
340 * 490 * '$key="$value"'. If that is the case,
341 * TODO: If the content-type is multipart/mixed, we do not do anything 491 * copy a copy of $value to destination.
342 * special. However, we should probably break the individual values 492 *
343 * apart and give them to the callback individually (will require some 493 * If destination is already non-NULL,
344 * additional states & state). 494 * do nothing.
495 */
496static void
497try_get_value(const char * buf,
498 const char * key,
499 char ** destination)
500{
501 const char * spos;
502 const char * bpos;
503 const char * endv;
504 size_t klen;
505 size_t vlen;
506
507 if (NULL != *destination)
508 return;
509 bpos = buf;
510 klen = strlen(key);
511 while (NULL != (spos = strstr(bpos, key)))
512 {
513 if ( (spos[klen] != '=') ||
514 ( (spos != buf) &&
515 (spos[-1] != ' ') ) )
516 {
517 /* no match */
518 bpos = spos + 1;
519 continue;
520 }
521 if (spos[klen + 1] != '"')
522 return; /* not quoted */
523 if (NULL == (endv = strstr(&spos[klen+2], "\"")))
524 return; /* no end-quote */
525 vlen = endv - spos - klen - 1;
526 *destination = malloc(vlen);
527 if (NULL == *destination)
528 return; /* out of memory */
529 (*destination)[vlen - 1] = '\0';
530 memcpy(*destination,
531 &spos[klen + 2],
532 vlen - 1);
533 return; /* success */
534 }
535}
536
537/**
538 * Go over the headers of the part and update
539 * the fields in "pp" according to what we find.
540 * If we are at the end of the headers (as indicated
541 * by an empty line), transition into next_state.
542 *
543 * @param ioffptr set to how many bytes have been
544 * processed
545 * @return MHD_YES if we can continue processing,
546 * MHD_NO on error or if we do not have
547 * enough data yet
548 */
549static int
550process_multipart_headers(struct MHD_PostProcessor * pp,
551 unsigned int * ioffptr,
552 enum PP_State next_state)
553{
554 char * buf = (char*) &pp[1];
555 unsigned int newline;
556
557 newline = 0;
558 while ((newline < pp->buffer_pos) &&
559 (buf[newline] != '\r') &&
560 (buf[newline] != '\n'))
561 newline++;
562 if (newline == pp->buffer_size)
563 {
564 pp->state = PP_Error;
565 return MHD_NO; /* out of memory */
566 }
567 if (newline == pp->buffer_pos)
568 return MHD_NO; /* will need more data */
569 if (newline == 0)
570 {
571 /* empty line - end of headers */
572 pp->skip_rn = RN_Full;
573 pp->state = next_state;
574 return MHD_YES;
575 }
576 /* got an actual header */
577 if (buf[newline] == '\r')
578 pp->skip_rn = RN_OptN;
579 buf[newline] = '\0';
580 if (0 == strncasecmp("Content-disposition: ",
581 buf,
582 strlen("Content-disposition: ")))
583 {
584 try_get_value(&buf[strlen("Content-disposition: ")],
585 "name",
586 &pp->content_name);
587 try_get_value(&buf[strlen("Content-disposition: ")],
588 "filename",
589 &pp->content_filename);
590 }
591 else
592 {
593 try_match_header ("Content-type: ", buf, &pp->content_type);
594 try_match_header ("Content-Transfer-Encoding: ",
595 buf, &pp->content_transfer_encoding);
596}
597 (*ioffptr) += newline + 1;
598 return MHD_YES;
599}
600
601/**
602 * We have the value until we hit the given boundary;
603 * process accordingly.
345 * 604 *
346 * See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 605 * @param boundary the boundary to look for
606 * @param blen strlen(boundary)
607 * @param next_state what state to go into after the
608 * boundary was found
609 * @param next_dash_state state to go into if the next
610 * boundary ends with "--"
611 * @return MHD_YES if we can continue processing,
612 * MHD_NO on error or if we do not have
613 * enough data yet
614 */
615static int
616process_value_to_boundary(struct MHD_PostProcessor * pp,
617 unsigned int * ioffptr,
618 const char * boundary,
619 size_t blen,
620 enum PP_State next_state,
621 enum PP_State next_dash_state)
622{
623 char * buf = (char*) &pp[1];
624 unsigned int newline;
625
626 /* all data in buf until the boundary
627 (\r\n--+boundary) is part of the value */
628 newline = 0;
629 while (1)
630 {
631 while ((newline + 4 < pp->buffer_pos) &&
632 (0 != memcmp ("\r\n--", &buf[newline], 4)))
633 newline++;
634 if (newline + pp->blen + 4 <= pp->buffer_pos)
635 {
636 /* can check boundary */
637 if (0 != memcmp (&buf[newline + 4], boundary, pp->blen))
638 {
639 /* no boundary, "\r\n--" is part of content, skip */
640 newline += 4;
641 continue;
642 }
643 else
644 {
645 /* boundary found, process until newline then
646 skip boundary and go back to init */
647 pp->skip_rn = RN_Dash;
648 pp->state = next_state;
649 pp->dash_state = next_dash_state;
650 (*ioffptr) += pp->blen + 4; /* skip boundary as well */
651 break;
652 }
653 }
654 else
655 {
656 /* cannot check for boundary, process content that
657 we have and check again later; except, if we have
658 no content, abort (out of memory) */
659 if ( (newline == 0) &&
660 (pp->buffer_pos == pp->buffer_size) )
661 {
662 pp->state = PP_Error;
663 return MHD_NO;
664 }
665 return MHD_NO;
666 }
667 }
668 /* newline is either at beginning of boundary or
669 at least at the last character that we are sure
670 is not part of the boundary */
671 if (MHD_NO == pp->ikvi (pp->cls,
672 MHD_POSTDATA_KIND,
673 pp->content_name,
674 pp->content_filename,
675 pp->content_type,
676 pp->content_transfer_encoding,
677 buf,
678 pp->value_offset, newline))
679 {
680 pp->state = PP_Error;
681 return MHD_NO;
682 }
683 pp->value_offset += newline;
684 (*ioffptr) += newline;
685 return MHD_YES;
686}
687
688static void
689free_unmarked(struct MHD_PostProcessor * pp)
690{
691 if ( (pp->content_name != NULL) &&
692 (0 == (pp->have & NE_content_name)) )
693 {
694 free(pp->content_name);
695 pp->content_name = NULL;
696 }
697 if ( (pp->content_type != NULL) &&
698 (0 == (pp->have & NE_content_type)) )
699 {
700 free(pp->content_type);
701 pp->content_type = NULL;
702 }
703 if ( (pp->content_filename != NULL) &&
704 (0 == (pp->have & NE_content_filename)) )
705 {
706 free(pp->content_filename);
707 pp->content_filename = NULL;
708 }
709 if ( (pp->content_transfer_encoding != NULL) &&
710 (0 == (pp->have & NE_content_transfer_encoding)) )
711 {
712 free(pp->content_transfer_encoding);
713 pp->content_transfer_encoding = NULL;
714 }
715}
716
717/**
718 * Decode multipart POST data.
347 */ 719 */
348static int 720static int
349post_process_multipart (struct MHD_PostProcessor *pp, 721post_process_multipart (struct MHD_PostProcessor *pp,
350 const char *post_data, unsigned int post_data_len) 722 const char *post_data, unsigned int post_data_len)
351{ 723{
352 char *buf; 724 char *buf;
353 const char *boundary;
354 unsigned int max; 725 unsigned int max;
355 unsigned int ioff; 726 unsigned int ioff;
356 unsigned int poff; 727 unsigned int poff;
357 unsigned int newline;
358 unsigned int endquote;
359 size_t blen;
360 728
361 buf = (char *) &pp[1]; 729 buf = (char *) &pp[1];
362 ioff = 0; 730 ioff = 0;
363 poff = 0; 731 poff = 0;
364 boundary = 732 max = 1;
365 &pp->encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)]; 733 while ( (poff < post_data_len) ||
366 /* Q: should this be "strcasestr"? */ 734 ( (pp->buffer_pos > 0) &&
367 if (NULL != strstr (boundary, "boundary=")) 735 (max != 0) ) )
368 boundary = strstr (boundary, "boundary=") + strlen ("boundary=");
369 else
370 return MHD_NO; /* failed to determine boundary */
371 blen = strlen (boundary);
372 if (blen * 2 + 2 > pp->buffer_size)
373 return MHD_NO; /* (will be) out of memory */
374 while ((poff < post_data_len) || (pp->buffer_pos > ioff))
375 { 736 {
376 /* first, move data to our internal buffer */ 737 /* first, move as much input data
738 as possible to our internal buffer */
377 max = pp->buffer_size - pp->buffer_pos; 739 max = pp->buffer_size - pp->buffer_pos;
378 if ((max < ioff) && (max < post_data_len - poff))
379 {
380 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
381 pp->buffer_pos -= ioff;
382 ioff = 0;
383 max = pp->buffer_size - pp->buffer_pos;
384 }
385 if (max > post_data_len - poff) 740 if (max > post_data_len - poff)
386 max = post_data_len - poff; 741 max = post_data_len - poff;
387 memcpy (&buf[pp->buffer_pos], &post_data[poff], max); 742 memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
388 poff += max; 743 poff += max;
389 pp->buffer_pos += max; 744 pp->buffer_pos += max;
745 if ( (max == 0) &&
746 (poff < post_data_len) )
747 {
748 pp->state = PP_Error;
749 return MHD_NO; /* out of memory */
750 }
751
752 /* first state machine for '\r'-'\n' and '--' handling */
753 switch (pp->skip_rn)
754 {
755 case RN_Inactive:
756 break;
757 case RN_OptN:
758 if (buf[0] == '\n')
759 {
760 ioff++;
761 pp->skip_rn = RN_Inactive;
762 goto AGAIN;
763 }
764 case RN_Dash:
765 if (buf[0] == '-')
766 {
767 ioff++;
768 pp->skip_rn = RN_Dash2;
769 goto AGAIN;
770 }
771 pp->skip_rn = RN_Full;
772 /* fall-through! */
773 case RN_Full:
774 if (buf[0] == '\r')
775 {
776 if ( (pp->buffer_pos > 1) &&
777 (buf[1] == '\n') )
778 {
779 pp->skip_rn = RN_Inactive;
780 ioff += 2;
781 }
782 else
783 {
784 pp->skip_rn = RN_OptN;
785 ioff++;
786 }
787 goto AGAIN;
788 }
789 if (buf[0] == '\n')
790 {
791 ioff++;
792 pp->skip_rn = RN_Inactive;
793 goto AGAIN;
794 }
795 pp->skip_rn = RN_Inactive;
796 pp->state = PP_Error;
797 return MHD_NO; /* no '\r\n' */
798 case RN_Dash2:
799 if (buf[0] == '-')
800 {
801 ioff++;
802 pp->skip_rn = RN_Full;
803 pp->state = pp->dash_state;
804 goto AGAIN;
805 }
806 pp->state = PP_Error;
807 break;
808 }
390 809
810 /* main state engine */
391 switch (pp->state) 811 switch (pp->state)
392 { 812 {
393 case PP_Init: 813 case PP_Error:
394 /* we're looking for the boundary */ 814 return MHD_NO;
395 if (pp->buffer_pos < 2 + blen + ioff) 815 case PP_Done:
396 goto END; 816 /* did not expect to receive more data */
397 if ((0 != memcmp ("--", &buf[ioff], 2)) || 817 pp->state = PP_Error;
398 (0 != memcmp (&buf[ioff + 2], boundary, blen))) 818 return MHD_NO;
399 return MHD_NO; /* expected boundary */ 819 case PP_Init:
400 820 if (MHD_NO == find_boundary(pp,
401 /* remove boundary from buffer */ 821 pp->boundary,
402 ioff += 2 + blen; 822 pp->blen,
403 823 &ioff,
404 /* next: start with headers */ 824 PP_ProcessEntryHeaders,
405 pp->state = PP_ExpectNewLineR; 825 PP_Done))
826 {
827 if (pp->state == PP_Error)
828 return MHD_NO;
829 goto END;
830 }
831 break;
832 case PP_ProcessEntryHeaders:
833 if (MHD_NO == process_multipart_headers(pp, &ioff, PP_PerformCheckMultipart))
834 {
835 if (pp->state == PP_Error)
836 return MHD_NO;
837 else
838 goto END;
839 }
840 max = 1;
406 break; 841 break;
407 case PP_ExpectNewLineR: 842 case PP_PerformCheckMultipart:
408 if (buf[ioff] == '-') 843 if ( (pp->content_type != NULL) &&
409 { 844 (0 == strncasecmp(pp->content_type,
410 /* last boundary ends with "--" */ 845 "multipart/mixed",
411 ioff++; 846 strlen("multipart/mixed")) ) )
412 pp->state = PP_FinalDash; 847 {
413 break; 848 pp->nested_boundary = strstr(pp->content_type,
414 } 849 "boundary=");
415 if (buf[ioff] == '\r') 850 if (pp->nested_boundary == NULL)
416 { 851 {
417 ioff++; 852 pp->state = PP_Error;
418 pp->state = PP_ExpectNewLineNOPT; 853 return MHD_NO;
419 break; 854 }
420 } 855 pp->nested_boundary = strdup(&pp->nested_boundary[strlen("boundary=")]);
421 /* fall through! */ 856 if (pp->nested_boundary == NULL)
422 case PP_ExpectNewLineN: 857 {
423 if (buf[ioff] == '\n') 858 /* out of memory */
424 { 859 pp->state = PP_Error;
425 ioff++; 860 return MHD_NO;
426 pp->state = PP_Headers; 861 }
427 break; 862 /* free old content type, we will need that field
428 } 863 for the content type of the nested elements */
429 return MHD_NO; 864 free(pp->content_type);
430 case PP_ExpectNewLineNOPT: 865 pp->content_type = NULL;
431 if (buf[ioff] == '\n') 866 pp->nlen = strlen(pp->nested_boundary);
432 { 867 pp->state = PP_Nested_Init;
433 ioff++; 868 max = 1;
434 pp->state = PP_Headers; 869 break;
435 break; 870 }
436 } 871 pp->state = PP_ProcessValueToBoundary;
437 /* fall through! */ 872 pp->value_offset = 0;
438 case PP_Headers: 873 max = 1;
439 newline = 0; 874 break;
440 while ((newline + ioff < pp->buffer_pos) && 875 case PP_ProcessValueToBoundary:
441 (buf[newline + ioff] != '\r') && 876 if (MHD_NO == process_value_to_boundary(pp,
442 (buf[newline + ioff] != '\n')) 877 &ioff,
443 newline++; 878 pp->boundary,
444 if (newline == pp->buffer_size) 879 pp->blen,
445 return MHD_NO; /* out of memory */ 880 PP_PerformCleanup,
446 if (newline + ioff == pp->buffer_pos) 881 PP_Done))
447 { 882 {
448 /* try to make more room */ 883 if (pp->state == PP_Error)
449 memmove (buf, &buf[ioff], pp->buffer_pos - ioff); 884 return MHD_NO;
450 pp->buffer_pos -= ioff; 885 break;
451 ioff = 0; 886 }
452 break; 887 break;
453 } 888 case PP_PerformCleanup:
454 if (newline == 0) 889 /* clean up state of one multipart form-data element! */
455 { 890 pp->have = NE_none;
456 pp->state = PP_SkipRN; 891 free_unmarked(pp);
457 break; 892 if (pp->nested_boundary != NULL)
458 } 893 {
459 buf[ioff + newline] = '\0'; 894 free (pp->nested_boundary);
460 if ((MHD_YES 895 pp->nested_boundary = NULL;
461 == try_match_header ("Content-Disposition: form-data; name=\"", 896 }
462 &buf[ioff], 897 pp->state = PP_ProcessEntryHeaders;
463 &pp->content_disposition)) && 898 max = 1;
464 (pp->content_disposition != NULL) && 899 break;
465 (0 < strlen (pp->content_disposition))) 900 case PP_Nested_Init:
466 { 901 if (pp->nested_boundary == NULL)
467 /* find end-quote; then check if we also have a filename! */ 902 {
468 endquote = 0; 903 pp->state = PP_Error;
469 while ((pp->content_disposition[endquote] != '\"') && 904 return MHD_NO;
470 (pp->content_disposition[endquote] != '\0')) 905 }
471 endquote++; 906 if (MHD_NO == find_boundary(pp,
472 pp->content_disposition[endquote++] = '\0'; /* remove end-quote */ 907 pp->nested_boundary,
473 if ((MHD_YES 908 pp->nlen,
474 == try_match_header (" filename=\"", 909 &ioff,
475 &pp->content_disposition[endquote], 910 PP_Nested_PerformMarking,
476 &pp->filename)) && 911 PP_Init /* or PP_Error? */))
477 (pp->filename != NULL) && (0 < strlen (pp->filename))) 912 {
478 pp->filename[strlen (pp->filename) - 1] = '\0'; /* remove end-quote */ 913 if (pp->state == PP_Error)
479 } 914 return MHD_NO;
480 try_match_header ("Content-Type: ", &buf[ioff], &pp->content_type); 915 goto END;
481 try_match_header ("Content-Transfer-Encoding: ", 916 }
482 &buf[ioff], &pp->transfer_encoding); 917 break;
483 ioff += newline + 1; 918 case PP_Nested_PerformMarking:
484 pp->state = PP_ExpectNewLineNOPT; 919 /* remember what headers were given
920 globally */
921 pp->have = NE_none;
922 if (pp->content_name != NULL)
923 pp->have |= NE_content_name;
924 if (pp->content_type != NULL)
925 pp->have |= NE_content_type;
926 if (pp->content_filename != NULL)
927 pp->have |= NE_content_filename;
928 if (pp->content_transfer_encoding != NULL)
929 pp->have |= NE_content_transfer_encoding;
930 pp->state = PP_Nested_ProcessEntryHeaders;
931 max = 1;
932 break;
933 case PP_Nested_ProcessEntryHeaders:
934 pp->value_offset = 0;
935 if (MHD_NO == process_multipart_headers(pp, &ioff, PP_Nested_ProcessValueToBoundary))
936 {
937 if (pp->state == PP_Error)
938 return MHD_NO;
939 else
940 goto END;
941 }
942 max = 1;
485 break; 943 break;
486 case PP_SkipRN: 944 case PP_Nested_ProcessValueToBoundary:
487 if (buf[ioff] == '\r') 945 if (MHD_NO == process_value_to_boundary(pp,
488 { 946 &ioff,
489 ioff++; 947 pp->nested_boundary,
490 pp->state = PP_SkipN; 948 pp->nlen,
491 break; 949 PP_Nested_PerformCleanup,
492 } 950 PP_Init))
493 /* fall through! */ 951 {
494 case PP_SkipN: 952 if (pp->state == PP_Error)
495 if (buf[ioff] == '\n') 953 return MHD_NO;
496 { 954 break;
497 ioff++; 955 }
498 pp->state = PP_ValueToBoundary; 956 break;
499 pp->value_offset = 0; 957 case PP_Nested_PerformCleanup:
500 break; 958 free_unmarked(pp);
501 } 959 pp->state = PP_Nested_ProcessEntryHeaders;
502 return MHD_NO; /* parse error */ 960 max = 1;
503 case PP_ValueToBoundary: 961 break;
504 /* all data in buf until the boundary
505 (\r\n--+boundary) is part of the value */
506 newline = 0;
507 while (1)
508 {
509 while ((newline + ioff + 4 < pp->buffer_pos) &&
510 (0 != memcmp ("\r\n--", &buf[newline + ioff], 4)))
511 newline++;
512 if (newline + blen + 4 > pp->buffer_size)
513 {
514 /* boundary not in sight --
515 process data, then make room for more! */
516 if (MHD_NO == pp->ikvi (pp->cls,
517 MHD_POSTDATA_KIND,
518 pp->content_disposition,
519 pp->filename,
520 pp->content_type,
521 pp->transfer_encoding,
522 &buf[ioff],
523 pp->value_offset, newline))
524 {
525 pp->state = PP_Error;
526 break;
527 }
528 pp->value_offset += newline;
529 ioff += newline;
530 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
531 pp->buffer_pos -= ioff;
532 ioff = 0;
533 break;
534 }
535 if (newline + blen + 4 < pp->buffer_pos)
536 {
537 /* can check for boundary right now! */
538 if (0 == memcmp (&buf[newline + ioff + 4], boundary, blen))
539 {
540 /* found: process data, then look for more */
541 if (MHD_NO == pp->ikvi (pp->cls,
542 MHD_POSTDATA_KIND,
543 pp->content_disposition,
544 pp->filename,
545 pp->content_type,
546 pp->transfer_encoding,
547 &buf[ioff],
548 pp->value_offset, newline))
549 {
550 pp->state = PP_Error;
551 break;
552 }
553
554 /* clean up! */
555 if (pp->content_type != NULL)
556 {
557 free (pp->content_type);
558 pp->content_type = NULL;
559 }
560 if (pp->content_disposition != NULL)
561 {
562 free (pp->content_disposition);
563 pp->content_disposition = NULL;
564 }
565 if (pp->filename != NULL)
566 {
567 free (pp->filename);
568 pp->filename = NULL;
569 }
570 if (pp->transfer_encoding != NULL)
571 {
572 free (pp->transfer_encoding);
573 pp->transfer_encoding = NULL;
574 }
575 pp->value_offset = 0;
576 ioff += newline + 2; /* skip data + new line */
577 pp->state = PP_Init;
578 break;
579 }
580 /* not the boundary, look further! */
581 newline += 4;
582 continue;
583 }
584 goto END;
585 }
586 break;
587 case PP_FinalDash:
588 if (buf[ioff] == '-')
589 {
590 /* last boundary ends with "--" */
591 ioff++;
592 pp->state = PP_FinalRN;
593 break;
594 }
595 return MHD_NO; /* parse error */
596 case PP_FinalRN:
597 if (buf[ioff] == '\r')
598 {
599 ioff++;
600 pp->state = PP_FinalN;
601 break;
602 }
603 /* fall through! */
604 case PP_FinalN:
605 if (buf[ioff] == '\n')
606 {
607 ioff++;
608 pp->state = PP_Error;
609 break;
610 }
611 return MHD_NO; /* parse error */
612 case PP_Error:
613 return MHD_NO;
614 default: 962 default:
615 abort (); /* should never happen! */ 963 abort (); /* should never happen! */
616
617 } 964 }
965AGAIN:
966 if (ioff > 0)
967 {
968 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
969 pp->buffer_pos -= ioff;
970 ioff = 0;
971 max = 1;
972 }
618 } 973 }
619END: 974END:
620 memmove (buf, &buf[ioff], pp->buffer_pos - ioff); 975 if (ioff != 0)
621 pp->buffer_pos -= ioff; 976 {
977 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
978 pp->buffer_pos -= ioff;
979 }
980 if (poff < post_data_len)
981 {
982 pp->state = PP_Error;
983 return MHD_NO; /* serious error */
984 }
622 return MHD_YES; 985 return MHD_YES;
623} 986}
624 987
@@ -661,14 +1024,10 @@ MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
661 /* These internal strings need cleaning up since 1024 /* These internal strings need cleaning up since
662 the post-processing may have been interrupted 1025 the post-processing may have been interrupted
663 at any stage */ 1026 at any stage */
664 if (pp->content_type != NULL) 1027 pp->have = NE_none;
665 free (pp->content_type); 1028 free_unmarked(pp);
666 if (pp->content_disposition != NULL) 1029 if (pp->nested_boundary != NULL)
667 free (pp->content_disposition); 1030 free(pp->nested_boundary);
668 if (pp->filename != NULL)
669 free (pp->filename);
670 if (pp->transfer_encoding != NULL)
671 free (pp->transfer_encoding);
672 free (pp); 1031 free (pp);
673} 1032}
674 1033
diff --git a/src/daemon/postprocessor_test.c b/src/daemon/postprocessor_test.c
index 46f2463a..a931b300 100644
--- a/src/daemon/postprocessor_test.c
+++ b/src/daemon/postprocessor_test.c
@@ -85,9 +85,11 @@ value_checker(void * cls,
85 int * want_off = cls; 85 int * want_off = cls;
86 int idx = *want_off; 86 int idx = *want_off;
87 87
88#if 0
88 fprintf(stderr, 89 fprintf(stderr,
89 "VC: `%s' `%s' `%s' `%s' `%.*s'\n", 90 "VC: `%s' `%s' `%s' `%s' `%.*s'\n",
90 key, filename, content_type, transfer_encoding, size, data); 91 key, filename, content_type, transfer_encoding, size, data);
92#endif
91 if (size == 0) 93 if (size == 0)
92 return MHD_YES; 94 return MHD_YES;
93 if ( (idx < 0) || 95 if ( (idx < 0) ||
@@ -213,8 +215,9 @@ int
213main (int argc, char *const *argv) 215main (int argc, char *const *argv)
214{ 216{
215 unsigned int errorCount = 0; 217 unsigned int errorCount = 0;
218
216 errorCount += test_urlencoding(); 219 errorCount += test_urlencoding();
217 errorCount += test_multipart(); 220 errorCount += test_multipart();
218 errorCount += test_nested_multipart(); 221 errorCount += test_nested_multipart();
219 if (errorCount != 0) 222 if (errorCount != 0)
220 fprintf (stderr, "Error (code: %u)\n", errorCount); 223 fprintf (stderr, "Error (code: %u)\n", errorCount);