GNU libmicrohttpd  0.9.73
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 
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 #include "mhd_str.h"
28 #include "mhd_compat.h"
29 #include "mhd_assert.h"
30 
36 #define XBUF_SIZE 512
37 
42 {
43  /* general states */
48 
49  /* url encoding-states */
53 
54  /* post encoding-states */
59 
60  /* nested post-encoding states */
66 
67 };
68 
69 
71 {
76 
81  RN_OptN = 1,
82 
87  RN_Full = 2,
88 
93  RN_Dash = 3,
94 
98  RN_Dash2 = 4
99 };
100 
101 
108 {
109  NE_none = 0,
114 };
115 
116 
121 struct MHD_PostProcessor
122 {
123 
128  struct MHD_Connection *connection;
129 
134 
138  void *cls;
139 
143  const char *encoding;
144 
148  const char *boundary;
149 
153  char *nested_boundary;
154 
158  char *content_name;
159 
163  char *content_type;
164 
168  char *content_filename;
169 
173  char *content_transfer_encoding;
174 
178  char xbuf[2];
179 
183  size_t buffer_size;
184 
188  size_t buffer_pos;
189 
193  size_t xbuf_pos;
194 
198  uint64_t value_offset;
199 
203  size_t blen;
204 
208  size_t nlen;
209 
218  bool must_ikvi;
219 
224  bool must_unescape_key;
225 
229  enum PP_State state;
230 
237  enum RN_State skip_rn;
238 
243  enum PP_State dash_state;
244 
249  enum NE_State have;
250 
251 };
252 
253 
254 struct MHD_PostProcessor *
256  size_t buffer_size,
258  void *iter_cls)
259 {
260  struct MHD_PostProcessor *ret;
261  const char *encoding;
262  const char *boundary;
263  size_t blen;
264 
265  if ( (buffer_size < 256) ||
266  (NULL == connection) ||
267  (NULL == iter))
269  __FILE__,
270  __LINE__,
271  NULL);
272  if (MHD_NO == MHD_lookup_connection_value_n (connection,
277  &encoding,
278  NULL))
279  return NULL;
280  boundary = NULL;
282  encoding,
285  {
287  encoding,
290  return NULL;
291  boundary =
293  /* Q: should this be "strcasestr"? */
294  boundary = strstr (boundary, "boundary=");
295  if (NULL == boundary)
296  return NULL; /* failed to determine boundary */
297  boundary += MHD_STATICSTR_LEN_ ("boundary=");
298  blen = strlen (boundary);
299  if ( (blen == 0) ||
300  (blen * 2 + 2 > buffer_size) )
301  return NULL; /* (will be) out of memory or invalid boundary */
302  if ( (boundary[0] == '"') &&
303  (boundary[blen - 1] == '"') )
304  {
305  /* remove enclosing quotes */
306  ++boundary;
307  blen -= 2;
308  }
309  }
310  else
311  blen = 0;
312  buffer_size += 4; /* round up to get nice block sizes despite boundary search */
313 
314  /* add +1 to ensure we ALWAYS have a zero-termination at the end */
315  if (NULL == (ret = MHD_calloc_ (1, sizeof (struct MHD_PostProcessor)
316  + buffer_size + 1)))
317  return NULL;
318  ret->connection = connection;
319  ret->ikvi = iter;
320  ret->cls = iter_cls;
321  ret->encoding = encoding;
322  ret->buffer_size = buffer_size;
323  ret->state = PP_Init;
324  ret->blen = blen;
325  ret->boundary = boundary;
326  ret->skip_rn = RN_Inactive;
327  return ret;
328 }
329 
330 
348 static void
349 process_value (struct MHD_PostProcessor *pp,
350  const char *value_start,
351  const char *value_end,
352  const char *last_escape)
353 {
354  char xbuf[XBUF_SIZE + 1];
355  size_t xoff;
356 
357  mhd_assert (pp->xbuf_pos < sizeof (xbuf));
358  /* move remaining input from previous round into processing buffer */
359  memcpy (xbuf,
360  pp->xbuf,
361  pp->xbuf_pos);
362  xoff = pp->xbuf_pos;
363  pp->xbuf_pos = 0;
364  if ( (NULL != last_escape) &&
365  (((size_t) (value_end - last_escape)) < sizeof (pp->xbuf)) )
366  {
367  pp->xbuf_pos = value_end - last_escape;
368  memcpy (pp->xbuf,
369  last_escape,
370  value_end - last_escape);
371  value_end = last_escape;
372  }
373  while ( (value_start != value_end) ||
374  (pp->must_ikvi) ||
375  (xoff > 0) )
376  {
377  size_t delta = value_end - value_start;
378  bool cut = false;
379  size_t clen = 0;
380 
381  if (delta > XBUF_SIZE - xoff)
382  delta = XBUF_SIZE - xoff;
383  /* move (additional) input into processing buffer */
384  memcpy (&xbuf[xoff],
385  value_start,
386  delta);
387  xoff += delta;
388  value_start += delta;
389  /* find if escape sequence is at the end of the processing buffer;
390  if so, exclude those from processing (reduce delta to point at
391  end of processed region) */
392  if ( (xoff > 0) &&
393  ('%' == xbuf[xoff - 1]) )
394  {
395  cut = (xoff != XBUF_SIZE);
396  xoff--;
397  if (cut)
398  {
399  /* move escape sequence into buffer for next function invocation */
400  pp->xbuf[0] = '%';
401  pp->xbuf_pos = 1;
402  }
403  else
404  {
405  /* just skip escape sequence for next loop iteration */
406  delta = xoff;
407  clen = 1;
408  }
409  }
410  else if ( (xoff > 1) &&
411  ('%' == xbuf[xoff - 2]) )
412  {
413  cut = (xoff != XBUF_SIZE);
414  xoff -= 2;
415  if (cut)
416  {
417  /* move escape sequence into buffer for next function invocation */
418  memcpy (pp->xbuf,
419  &xbuf[xoff],
420  2);
421  pp->xbuf_pos = 2;
422  }
423  else
424  {
425  /* just skip escape sequence for next loop iteration */
426  delta = xoff;
427  clen = 2;
428  }
429  }
430  mhd_assert (xoff < sizeof (xbuf));
431  /* unescape */
432  xbuf[xoff] = '\0'; /* 0-terminate in preparation */
433  MHD_unescape_plus (xbuf);
434  xoff = MHD_http_unescape (xbuf);
435  /* finally: call application! */
436  if (pp->must_ikvi || (0 != xoff) )
437  {
438  pp->must_ikvi = false;
439  if (MHD_NO == pp->ikvi (pp->cls,
441  (const char *) &pp[1], /* key */
442  NULL,
443  NULL,
444  NULL,
445  xbuf,
446  pp->value_offset,
447  xoff))
448  {
449  pp->state = PP_Error;
450  return;
451  }
452  }
453  pp->value_offset += xoff;
454  if (cut)
455  break;
456  xbuf[delta] = '%'; /* undo 0-termination */
457  memmove (xbuf,
458  &xbuf[delta],
459  clen);
460  xoff = clen;
461  }
462 }
463 
464 
473 static int
474 post_process_urlencoded (struct MHD_PostProcessor *pp,
475  const char *post_data,
476  size_t post_data_len)
477 {
478  char *kbuf = (char *) &pp[1];
479  size_t poff;
480  const char *start_key = NULL;
481  const char *end_key = NULL;
482  const char *start_value = NULL;
483  const char *end_value = NULL;
484  const char *last_escape = NULL;
485 
486  mhd_assert (PP_Callback != pp->state);
487 
488  poff = 0;
489  while ( ( (poff < post_data_len) ||
490  (pp->state == PP_Callback) ) &&
491  (pp->state != PP_Error) )
492  {
493  switch (pp->state)
494  {
495  case PP_Error:
496  /* clearly impossible as per while loop invariant */
497  abort ();
498  break;
499  case PP_Init:
500  /* key phase */
501  if (NULL == start_key)
502  start_key = &post_data[poff];
503  pp->must_ikvi = true;
504  switch (post_data[poff])
505  {
506  case '=':
507  /* Case: 'key=' */
508  end_key = &post_data[poff];
509  poff++;
510  pp->state = PP_ProcessValue;
511  break;
512  case '&':
513  /* Case: 'key&' */
514  end_key = &post_data[poff];
515  mhd_assert (NULL == start_value);
516  mhd_assert (NULL == end_value);
517  poff++;
518  pp->state = PP_Callback;
519  break;
520  case '\n':
521  case '\r':
522  /* Case: 'key\n' or 'key\r' */
523  end_key = &post_data[poff];
524  poff++;
525  pp->state = PP_Done;
526  break;
527  default:
528  /* normal character, advance! */
529  poff++;
530  continue;
531  }
532  break; /* end PP_Init */
533  case PP_ProcessValue:
534  if (NULL == start_value)
535  start_value = &post_data[poff];
536  switch (post_data[poff])
537  {
538  case '=':
539  /* case 'key==' */
540  pp->state = PP_Error;
541  continue;
542  case '&':
543  /* case 'value&' */
544  end_value = &post_data[poff];
545  poff++;
546  if (pp->must_ikvi ||
547  (start_value != end_value) )
548  {
549  pp->state = PP_Callback;
550  }
551  else
552  {
553  pp->buffer_pos = 0;
554  pp->value_offset = 0;
555  pp->state = PP_Init;
556  start_value = NULL;
557  end_value = NULL;
558  }
559  continue;
560  case '\n':
561  case '\r':
562  /* Case: 'value\n' or 'value\r' */
563  end_value = &post_data[poff];
564  poff++;
565  if (pp->must_ikvi)
566  pp->state = PP_Callback;
567  else
568  pp->state = PP_Done;
569  break;
570  case '%':
571  last_escape = &post_data[poff];
572  poff++;
573  break;
574  case '0':
575  case '1':
576  case '2':
577  case '3':
578  case '4':
579  case '5':
580  case '6':
581  case '7':
582  case '8':
583  case '9':
584  /* character, may be part of escaping */
585  poff++;
586  continue;
587  default:
588  /* normal character, no more escaping! */
589  last_escape = NULL;
590  poff++;
591  continue;
592  }
593  break; /* end PP_ProcessValue */
594  case PP_Done:
595  switch (post_data[poff])
596  {
597  case '\n':
598  case '\r':
599  poff++;
600  continue;
601  }
602  /* unexpected data at the end, fail! */
603  pp->state = PP_Error;
604  break;
605  case PP_Callback:
606  if ( (pp->buffer_pos + (end_key - start_key) >=
607  pp->buffer_size) ||
608  (pp->buffer_pos + (end_key - start_key) <
609  pp->buffer_pos) )
610  {
611  /* key too long, cannot parse! */
612  pp->state = PP_Error;
613  continue;
614  }
615  /* compute key, if we have not already */
616  if (NULL != start_key)
617  {
618  memcpy (&kbuf[pp->buffer_pos],
619  start_key,
620  end_key - start_key);
621  pp->buffer_pos += end_key - start_key;
622  start_key = NULL;
623  end_key = NULL;
624  pp->must_unescape_key = true;
625  }
626  if (pp->must_unescape_key)
627  {
628  kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
629  MHD_unescape_plus (kbuf);
630  MHD_http_unescape (kbuf);
631  pp->must_unescape_key = false;
632  }
633  process_value (pp,
634  start_value,
635  end_value,
636  NULL);
637  if (PP_Error == pp->state)
638  continue;
639  pp->value_offset = 0;
640  start_value = NULL;
641  end_value = NULL;
642  pp->buffer_pos = 0;
643  pp->state = PP_Init;
644  break;
645  default:
647  __FILE__,
648  __LINE__,
649  NULL); /* should never happen! */
650  }
651  }
652 
653  /* save remaining data for next iteration */
654  if (NULL != start_key)
655  {
656  if (NULL == end_key)
657  end_key = &post_data[poff];
658  if (pp->buffer_pos + (end_key - start_key) >= pp->buffer_size)
659  {
660  pp->state = PP_Error;
661  return MHD_NO;
662  }
663  memcpy (&kbuf[pp->buffer_pos],
664  start_key,
665  end_key - start_key);
666  pp->buffer_pos += end_key - start_key;
667  pp->must_unescape_key = true;
668  start_key = NULL;
669  end_key = NULL;
670  }
671  if ( (NULL != start_value) &&
672  (PP_ProcessValue == pp->state) )
673  {
674  /* compute key, if we have not already */
675  if (pp->must_unescape_key)
676  {
677  kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
678  MHD_unescape_plus (kbuf);
679  MHD_http_unescape (kbuf);
680  pp->must_unescape_key = false;
681  }
682  if (NULL == end_value)
683  end_value = &post_data[poff];
684  process_value (pp,
685  start_value,
686  end_value,
687  last_escape);
688  pp->must_ikvi = false;
689  }
690  if (PP_Error == pp->state)
691  {
692  /* State in error, returning failure */
693  return MHD_NO;
694  }
695  return MHD_YES;
696 }
697 
698 
709 static int
710 try_match_header (const char *prefix,
711  size_t prefix_len,
712  char *line,
713  char **suffix)
714 {
715  if (NULL != *suffix)
716  return MHD_NO;
717  while (0 != *line)
718  {
719  if (MHD_str_equal_caseless_n_ (prefix,
720  line,
721  prefix_len))
722  {
723  *suffix = strdup (&line[prefix_len]);
724  return MHD_YES;
725  }
726  ++line;
727  }
728  return MHD_NO;
729 }
730 
731 
745 static int
746 find_boundary (struct MHD_PostProcessor *pp,
747  const char *boundary,
748  size_t blen,
749  size_t *ioffptr,
750  enum PP_State next_state,
751  enum PP_State next_dash_state)
752 {
753  char *buf = (char *) &pp[1];
754  const char *dash;
755 
756  if (pp->buffer_pos < 2 + blen)
757  {
758  if (pp->buffer_pos == pp->buffer_size)
759  pp->state = PP_Error; /* out of memory */
760  /* ++(*ioffptr); */
761  return MHD_NO; /* not enough data */
762  }
763  if ( (0 != memcmp ("--",
764  buf,
765  2)) ||
766  (0 != memcmp (&buf[2],
767  boundary,
768  blen)))
769  {
770  if (pp->state != PP_Init)
771  {
772  /* garbage not allowed */
773  pp->state = PP_Error;
774  }
775  else
776  {
777  /* skip over garbage (RFC 2046, 5.1.1) */
778  dash = memchr (buf,
779  '-',
780  pp->buffer_pos);
781  if (NULL == dash)
782  (*ioffptr) += pp->buffer_pos; /* skip entire buffer */
783  else if (dash == buf)
784  (*ioffptr)++; /* at least skip one byte */
785  else
786  (*ioffptr) += dash - buf; /* skip to first possible boundary */
787  }
788  return MHD_NO; /* expected boundary */
789  }
790  /* remove boundary from buffer */
791  (*ioffptr) += 2 + blen;
792  /* next: start with headers */
793  pp->skip_rn = RN_Dash;
794  pp->state = next_state;
795  pp->dash_state = next_dash_state;
796  return MHD_YES;
797 }
798 
799 
806 static void
807 try_get_value (const char *buf,
808  const char *key,
809  char **destination)
810 {
811  const char *spos;
812  const char *bpos;
813  const char *endv;
814  size_t klen;
815  size_t vlen;
816 
817  if (NULL != *destination)
818  return;
819  bpos = buf;
820  klen = strlen (key);
821  while (NULL != (spos = strstr (bpos, key)))
822  {
823  if ( (spos[klen] != '=') ||
824  ( (spos != buf) &&
825  (spos[-1] != ' ') ) )
826  {
827  /* no match */
828  bpos = spos + 1;
829  continue;
830  }
831  if (spos[klen + 1] != '"')
832  return; /* not quoted */
833  if (NULL == (endv = strchr (&spos[klen + 2],
834  '\"')))
835  return; /* no end-quote */
836  vlen = endv - spos - klen - 1;
837  *destination = malloc (vlen);
838  if (NULL == *destination)
839  return; /* out of memory */
840  (*destination)[vlen - 1] = '\0';
841  memcpy (*destination,
842  &spos[klen + 2],
843  vlen - 1);
844  return; /* success */
845  }
846 }
847 
848 
864 static int
865 process_multipart_headers (struct MHD_PostProcessor *pp,
866  size_t *ioffptr,
867  enum PP_State next_state)
868 {
869  char *buf = (char *) &pp[1];
870  size_t newline;
871 
872  newline = 0;
873  while ( (newline < pp->buffer_pos) &&
874  (buf[newline] != '\r') &&
875  (buf[newline] != '\n') )
876  newline++;
877  if (newline == pp->buffer_size)
878  {
879  pp->state = PP_Error;
880  return MHD_NO; /* out of memory */
881  }
882  if (newline == pp->buffer_pos)
883  return MHD_NO; /* will need more data */
884  if (0 == newline)
885  {
886  /* empty line - end of headers */
887  pp->skip_rn = RN_Full;
888  pp->state = next_state;
889  return MHD_YES;
890  }
891  /* got an actual header */
892  if (buf[newline] == '\r')
893  pp->skip_rn = RN_OptN;
894  buf[newline] = '\0';
895  if (MHD_str_equal_caseless_n_ ("Content-disposition: ",
896  buf,
897  MHD_STATICSTR_LEN_ ("Content-disposition: ")))
898  {
899  try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
900  "name",
901  &pp->content_name);
902  try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
903  "filename",
904  &pp->content_filename);
905  }
906  else
907  {
908  try_match_header ("Content-type: ",
909  MHD_STATICSTR_LEN_ ("Content-type: "),
910  buf,
911  &pp->content_type);
912  try_match_header ("Content-Transfer-Encoding: ",
913  MHD_STATICSTR_LEN_ ("Content-Transfer-Encoding: "),
914  buf,
915  &pp->content_transfer_encoding);
916  }
917  (*ioffptr) += newline + 1;
918  return MHD_YES;
919 }
920 
921 
938 static int
939 process_value_to_boundary (struct MHD_PostProcessor *pp,
940  size_t *ioffptr,
941  const char *boundary,
942  size_t blen,
943  enum PP_State next_state,
944  enum PP_State next_dash_state)
945 {
946  char *buf = (char *) &pp[1];
947  size_t newline;
948  const char *r;
949 
950  /* all data in buf until the boundary
951  (\r\n--+boundary) is part of the value */
952  newline = 0;
953  while (1)
954  {
955  while (newline + 4 < pp->buffer_pos)
956  {
957  r = memchr (&buf[newline],
958  '\r',
959  pp->buffer_pos - newline - 4);
960  if (NULL == r)
961  {
962  newline = pp->buffer_pos - 4;
963  break;
964  }
965  newline = r - buf;
966  if (0 == memcmp ("\r\n--",
967  &buf[newline],
968  4))
969  break;
970  newline++;
971  }
972  if (newline + blen + 4 <= pp->buffer_pos)
973  {
974  /* can check boundary */
975  if (0 != memcmp (&buf[newline + 4],
976  boundary,
977  blen))
978  {
979  /* no boundary, "\r\n--" is part of content, skip */
980  newline += 4;
981  continue;
982  }
983  else
984  {
985  /* boundary found, process until newline then
986  skip boundary and go back to init */
987  pp->skip_rn = RN_Dash;
988  pp->state = next_state;
989  pp->dash_state = next_dash_state;
990  (*ioffptr) += blen + 4; /* skip boundary as well */
991  buf[newline] = '\0';
992  break;
993  }
994  }
995  else
996  {
997  /* cannot check for boundary, process content that
998  we have and check again later; except, if we have
999  no content, abort (out of memory) */
1000  if ( (0 == newline) &&
1001  (pp->buffer_pos == pp->buffer_size) )
1002  {
1003  pp->state = PP_Error;
1004  return MHD_NO;
1005  }
1006  break;
1007  }
1008  }
1009  /* newline is either at beginning of boundary or
1010  at least at the last character that we are sure
1011  is not part of the boundary */
1012  if ( ( (pp->must_ikvi) ||
1013  (0 != newline) ) &&
1014  (MHD_NO == pp->ikvi (pp->cls,
1016  pp->content_name,
1017  pp->content_filename,
1018  pp->content_type,
1019  pp->content_transfer_encoding,
1020  buf,
1021  pp->value_offset,
1022  newline)) )
1023  {
1024  pp->state = PP_Error;
1025  return MHD_NO;
1026  }
1027  pp->must_ikvi = false;
1028  pp->value_offset += newline;
1029  (*ioffptr) += newline;
1030  return MHD_YES;
1031 }
1032 
1033 
1038 static void
1039 free_unmarked (struct MHD_PostProcessor *pp)
1040 {
1041  if ( (NULL != pp->content_name) &&
1042  (0 == (pp->have & NE_content_name)) )
1043  {
1044  free (pp->content_name);
1045  pp->content_name = NULL;
1046  }
1047  if ( (NULL != pp->content_type) &&
1048  (0 == (pp->have & NE_content_type)) )
1049  {
1050  free (pp->content_type);
1051  pp->content_type = NULL;
1052  }
1053  if ( (NULL != pp->content_filename) &&
1054  (0 == (pp->have & NE_content_filename)) )
1055  {
1056  free (pp->content_filename);
1057  pp->content_filename = NULL;
1058  }
1059  if ( (NULL != pp->content_transfer_encoding) &&
1060  (0 == (pp->have & NE_content_transfer_encoding)) )
1061  {
1062  free (pp->content_transfer_encoding);
1063  pp->content_transfer_encoding = NULL;
1064  }
1065 }
1066 
1067 
1076 static int
1077 post_process_multipart (struct MHD_PostProcessor *pp,
1078  const char *post_data,
1079  size_t post_data_len)
1080 {
1081  char *buf;
1082  size_t max;
1083  size_t ioff;
1084  size_t poff;
1085  int state_changed;
1086 
1087  buf = (char *) &pp[1];
1088  ioff = 0;
1089  poff = 0;
1090  state_changed = 1;
1091  while ( (poff < post_data_len) ||
1092  ( (pp->buffer_pos > 0) &&
1093  (0 != state_changed) ) )
1094  {
1095  /* first, move as much input data
1096  as possible to our internal buffer */
1097  max = pp->buffer_size - pp->buffer_pos;
1098  if (max > post_data_len - poff)
1099  max = post_data_len - poff;
1100  memcpy (&buf[pp->buffer_pos],
1101  &post_data[poff],
1102  max);
1103  poff += max;
1104  pp->buffer_pos += max;
1105  if ( (0 == max) &&
1106  (0 == state_changed) &&
1107  (poff < post_data_len) )
1108  {
1109  pp->state = PP_Error;
1110  return MHD_NO; /* out of memory */
1111  }
1112  state_changed = 0;
1113 
1114  /* first state machine for '\r'-'\n' and '--' handling */
1115  switch (pp->skip_rn)
1116  {
1117  case RN_Inactive:
1118  break;
1119  case RN_OptN:
1120  if (buf[0] == '\n')
1121  {
1122  ioff++;
1123  pp->skip_rn = RN_Inactive;
1124  goto AGAIN;
1125  }
1126  /* fall-through! */
1127  case RN_Dash:
1128  if (buf[0] == '-')
1129  {
1130  ioff++;
1131  pp->skip_rn = RN_Dash2;
1132  goto AGAIN;
1133  }
1134  pp->skip_rn = RN_Full;
1135  /* fall-through! */
1136  case RN_Full:
1137  if (buf[0] == '\r')
1138  {
1139  if ( (pp->buffer_pos > 1) &&
1140  ('\n' == buf[1]) )
1141  {
1142  pp->skip_rn = RN_Inactive;
1143  ioff += 2;
1144  }
1145  else
1146  {
1147  pp->skip_rn = RN_OptN;
1148  ioff++;
1149  }
1150  goto AGAIN;
1151  }
1152  if (buf[0] == '\n')
1153  {
1154  ioff++;
1155  pp->skip_rn = RN_Inactive;
1156  goto AGAIN;
1157  }
1158  pp->skip_rn = RN_Inactive;
1159  pp->state = PP_Error;
1160  return MHD_NO; /* no '\r\n' */
1161  case RN_Dash2:
1162  if (buf[0] == '-')
1163  {
1164  ioff++;
1165  pp->skip_rn = RN_Full;
1166  pp->state = pp->dash_state;
1167  goto AGAIN;
1168  }
1169  pp->state = PP_Error;
1170  break;
1171  }
1172 
1173  /* main state engine */
1174  switch (pp->state)
1175  {
1176  case PP_Error:
1177  return MHD_NO;
1178  case PP_Done:
1179  /* did not expect to receive more data */
1180  pp->state = PP_Error;
1181  return MHD_NO;
1182  case PP_Init:
1194  (void) find_boundary (pp,
1195  pp->boundary,
1196  pp->blen,
1197  &ioff,
1199  PP_Done);
1200  break;
1201  case PP_NextBoundary:
1202  if (MHD_NO == find_boundary (pp,
1203  pp->boundary,
1204  pp->blen,
1205  &ioff,
1207  PP_Done))
1208  {
1209  if (pp->state == PP_Error)
1210  return MHD_NO;
1211  goto END;
1212  }
1213  break;
1215  pp->must_ikvi = true;
1216  if (MHD_NO ==
1218  &ioff,
1220  {
1221  if (pp->state == PP_Error)
1222  return MHD_NO;
1223  else
1224  goto END;
1225  }
1226  state_changed = 1;
1227  break;
1229  if ( (NULL != pp->content_type) &&
1230  (MHD_str_equal_caseless_n_ (pp->content_type,
1231  "multipart/mixed",
1232  MHD_STATICSTR_LEN_ ("multipart/mixed"))))
1233  {
1234  pp->nested_boundary = strstr (pp->content_type,
1235  "boundary=");
1236  if (NULL == pp->nested_boundary)
1237  {
1238  pp->state = PP_Error;
1239  return MHD_NO;
1240  }
1241  pp->nested_boundary =
1242  strdup (&pp->nested_boundary[MHD_STATICSTR_LEN_ ("boundary=")]);
1243  if (NULL == pp->nested_boundary)
1244  {
1245  /* out of memory */
1246  pp->state = PP_Error;
1247  return MHD_NO;
1248  }
1249  /* free old content type, we will need that field
1250  for the content type of the nested elements */
1251  free (pp->content_type);
1252  pp->content_type = NULL;
1253  pp->nlen = strlen (pp->nested_boundary);
1254  pp->state = PP_Nested_Init;
1255  state_changed = 1;
1256  break;
1257  }
1258  pp->state = PP_ProcessValueToBoundary;
1259  pp->value_offset = 0;
1260  state_changed = 1;
1261  break;
1263  if (MHD_NO == process_value_to_boundary (pp,
1264  &ioff,
1265  pp->boundary,
1266  pp->blen,
1268  PP_Done))
1269  {
1270  if (pp->state == PP_Error)
1271  return MHD_NO;
1272  break;
1273  }
1274  break;
1275  case PP_PerformCleanup:
1276  /* clean up state of one multipart form-data element! */
1277  pp->have = NE_none;
1278  free_unmarked (pp);
1279  if (NULL != pp->nested_boundary)
1280  {
1281  free (pp->nested_boundary);
1282  pp->nested_boundary = NULL;
1283  }
1284  pp->state = PP_ProcessEntryHeaders;
1285  state_changed = 1;
1286  break;
1287  case PP_Nested_Init:
1288  if (NULL == pp->nested_boundary)
1289  {
1290  pp->state = PP_Error;
1291  return MHD_NO;
1292  }
1293  if (MHD_NO == find_boundary (pp,
1294  pp->nested_boundary,
1295  pp->nlen,
1296  &ioff,
1298  PP_NextBoundary /* or PP_Error? */))
1299  {
1300  if (pp->state == PP_Error)
1301  return MHD_NO;
1302  goto END;
1303  }
1304  break;
1306  /* remember what headers were given
1307  globally */
1308  pp->have = NE_none;
1309  if (NULL != pp->content_name)
1310  pp->have |= NE_content_name;
1311  if (NULL != pp->content_type)
1312  pp->have |= NE_content_type;
1313  if (NULL != pp->content_filename)
1314  pp->have |= NE_content_filename;
1315  if (NULL != pp->content_transfer_encoding)
1316  pp->have |= NE_content_transfer_encoding;
1317  pp->state = PP_Nested_ProcessEntryHeaders;
1318  state_changed = 1;
1319  break;
1321  pp->value_offset = 0;
1322  if (MHD_NO ==
1324  &ioff,
1326  {
1327  if (pp->state == PP_Error)
1328  return MHD_NO;
1329  else
1330  goto END;
1331  }
1332  state_changed = 1;
1333  break;
1335  if (MHD_NO == process_value_to_boundary (pp,
1336  &ioff,
1337  pp->nested_boundary,
1338  pp->nlen,
1340  PP_NextBoundary))
1341  {
1342  if (pp->state == PP_Error)
1343  return MHD_NO;
1344  break;
1345  }
1346  break;
1348  free_unmarked (pp);
1349  pp->state = PP_Nested_ProcessEntryHeaders;
1350  state_changed = 1;
1351  break;
1352  default:
1354  __FILE__,
1355  __LINE__,
1356  NULL); /* should never happen! */
1357  }
1358 AGAIN:
1359  if (ioff > 0)
1360  {
1361  memmove (buf,
1362  &buf[ioff],
1363  pp->buffer_pos - ioff);
1364  pp->buffer_pos -= ioff;
1365  ioff = 0;
1366  state_changed = 1;
1367  }
1368  }
1369 END:
1370  if (0 != ioff)
1371  {
1372  memmove (buf,
1373  &buf[ioff],
1374  pp->buffer_pos - ioff);
1375  pp->buffer_pos -= ioff;
1376  }
1377  if (poff < post_data_len)
1378  {
1379  pp->state = PP_Error;
1380  return MHD_NO; /* serious error */
1381  }
1382  return MHD_YES;
1383 }
1384 
1385 
1386 enum MHD_Result
1387 MHD_post_process (struct MHD_PostProcessor *pp,
1388  const char *post_data,
1389  size_t post_data_len)
1390 {
1391  if (0 == post_data_len)
1392  return MHD_YES;
1393  if (NULL == pp)
1394  return MHD_NO;
1396  pp->encoding,
1399  return post_process_urlencoded (pp,
1400  post_data,
1401  post_data_len);
1403  pp->encoding,
1406  return post_process_multipart (pp,
1407  post_data,
1408  post_data_len);
1409  /* this should never be reached */
1410  return MHD_NO;
1411 }
1412 
1413 
1414 enum MHD_Result
1415 MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
1416 {
1417  enum MHD_Result ret;
1418 
1419  if (NULL == pp)
1420  return MHD_YES;
1421  if (PP_ProcessValue == pp->state)
1422  {
1423  /* key without terminated value left at the end of the
1424  buffer; fake receiving a termination character to
1425  ensure it is also processed */
1427  "\n",
1428  1);
1429  }
1430  /* These internal strings need cleaning up since
1431  the post-processing may have been interrupted
1432  at any stage */
1433  if ( (pp->xbuf_pos > 0) ||
1434  ( (pp->state != PP_Done) &&
1435  (pp->state != PP_Init) ) )
1436  ret = MHD_NO;
1437  else
1438  ret = MHD_YES;
1439  pp->have = NE_none;
1440  free_unmarked (pp);
1441  if (NULL != pp->nested_boundary)
1442  free (pp->nested_boundary);
1443  free (pp);
1444  return ret;
1445 }
1446 
1447 
1448 /* end of postprocessor.c */
#define MHD_HTTP_HEADER_CONTENT_TYPE
Definition: microhttpd.h:578
#define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
Definition: microhttpd.h:1003
#define MHD_HTTP_POST_ENCODING_FORM_URLENCODED
Definition: microhttpd.h:1001
enum MHD_Result MHD_destroy_post_processor(struct MHD_PostProcessor *pp)
enum MHD_Result MHD_post_process(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
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)
Definition: connection.c:512
void MHD_unescape_plus(char *arg)
Definition: internal.c:123
MHD_PanicCallback mhd_panic
Definition: panic.c:31
void * mhd_panic_cls
Definition: panic.c:36
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
void * MHD_calloc_(size_t nelem, size_t elsize)
Definition: mhd_compat.c:98
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition: mhd_str.c:378
#define MHD_STATICSTR_LEN_(macro)
Definition: mhd_str.h:45
#define NULL
Definition: reason_phrase.c:30
internal shared structures
macros for mhd_assert()
Header for platform missing functions.
Header for string manipulating helpers.
MHD_Result
Definition: microhttpd.h:139
@ MHD_YES
Definition: microhttpd.h:148
@ MHD_NO
Definition: microhttpd.h:143
_MHD_EXTERN size_t MHD_http_unescape(char *val)
Definition: internal.c:142
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)
Definition: microhttpd.h:2415
@ MHD_POSTDATA_KIND
Definition: microhttpd.h:1831
@ MHD_HEADER_KIND
Definition: microhttpd.h:1815
static int post_process_urlencoded(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)
RN_State
Definition: postprocessor.c:71
@ RN_Dash
Definition: postprocessor.c:93
@ RN_Inactive
Definition: postprocessor.c:75
@ RN_Full
Definition: postprocessor.c:87
@ RN_OptN
Definition: postprocessor.c:81
@ RN_Dash2
Definition: postprocessor.c:98
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
Definition: postprocessor.c:36
NE_State
@ NE_content_name
@ NE_content_type
@ NE_content_transfer_encoding
@ NE_none
@ NE_content_filename
static int post_process_multipart(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
static void process_value(struct MHD_PostProcessor *pp, const char *value_start, const char *value_end, const char *last_escape)
PP_State
Definition: postprocessor.c:42
@ PP_PerformCleanup
Definition: postprocessor.c:58
@ PP_Error
Definition: postprocessor.c:44
@ PP_Nested_PerformMarking
Definition: postprocessor.c:62
@ PP_Init
Definition: postprocessor.c:46
@ PP_PerformCheckMultipart
Definition: postprocessor.c:56
@ PP_Nested_Init
Definition: postprocessor.c:61
@ PP_ProcessValue
Definition: postprocessor.c:50
@ PP_ExpectNewLine
Definition: postprocessor.c:52
@ PP_Nested_ProcessEntryHeaders
Definition: postprocessor.c:63
@ PP_Nested_PerformCleanup
Definition: postprocessor.c:65
@ PP_NextBoundary
Definition: postprocessor.c:47
@ PP_ProcessEntryHeaders
Definition: postprocessor.c:55
@ PP_ProcessValueToBoundary
Definition: postprocessor.c:57
@ PP_Done
Definition: postprocessor.c:45
@ PP_Callback
Definition: postprocessor.c:51
@ PP_Nested_ProcessValueToBoundary
Definition: postprocessor.c:64
static void try_get_value(const char *buf, const char *key, char **destination)
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)
enum MHD_CONNECTION_STATE state
Definition: internal.h:1064