GNU libmicrohttpd  0.9.5
postprocessor.c
Go to the documentation of this file.
1 /*
2  This file is part of libmicrohttpd
3  (C) 2007, 2009 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 
26 #include "internal.h"
27 
31 #define XBUF_SIZE 1024
32 
37 {
38  /* general states */
42 
43  /* url encoding-states */
46 
47  /* post encoding-states */
52 
53  /* nested post-encoding states */
59 
60 };
61 
63 {
68 
73  RN_OptN = 1,
74 
79  RN_Full = 2,
80 
85  RN_Dash = 3,
86 
90  RN_Dash2 = 4,
91 };
92 
99 {
100  NE_none = 0,
105 };
106 
111 struct MHD_PostProcessor
112 {
113 
118  struct MHD_Connection *connection;
119 
124 
128  void *cls;
129 
134  const char *encoding;
135 
139  const char *boundary;
140 
144  char *nested_boundary;
145 
149  char *content_name;
150 
154  char *content_type;
155 
159  char *content_filename;
160 
164  char *content_transfer_encoding;
165 
170  char xbuf[8];
171 
175  size_t buffer_size;
176 
180  size_t buffer_pos;
181 
185  size_t xbuf_pos;
186 
190  uint64_t value_offset;
191 
195  size_t blen;
196 
200  size_t nlen;
201 
205  enum PP_State state;
206 
213  enum RN_State skip_rn;
214 
219  enum PP_State dash_state;
220 
225  enum NE_State have;
226 
227 };
228 
229 
248 struct MHD_PostProcessor *
250  size_t buffer_size,
251  MHD_PostDataIterator ikvi, void *cls)
252 {
253  struct MHD_PostProcessor *ret;
254  const char *encoding;
255  const char *boundary;
256  size_t blen;
257 
258  if ((buffer_size < 256) || (connection == NULL) || (ikvi == NULL))
259  mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
260  encoding = MHD_lookup_connection_value (connection,
263  if (encoding == NULL)
264  return NULL;
265  boundary = NULL;
266  if (0 != strncasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, encoding,
268  {
269  if (0 !=
270  strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding,
272  return NULL;
273  boundary =
274  &encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
275  /* Q: should this be "strcasestr"? */
276  boundary = strstr (boundary, "boundary=");
277  if (NULL == boundary)
278  return NULL; /* failed to determine boundary */
279  boundary += strlen ("boundary=");
280  blen = strlen (boundary);
281  if ((blen == 0) || (blen * 2 + 2 > buffer_size))
282  return NULL; /* (will be) out of memory or invalid boundary */
283  }
284  else
285  blen = 0;
286  ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size + 1);
287  if (ret == NULL)
288  return NULL;
289  memset (ret, 0, sizeof (struct MHD_PostProcessor) + buffer_size + 1);
290  ret->connection = connection;
291  ret->ikvi = ikvi;
292  ret->cls = cls;
293  ret->encoding = encoding;
294  ret->buffer_size = buffer_size;
295  ret->state = PP_Init;
296  ret->blen = blen;
297  ret->boundary = boundary;
298  ret->skip_rn = RN_Inactive;
299  return ret;
300 }
301 
305 static int
306 post_process_urlencoded (struct MHD_PostProcessor *pp,
307  const char *post_data,
308  size_t post_data_len)
309 {
310  size_t equals;
311  size_t amper;
312  size_t poff;
313  size_t xoff;
314  size_t delta;
315  int end_of_value_found;
316  char *buf;
317  char xbuf[XBUF_SIZE + 1];
318 
319  buf = (char *) &pp[1];
320  poff = 0;
321  while (poff < post_data_len)
322  {
323  switch (pp->state)
324  {
325  case PP_Error:
326  return MHD_NO;
327  case PP_Done:
328  /* did not expect to receive more data */
329  pp->state = PP_Error;
330  return MHD_NO;
331  case PP_Init:
332  equals = 0;
333  while ((equals + poff < post_data_len) &&
334  (post_data[equals + poff] != '='))
335  equals++;
336  if (equals + pp->buffer_pos > pp->buffer_size)
337  {
338  pp->state = PP_Error; /* out of memory */
339  return MHD_NO;
340  }
341  memcpy (&buf[pp->buffer_pos], &post_data[poff], equals);
342  pp->buffer_pos += equals;
343  if (equals + poff == post_data_len)
344  return MHD_YES; /* no '=' yet */
345  buf[pp->buffer_pos] = '\0'; /* 0-terminate key */
346  pp->buffer_pos = 0; /* reset for next key */
347  MHD_http_unescape (NULL, NULL, buf);
348  poff += equals + 1;
349  pp->state = PP_ProcessValue;
350  pp->value_offset = 0;
351  break;
352  case PP_ProcessValue:
353  /* obtain rest of value from previous iteration */
354  memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
355  xoff = pp->xbuf_pos;
356  pp->xbuf_pos = 0;
357 
358  /* find last position in input buffer that is part of the value */
359  amper = 0;
360  while ((amper + poff < post_data_len) &&
361  (amper < XBUF_SIZE) &&
362  (post_data[amper + poff] != '&') &&
363  (post_data[amper + poff] != '\n') &&
364  (post_data[amper + poff] != '\r'))
365  amper++;
366  end_of_value_found = ((amper + poff < post_data_len) &&
367  ((post_data[amper + poff] == '&') ||
368  (post_data[amper + poff] == '\n') ||
369  (post_data[amper + poff] == '\r')));
370  /* compute delta, the maximum number of bytes that we will be able to
371  process right now (either amper-limited of xbuf-size limited) */
372  delta = amper;
373  if (delta > XBUF_SIZE - xoff)
374  delta = XBUF_SIZE - xoff;
375 
376  /* move input into processing buffer */
377  memcpy (&xbuf[xoff], &post_data[poff], delta);
378  xoff += delta;
379  poff += delta;
380 
381  /* find if escape sequence is at the end of the processing buffer;
382  if so, exclude those from processing (reduce delta to point at
383  end of processed region) */
384  delta = xoff;
385  if ((delta > 0) && (xbuf[delta - 1] == '%'))
386  delta--;
387  else if ((delta > 1) && (xbuf[delta - 2] == '%'))
388  delta -= 2;
389 
390  /* if we have an incomplete escape sequence, save it to
391  pp->xbuf for later */
392  if (delta < xoff)
393  {
394  memcpy (pp->xbuf, &xbuf[delta], xoff - delta);
395  pp->xbuf_pos = xoff - delta;
396  xoff = delta;
397  }
398 
399  /* If we have nothing to do (delta == 0) and
400  not just because the value is empty (are
401  waiting for more data), go for next iteration */
402  if ((xoff == 0) && (poff == post_data_len))
403  continue;
404 
405  /* unescape */
406  xbuf[xoff] = '\0'; /* 0-terminate in preparation */
407  xoff = MHD_http_unescape (NULL, NULL, xbuf);
408  /* finally: call application! */
409  if (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1], /* key */
410  NULL, NULL, NULL, xbuf, pp->value_offset,
411  xoff))
412  {
413  pp->state = PP_Error;
414  return MHD_NO;
415  }
416  pp->value_offset += xoff;
417 
418  /* are we done with the value? */
419  if (end_of_value_found)
420  {
421  /* we found the end of the value! */
422  if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
423  {
424  pp->state = PP_ExpectNewLine;
425  }
426  else
427  {
428  poff++; /* skip '&' */
429  pp->state = PP_Init;
430  }
431  }
432  break;
433  case PP_ExpectNewLine:
434  if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
435  {
436  poff++;
437  /* we are done, report error if we receive any more... */
438  pp->state = PP_Done;
439  return MHD_YES;
440  }
441  return MHD_NO;
442  default:
443  mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /* should never happen! */
444  }
445  }
446  return MHD_YES;
447 }
448 
455 static int
456 try_match_header (const char *prefix, char *line, char **suffix)
457 {
458  if (NULL != *suffix)
459  return MHD_NO;
460  while (*line != 0)
461  {
462  if (0 == strncasecmp (prefix, line, strlen (prefix)))
463  {
464  *suffix = strdup (&line[strlen (prefix)]);
465  return MHD_YES;
466  }
467  ++line;
468  }
469  return MHD_NO;
470 }
471 
472 static int
473 find_boundary (struct MHD_PostProcessor *pp,
474  const char *boundary,
475  size_t blen,
476  size_t *ioffptr,
477  enum PP_State next_state, enum PP_State next_dash_state)
478 {
479  char *buf = (char *) &pp[1];
480 
481  if (pp->buffer_pos < 2 + blen)
482  {
483  if (pp->buffer_pos == pp->buffer_size)
484  pp->state = PP_Error; /* out of memory */
485  return MHD_NO; /* not enough data */
486  }
487  if ((0 != memcmp ("--", buf, 2)) || (0 != memcmp (&buf[2], boundary, blen)))
488  {
489  pp->state = PP_Error;
490  return MHD_NO; /* expected boundary */
491  }
492  /* remove boundary from buffer */
493  (*ioffptr) += 2 + blen;
494  /* next: start with headers */
495  pp->skip_rn = RN_Dash;
496  pp->state = next_state;
497  pp->dash_state = next_dash_state;
498  return MHD_YES;
499 }
500 
509 static void
510 try_get_value (const char *buf, const char *key, char **destination)
511 {
512  const char *spos;
513  const char *bpos;
514  const char *endv;
515  size_t klen;
516  size_t vlen;
517 
518  if (NULL != *destination)
519  return;
520  bpos = buf;
521  klen = strlen (key);
522  while (NULL != (spos = strstr (bpos, key)))
523  {
524  if ((spos[klen] != '=') || ((spos != buf) && (spos[-1] != ' ')))
525  {
526  /* no match */
527  bpos = spos + 1;
528  continue;
529  }
530  if (spos[klen + 1] != '"')
531  return; /* not quoted */
532  if (NULL == (endv = strstr (&spos[klen + 2], "\"")))
533  return; /* no end-quote */
534  vlen = endv - spos - klen - 1;
535  *destination = malloc (vlen);
536  if (NULL == *destination)
537  return; /* out of memory */
538  (*destination)[vlen - 1] = '\0';
539  memcpy (*destination, &spos[klen + 2], vlen - 1);
540  return; /* success */
541  }
542 }
543 
556 static int
557 process_multipart_headers (struct MHD_PostProcessor *pp,
558  size_t *ioffptr, enum PP_State next_state)
559 {
560  char *buf = (char *) &pp[1];
561  size_t newline;
562 
563  newline = 0;
564  while ((newline < pp->buffer_pos) &&
565  (buf[newline] != '\r') && (buf[newline] != '\n'))
566  newline++;
567  if (newline == pp->buffer_size)
568  {
569  pp->state = PP_Error;
570  return MHD_NO; /* out of memory */
571  }
572  if (newline == pp->buffer_pos)
573  return MHD_NO; /* will need more data */
574  if (newline == 0)
575  {
576  /* empty line - end of headers */
577  pp->skip_rn = RN_Full;
578  pp->state = next_state;
579  return MHD_YES;
580  }
581  /* got an actual header */
582  if (buf[newline] == '\r')
583  pp->skip_rn = RN_OptN;
584  buf[newline] = '\0';
585  if (0 == strncasecmp ("Content-disposition: ",
586  buf, strlen ("Content-disposition: ")))
587  {
588  try_get_value (&buf[strlen ("Content-disposition: ")],
589  "name", &pp->content_name);
590  try_get_value (&buf[strlen ("Content-disposition: ")],
591  "filename", &pp->content_filename);
592  }
593  else
594  {
595  try_match_header ("Content-type: ", buf, &pp->content_type);
596  try_match_header ("Content-Transfer-Encoding: ",
597  buf, &pp->content_transfer_encoding);
598  }
599  (*ioffptr) += newline + 1;
600  return MHD_YES;
601 }
602 
617 static int
618 process_value_to_boundary (struct MHD_PostProcessor *pp,
619  size_t *ioffptr,
620  const char *boundary,
621  size_t blen,
622  enum PP_State next_state,
623  enum PP_State next_dash_state)
624 {
625  char *buf = (char *) &pp[1];
626  size_t newline;
627 
628  /* all data in buf until the boundary
629  (\r\n--+boundary) is part of the value */
630  newline = 0;
631  while (1)
632  {
633  while ((newline + 4 < pp->buffer_pos) &&
634  (0 != memcmp ("\r\n--", &buf[newline], 4)))
635  newline++;
636  if (newline + pp->blen + 4 <= pp->buffer_pos)
637  {
638  /* can check boundary */
639  if (0 != memcmp (&buf[newline + 4], boundary, pp->blen))
640  {
641  /* no boundary, "\r\n--" is part of content, skip */
642  newline += 4;
643  continue;
644  }
645  else
646  {
647  /* boundary found, process until newline then
648  skip boundary and go back to init */
649  pp->skip_rn = RN_Dash;
650  pp->state = next_state;
651  pp->dash_state = next_dash_state;
652  (*ioffptr) += pp->blen + 4; /* skip boundary as well */
653  break;
654  }
655  }
656  else
657  {
658  /* cannot check for boundary, process content that
659  we have and check again later; except, if we have
660  no content, abort (out of memory) */
661  if ((newline == 0) && (pp->buffer_pos == pp->buffer_size))
662  {
663  pp->state = PP_Error;
664  return MHD_NO;
665  }
666  break;
667  }
668  }
669  /* newline is either at beginning of boundary or
670  at least at the last character that we are sure
671  is not part of the boundary */
672  if (MHD_NO == pp->ikvi (pp->cls,
674  pp->content_name,
675  pp->content_filename,
676  pp->content_type,
677  pp->content_transfer_encoding,
678  buf, 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 
688 static void
689 free_unmarked (struct MHD_PostProcessor *pp)
690 {
691  if ((pp->content_name != NULL) && (0 == (pp->have & NE_content_name)))
692  {
693  free (pp->content_name);
694  pp->content_name = NULL;
695  }
696  if ((pp->content_type != NULL) && (0 == (pp->have & NE_content_type)))
697  {
698  free (pp->content_type);
699  pp->content_type = NULL;
700  }
701  if ((pp->content_filename != NULL) &&
702  (0 == (pp->have & NE_content_filename)))
703  {
704  free (pp->content_filename);
705  pp->content_filename = NULL;
706  }
707  if ((pp->content_transfer_encoding != NULL) &&
708  (0 == (pp->have & NE_content_transfer_encoding)))
709  {
710  free (pp->content_transfer_encoding);
711  pp->content_transfer_encoding = NULL;
712  }
713 }
714 
718 static int
719 post_process_multipart (struct MHD_PostProcessor *pp,
720  const char *post_data,
721  size_t post_data_len)
722 {
723  char *buf;
724  size_t max;
725  size_t ioff;
726  size_t poff;
727  int state_changed;
728 
729  buf = (char *) &pp[1];
730  ioff = 0;
731  poff = 0;
732  state_changed = 1;
733  while ((poff < post_data_len) ||
734  ((pp->buffer_pos > 0) && (state_changed != 0)))
735  {
736  /* first, move as much input data
737  as possible to our internal buffer */
738  max = pp->buffer_size - pp->buffer_pos;
739  if (max > post_data_len - poff)
740  max = post_data_len - poff;
741  memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
742  poff += max;
743  pp->buffer_pos += max;
744  if ((max == 0) && (state_changed == 0) && (poff < post_data_len))
745  {
746  pp->state = PP_Error;
747  return MHD_NO; /* out of memory */
748  }
749  state_changed = 0;
750 
751  /* first state machine for '\r'-'\n' and '--' handling */
752  switch (pp->skip_rn)
753  {
754  case RN_Inactive:
755  break;
756  case RN_OptN:
757  if (buf[0] == '\n')
758  {
759  ioff++;
760  pp->skip_rn = RN_Inactive;
761  goto AGAIN;
762  }
763  /* fall-through! */
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) && (buf[1] == '\n'))
777  {
778  pp->skip_rn = RN_Inactive;
779  ioff += 2;
780  }
781  else
782  {
783  pp->skip_rn = RN_OptN;
784  ioff++;
785  }
786  goto AGAIN;
787  }
788  if (buf[0] == '\n')
789  {
790  ioff++;
791  pp->skip_rn = RN_Inactive;
792  goto AGAIN;
793  }
794  pp->skip_rn = RN_Inactive;
795  pp->state = PP_Error;
796  return MHD_NO; /* no '\r\n' */
797  case RN_Dash2:
798  if (buf[0] == '-')
799  {
800  ioff++;
801  pp->skip_rn = RN_Full;
802  pp->state = pp->dash_state;
803  goto AGAIN;
804  }
805  pp->state = PP_Error;
806  break;
807  }
808 
809  /* main state engine */
810  switch (pp->state)
811  {
812  case PP_Error:
813  return MHD_NO;
814  case PP_Done:
815  /* did not expect to receive more data */
816  pp->state = PP_Error;
817  return MHD_NO;
818  case PP_Init:
819  if (MHD_NO == find_boundary (pp,
820  pp->boundary,
821  pp->blen,
822  &ioff,
824  {
825  if (pp->state == PP_Error)
826  return MHD_NO;
827  goto END;
828  }
829  break;
831  if (MHD_NO ==
833  {
834  if (pp->state == PP_Error)
835  return MHD_NO;
836  else
837  goto END;
838  }
839  state_changed = 1;
840  break;
842  if ((pp->content_type != NULL) &&
843  (0 == strncasecmp (pp->content_type,
844  "multipart/mixed",
845  strlen ("multipart/mixed"))))
846  {
847  pp->nested_boundary = strstr (pp->content_type, "boundary=");
848  if (pp->nested_boundary == NULL)
849  {
850  pp->state = PP_Error;
851  return MHD_NO;
852  }
853  pp->nested_boundary =
854  strdup (&pp->nested_boundary[strlen ("boundary=")]);
855  if (pp->nested_boundary == NULL)
856  {
857  /* out of memory */
858  pp->state = PP_Error;
859  return MHD_NO;
860  }
861  /* free old content type, we will need that field
862  for the content type of the nested elements */
863  free (pp->content_type);
864  pp->content_type = NULL;
865  pp->nlen = strlen (pp->nested_boundary);
866  pp->state = PP_Nested_Init;
867  state_changed = 1;
868  break;
869  }
870  pp->state = PP_ProcessValueToBoundary;
871  pp->value_offset = 0;
872  state_changed = 1;
873  break;
876  &ioff,
877  pp->boundary,
878  pp->blen,
880  PP_Done))
881  {
882  if (pp->state == PP_Error)
883  return MHD_NO;
884  break;
885  }
886  break;
887  case PP_PerformCleanup:
888  /* clean up state of one multipart form-data element! */
889  pp->have = NE_none;
890  free_unmarked (pp);
891  if (pp->nested_boundary != NULL)
892  {
893  free (pp->nested_boundary);
894  pp->nested_boundary = NULL;
895  }
896  pp->state = PP_ProcessEntryHeaders;
897  state_changed = 1;
898  break;
899  case PP_Nested_Init:
900  if (pp->nested_boundary == NULL)
901  {
902  pp->state = PP_Error;
903  return MHD_NO;
904  }
905  if (MHD_NO == find_boundary (pp,
906  pp->nested_boundary,
907  pp->nlen,
908  &ioff,
910  PP_Init /* or PP_Error? */ ))
911  {
912  if (pp->state == PP_Error)
913  return MHD_NO;
914  goto END;
915  }
916  break;
918  /* remember what headers were given
919  globally */
920  pp->have = NE_none;
921  if (pp->content_name != NULL)
922  pp->have |= NE_content_name;
923  if (pp->content_type != NULL)
924  pp->have |= NE_content_type;
925  if (pp->content_filename != NULL)
926  pp->have |= NE_content_filename;
927  if (pp->content_transfer_encoding != NULL)
928  pp->have |= NE_content_transfer_encoding;
929  pp->state = PP_Nested_ProcessEntryHeaders;
930  state_changed = 1;
931  break;
933  pp->value_offset = 0;
934  if (MHD_NO ==
935  process_multipart_headers (pp, &ioff,
937  {
938  if (pp->state == PP_Error)
939  return MHD_NO;
940  else
941  goto END;
942  }
943  state_changed = 1;
944  break;
947  &ioff,
948  pp->nested_boundary,
949  pp->nlen,
951  PP_Init))
952  {
953  if (pp->state == PP_Error)
954  return MHD_NO;
955  break;
956  }
957  break;
959  free_unmarked (pp);
960  pp->state = PP_Nested_ProcessEntryHeaders;
961  state_changed = 1;
962  break;
963  default:
964  mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /* should never happen! */
965  }
966  AGAIN:
967  if (ioff > 0)
968  {
969  memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
970  pp->buffer_pos -= ioff;
971  ioff = 0;
972  state_changed = 1;
973  }
974  }
975 END:
976  if (ioff != 0)
977  {
978  memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
979  pp->buffer_pos -= ioff;
980  }
981  if (poff < post_data_len)
982  {
983  pp->state = PP_Error;
984  return MHD_NO; /* serious error */
985  }
986  return MHD_YES;
987 }
988 
1003 int
1004 MHD_post_process (struct MHD_PostProcessor *pp,
1005  const char *post_data, size_t post_data_len)
1006 {
1007  if (post_data_len == 0)
1008  return MHD_YES;
1009  if (pp == NULL)
1010  return MHD_NO;
1011  if (0 == strncasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, pp->encoding,
1013  return post_process_urlencoded (pp, post_data, post_data_len);
1014  if (0 ==
1015  strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, pp->encoding,
1017  return post_process_multipart (pp, post_data, post_data_len);
1018  /* this should never be reached */
1019  return MHD_NO;
1020 }
1021 
1025 int
1026 MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
1027 {
1028  int ret;
1029 
1030  /* These internal strings need cleaning up since
1031  the post-processing may have been interrupted
1032  at any stage */
1033  if ((pp->xbuf_pos > 0) || (pp->state != PP_Done))
1034  ret = MHD_NO;
1035  else
1036  ret = MHD_YES;
1037  pp->have = NE_none;
1038  free_unmarked (pp);
1039  if (pp->nested_boundary != NULL)
1040  free (pp->nested_boundary);
1041  free (pp);
1042  return ret;
1043 }
1044 
1045 /* end of postprocessor.c */