aboutsummaryrefslogtreecommitdiff
path: root/src/daemon/postprocessor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/postprocessor.c')
-rw-r--r--src/daemon/postprocessor.c675
1 files changed, 675 insertions, 0 deletions
diff --git a/src/daemon/postprocessor.c b/src/daemon/postprocessor.c
new file mode 100644
index 00000000..47d2efa6
--- /dev/null
+++ b/src/daemon/postprocessor.c
@@ -0,0 +1,675 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20/**
21 * @file postprocessor.c
22 * @brief Methods for parsing POST data
23 * @author Christian Grothoff
24 */
25
26#include "internal.h"
27
28/**
29 * States in the PP parser's state machine.
30 */
31enum PP_State
32{
33
34 PP_Init = 0,
35
36 PP_HaveKey = 1,
37
38 PP_ExpectNewLine = 2,
39
40 PP_ExpectNewLineR = 3,
41
42 PP_ExpectNewLineN = 4,
43
44 PP_Headers = 5,
45
46 PP_SkipRNRN = 6,
47
48 PP_SkipNRN = 7,
49
50 PP_SkipRN = 8,
51
52 PP_SkipN = 9,
53
54 PP_ValueToBoundary = 10,
55
56 PP_FinalDash = 11,
57
58 PP_Error = 9999,
59
60
61};
62
63/**
64 * Internal state of the post-processor.
65 */
66struct MHD_PostProcessor
67{
68
69 /**
70 * The connection for which we are doing
71 * POST processing.
72 */
73 struct MHD_Connection *connection;
74
75 /**
76 * Function to call with POST data.
77 */
78 MHD_PostDataIterator ikvi;
79
80 /**
81 * Extra argument to ikvi.
82 */
83 void *cls;
84
85 /**
86 * Encoding as given by the headers of the
87 * connection.
88 */
89 const char *encoding;
90
91 /**
92 * Pointer to the name given in disposition.
93 */
94 char *content_disposition;
95
96 /**
97 * Pointer to the (current) content type.
98 */
99 char *content_type;
100
101 /**
102 * Pointer to the (current) filename.
103 */
104 char *filename;
105
106 /**
107 * Pointer to the (current) encoding.
108 */
109 char *transfer_encoding;
110
111 /**
112 * Unprocessed value bytes due to escape
113 * sequences (URL-encoding only).
114 */
115 char xbuf[8];
116
117 /**
118 * Size of our buffer for the key.
119 */
120 unsigned int buffer_size;
121
122 /**
123 * Current position in the key buffer.
124 */
125 unsigned int buffer_pos;
126
127 /**
128 * Current position in xbuf.
129 */
130 unsigned int xbuf_pos;
131
132 /**
133 * Current offset in the value being processed.
134 */
135 unsigned int value_offset;
136
137 /**
138 * State of the parser.
139 */
140 enum PP_State state;
141
142};
143
144
145/**
146 * Create a PostProcessor.
147 *
148 * A PostProcessor can be used to (incrementally)
149 * parse the data portion of a POST request.
150 *
151 * @param connection the connection on which the POST is
152 * happening (used to determine the POST format)
153 * @param buffer_size maximum number of bytes to use for
154 * internal buffering (used only for the parsing,
155 * specifically the parsing of the keys). A
156 * tiny value (256-1024) should be sufficient.
157 * Do NOT use 0.
158 * @param ikvi iterator to be called with the parsed data
159 * @param cls first argument to ikvi
160 * @return NULL on error (out of memory, unsupported encoding),
161 * otherwise a PP handle
162 */
163struct MHD_PostProcessor *
164MHD_create_post_processor (struct MHD_Connection *connection,
165 unsigned int buffer_size,
166 MHD_PostDataIterator ikvi, void *cls)
167{
168 struct MHD_PostProcessor *ret;
169 const char *encoding;
170
171 if ((buffer_size < 256) || (connection == NULL) || (ikvi == NULL))
172 abort ();
173 encoding = MHD_lookup_connection_value (connection,
174 MHD_HEADER_KIND,
175 MHD_HTTP_HEADER_CONTENT_TYPE);
176 if (encoding == NULL)
177 return NULL;
178 if ((0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
179 encoding)) &&
180 (0 != strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding)))
181 return NULL;
182 ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size + 1);
183 if (ret == NULL)
184 return NULL;
185 memset (ret, 0, sizeof (struct MHD_PostProcessor));
186 ret->connection = connection;
187 ret->ikvi = ikvi;
188 ret->cls = cls;
189 ret->encoding = encoding;
190 ret->buffer_size = buffer_size;
191 ret->state = PP_Init;
192 return ret;
193}
194
195/**
196 * On-stack buffer that we use for un-escaping of the value.
197 */
198#define XBUF_SIZE 1024
199
200/**
201 * Process url-encoded POST data.
202 */
203static int
204post_process_urlencoded (struct MHD_PostProcessor *pp,
205 const char *post_data, unsigned int post_data_len)
206{
207 unsigned int equals;
208 unsigned int amper;
209 unsigned int poff;
210 unsigned int xoff;
211 unsigned int delta;
212 char *buf;
213 char xbuf[XBUF_SIZE + 1];
214
215 buf = (char *) &pp[1];
216 poff = 0;
217 while (poff < post_data_len)
218 {
219 switch (pp->state)
220 {
221 case PP_Init:
222 equals = 0;
223 while ((equals + poff < post_data_len) &&
224 (post_data[equals + poff] != '='))
225 equals++;
226 if (equals + pp->buffer_pos > pp->buffer_size)
227 {
228 pp->state = PP_Error; /* out of memory */
229 return MHD_NO;
230 }
231 memcpy (&buf[pp->buffer_pos], &post_data[poff], equals);
232 pp->buffer_pos += equals;
233 if (equals + poff == post_data_len)
234 return MHD_YES; /* no '=' yet */
235 buf[pp->buffer_pos] = '\0'; /* 0-terminate key */
236 pp->buffer_pos = 0; /* reset for next key */
237 MHD_http_unescape (buf);
238 poff += equals + 1;
239 pp->state = PP_HaveKey;
240 pp->value_offset = 0;
241 break;
242 case PP_HaveKey:
243 /* obtain rest of value from previous iteration */
244 memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
245 xoff = pp->xbuf_pos;
246 pp->xbuf_pos = 0;
247
248 /* find last position in input buffer that is part of the value */
249 amper = 0;
250 while ((amper + poff < post_data_len) &&
251 (post_data[amper + poff] != '&') &&
252 (post_data[amper + poff] != '\n') &&
253 (post_data[amper + poff] != '\r'))
254 amper++;
255
256 /* compute delta, the maximum number of bytes that we will be able to
257 process right now (either amper-limited of xbuf-size limited) */
258 delta = amper;
259 if (delta > XBUF_SIZE - xoff)
260 delta = XBUF_SIZE - xoff;
261
262 /* move input into processing buffer */
263 memcpy (&xbuf[xoff], &post_data[poff], delta);
264 xoff += delta;
265 poff += delta;
266
267 /* find if escape sequence is at the end of the processing buffer;
268 if so, exclude those from processing (reduce delta to point at
269 end of processed region) */
270 delta = xoff;
271 if ((delta > 0) && (xbuf[delta - 1] == '%'))
272 delta--;
273 else if ((delta > 1) && (xbuf[delta - 2] == '%'))
274 delta -= 2;
275
276 /* if we have an incomplete escape sequence, save it to
277 pp->xbuf for later */
278 if (delta < xoff)
279 {
280 memcpy (pp->xbuf, &xbuf[delta], xoff - delta);
281 pp->xbuf_pos = xoff - delta;
282 xoff = delta;
283 }
284
285 /* If we have nothing to do (delta == 0) and
286 not just because the value is empty (are
287 waiting for more data), go for next iteration */
288 if ((xoff == 0) && (poff == post_data_len))
289 continue;
290
291 /* unescape */
292 xbuf[xoff] = '\0'; /* 0-terminate in preparation */
293 MHD_http_unescape (xbuf);
294
295 /* finally: call application! */
296 pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1], /* key */
297 NULL, NULL, NULL, xbuf, pp->value_offset, xoff);
298 pp->value_offset += xoff;
299
300 /* are we done with the value? */
301 if (poff < post_data_len)
302 {
303 /* we found the end of the value! */
304 pp->state = PP_Init;
305 poff++; /* skip '&' or new-lines */
306
307 if ((post_data[poff - 1] == '\n') ||
308 (post_data[poff - 1] == '\r'))
309 pp->state = PP_ExpectNewLine;
310 }
311 break;
312 case PP_ExpectNewLine:
313 if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
314 {
315 poff++;
316 /* we are done, report error if we receive any more... */
317 pp->state = PP_Error;
318 return MHD_YES;
319 }
320 return MHD_NO;
321 case PP_Error:
322 return MHD_NO;
323 default:
324 abort (); /* should never happen! */
325 }
326 }
327 return MHD_YES;
328}
329
330/**
331 * If the given line matches the prefix, strdup the
332 * rest of the line into the suffix ptr.
333 *
334 * @return MHD_YES if there was a match, MHD_NO if not
335 */
336static int
337try_match_header (const char *prefix, char *line, char **suffix)
338{
339 if (0 == strncasecmp (prefix, line, strlen (prefix)))
340 {
341 *suffix = strdup (&line[strlen (prefix)]);
342 return MHD_YES;
343 }
344 return MHD_NO;
345}
346
347/**
348 * Decode multipart POST data.
349 *
350 * TODO: If the content-type is multipart/mixed, we do not do anything
351 * special. However, we should probably break the individual values
352 * apart and give them to the callback individually (will require some
353 * additional states & state).
354 *
355 * TODO: this code has never been tested...
356 *
357 * See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4
358 */
359static int
360post_process_multipart (struct MHD_PostProcessor *pp,
361 const char *post_data, unsigned int post_data_len)
362{
363 char *buf;
364 const char *boundary;
365 unsigned int max;
366 unsigned int ioff;
367 unsigned int poff;
368 unsigned int newline;
369 unsigned int endquote;
370 size_t blen;
371
372 buf = (char *) &pp[1];
373 ioff = 0;
374 poff = 0;
375 boundary =
376 &pp->encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
377 if (NULL != strstr (boundary, "boundary="))
378 boundary = strstr (boundary, "boundary=") + strlen ("boundary=");
379 else
380 return MHD_NO; /* failed to determine boundary */
381 blen = strlen (boundary);
382 if (blen * 2 + 2 > pp->buffer_size)
383 return MHD_NO; /* (will be) out of memory */
384 while ((poff < post_data_len) || (pp->buffer_pos > ioff))
385 {
386 /* first, move data to our internal buffer */
387 max = pp->buffer_size - pp->buffer_pos;
388 if ((max < ioff) && (max < post_data_len))
389 {
390 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
391 pp->buffer_pos -= ioff;
392 ioff = 0;
393 max = pp->buffer_size - pp->buffer_pos;
394 }
395 if (max > post_data_len)
396 max = post_data_len;
397 memcpy (&buf[pp->buffer_pos], post_data, max);
398 poff += max;
399 pp->buffer_pos += max;
400
401 switch (pp->state)
402 {
403 case PP_Init:
404 /* we're looking for the boundary */
405 if (pp->buffer_pos < 2 + blen + ioff)
406 goto END;
407 if ((0 != memcmp ("--", &buf[ioff], 2)) ||
408 (0 != memcmp (&buf[ioff + 2], boundary, blen)))
409 return MHD_NO; /* expected boundary */
410
411 /* remove boundary from buffer */
412 ioff += 2 + blen;
413
414 /* next: start with headers */
415 pp->state = PP_ExpectNewLineR;
416 break;
417 case PP_ExpectNewLineR:
418 if (buf[ioff] == '-')
419 {
420 /* last boundary ends with "--" */
421 ioff++;
422 pp->state = PP_FinalDash;
423 break;
424 }
425 if (buf[ioff] == '\r')
426 {
427 ioff++;
428 pp->state = PP_ExpectNewLineN;
429 break;
430 }
431 /* fall through! */
432 case PP_ExpectNewLineN:
433 if (buf[ioff] == '\n')
434 {
435 ioff++;
436 pp->state = PP_Headers;
437 break;
438 }
439 return MHD_NO;
440 case PP_Headers:
441 newline = 0;
442 while ((newline + ioff < pp->buffer_pos) &&
443 (buf[newline + ioff] != '\r') &&
444 (buf[newline + ioff] != '\n'))
445 newline++;
446 if (newline == pp->buffer_size)
447 return MHD_NO; /* out of memory */
448 if (newline + ioff == pp->buffer_pos)
449 {
450 /* try to make more room */
451 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
452 pp->buffer_pos -= ioff;
453 ioff = 0;
454 break;
455 }
456 if (newline == 0)
457 {
458 pp->state = PP_SkipRNRN;
459 break;
460 }
461 buf[ioff + newline] = '\0';
462 if ((MHD_YES
463 == try_match_header ("Content-Disposition: form-data; name=\"",
464 &buf[ioff],
465 &pp->content_disposition)) &&
466 (pp->content_disposition != NULL) &&
467 (0 < strlen (pp->content_disposition)))
468 {
469 /* find end-quote; then check if we also have a filename! */
470 endquote = 0;
471 while ((pp->content_disposition[endquote] != '\"') &&
472 (pp->content_disposition[endquote] != '\0'))
473 endquote++;
474 pp->content_disposition[endquote++] = '\0'; /* remove end-quote */
475 if ((MHD_YES
476 == try_match_header (" filename=",
477 &pp->content_disposition[endquote],
478 &pp->filename)) &&
479 (pp->filename != NULL) && (0 < strlen (pp->filename)))
480 pp->filename[strlen (pp->filename) - 1] = '\0'; /* remove end-quote */
481 }
482 try_match_header ("Content-Type: ", &buf[ioff], &pp->content_type);
483 try_match_header ("Content-Transfer-Encoding: ",
484 &buf[ioff], &pp->transfer_encoding);
485 break;
486 case PP_SkipRNRN:
487 if (buf[ioff] == '\r')
488 {
489 ioff++;
490 pp->state = PP_SkipNRN;
491 break;
492 }
493 /* fall through! */
494 case PP_SkipNRN:
495 if (buf[ioff] == '\n')
496 {
497 ioff++;
498 pp->state = PP_SkipRN;
499 break;
500 }
501 return MHD_NO; /* parse error */
502 case PP_SkipRN:
503 if (buf[ioff] == '\r')
504 {
505 ioff++;
506 pp->state = PP_SkipN;
507 break;
508 }
509 /* fall through! */
510 case PP_SkipN:
511 if (buf[ioff] == '\n')
512 {
513 ioff++;
514 pp->state = PP_ValueToBoundary;
515 pp->value_offset = 0;
516 break;
517 }
518 return MHD_NO; /* parse error */
519 case PP_ValueToBoundary:
520 /* all data in buf until the boundary
521 (\r\n--+boundary) is part of the value */
522 newline = 0;
523 while (1)
524 {
525 while ((newline + ioff + 4 < pp->buffer_pos) &&
526 (0 != memcmp ("\r\n--", &buf[newline + ioff], 4)))
527 newline++;
528 if (newline + blen + 4 > pp->buffer_size)
529 {
530 /* boundary not in sight --
531 process data, then make room for more! */
532 if (MHD_NO == pp->ikvi (pp->cls,
533 MHD_POSTDATA_KIND,
534 pp->content_disposition,
535 pp->filename,
536 pp->content_type,
537 pp->transfer_encoding,
538 &buf[ioff],
539 pp->value_offset, newline))
540 {
541 pp->state = PP_Error;
542 break;
543 }
544 pp->value_offset += newline;
545 ioff += newline;
546 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
547 pp->buffer_pos -= ioff;
548 break;
549 }
550 if (newline + blen + 4 < pp->buffer_pos)
551 {
552 /* can check for boundary right now! */
553 if (0 == memcmp (&buf[newline + ioff + 4], boundary, blen))
554 {
555 /* found: process data, then look for more */
556 if (MHD_NO == pp->ikvi (pp->cls,
557 MHD_POSTDATA_KIND,
558 pp->content_disposition,
559 pp->filename,
560 pp->content_type,
561 pp->transfer_encoding,
562 &buf[ioff],
563 pp->value_offset, newline))
564 {
565 pp->state = PP_Error;
566 break;
567 }
568
569 /* clean up! */
570 if (pp->content_type != NULL)
571 {
572 free (pp->content_type);
573 pp->content_type = NULL;
574 }
575 if (pp->content_disposition != NULL)
576 {
577 free (pp->content_disposition);
578 pp->content_disposition = NULL;
579 }
580 if (pp->filename != NULL)
581 {
582 free (pp->filename);
583 pp->filename = NULL;
584 }
585 if (pp->transfer_encoding != NULL)
586 {
587 free (pp->transfer_encoding);
588 pp->transfer_encoding = NULL;
589 }
590 pp->value_offset = 0;
591 ioff += newline + 2; /* skip data + new line */
592 pp->state = PP_Init;
593 break;
594 }
595 /* not the boundary, look further! */
596 newline += 4;
597 continue;
598 }
599
600
601 }
602 break;
603 case PP_FinalDash:
604 if (buf[ioff] == '-')
605 {
606 /* last boundary ends with "--" */
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:
615 abort (); /* should never happen! */
616
617 }
618 }
619END:
620 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
621 pp->buffer_pos -= ioff;
622 return MHD_YES;
623}
624
625/**
626 * Parse and process POST data.
627 * Call this function when POST data is available
628 * (usually during an MHD_AccessHandlerCallback)
629 * with the upload_data and upload_data_size.
630 * Whenever possible, this will then cause calls
631 * to the MHD_IncrementalKeyValueIterator.
632 *
633 * @param pp the post processor
634 * @param post_data post_data_len bytes of POST data
635 * @param post_data_len length of post_data
636 * @return MHD_YES on success, MHD_NO on error
637 * (out-of-memory, iterator aborted, parse error)
638 */
639int
640MHD_post_process (struct MHD_PostProcessor *pp,
641 const char *post_data, unsigned int post_data_len)
642{
643 if (post_data_len == 0)
644 return MHD_YES;
645 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, pp->encoding))
646 return post_process_urlencoded (pp, post_data, post_data_len);
647 if (0 ==
648 strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, pp->encoding,
649 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
650 return post_process_multipart (pp, post_data, post_data_len);
651 /* this should never be reached */
652 return MHD_NO;
653}
654
655/**
656 * Release PostProcessor resources.
657 */
658void
659MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
660{
661 /* These internal strings need cleaning up since
662 the post-processing may have been interrupted
663 at any stage */
664 if (pp->content_type != NULL)
665 free (pp->content_type);
666 if (pp->content_disposition != NULL)
667 free (pp->content_disposition);
668 if (pp->filename != NULL)
669 free (pp->filename);
670 if (pp->transfer_encoding != NULL)
671 free (pp->transfer_encoding);
672 free (pp);
673}
674
675/* end of postprocessor.c */