GNU libmicrohttpd  1.0.1
postprocessor.c
Go to the documentation of this file.
1 /*
2  This file is part of libmicrohttpd
3  Copyright (C) 2007-2021 Daniel Pittman and Christian Grothoff
4  Copyright (C) 2014-2022 Karlson2k (Evgeny Grin)
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
28 #include "postprocessor.h"
29 #include "internal.h"
30 #include "mhd_str.h"
31 #include "mhd_compat.h"
32 #include "mhd_assert.h"
33 
39 #define XBUF_SIZE 512
40 
41 
44  size_t buffer_size,
46  void *iter_cls)
47 {
48  struct MHD_PostProcessor *ret;
49  const char *encoding;
50  const char *boundary;
51  size_t blen;
52 
53  if ( (buffer_size < 256) ||
54  (NULL == connection) ||
55  (NULL == iter))
56  MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
57  encoding = NULL;
58  if (MHD_NO ==
64  &encoding,
65  NULL))
66  return NULL;
68  boundary = NULL;
70  encoding,
73  {
75  encoding,
78  return NULL;
79  boundary =
81  /* Q: should this be "strcasestr"? */
82  boundary = strstr (boundary, "boundary=");
83  if (NULL == boundary)
84  return NULL; /* failed to determine boundary */
85  boundary += MHD_STATICSTR_LEN_ ("boundary=");
86  blen = strlen (boundary);
87  if ( (blen < 2) ||
88  (blen * 2 + 2 > buffer_size) )
89  return NULL; /* (will be) out of memory or invalid boundary */
90  if ( (boundary[0] == '"') &&
91  (boundary[blen - 1] == '"') )
92  {
93  /* remove enclosing quotes */
94  ++boundary;
95  blen -= 2;
96  }
97  }
98  else
99  blen = 0;
100  buffer_size += 4; /* round up to get nice block sizes despite boundary search */
101 
102  /* add +1 to ensure we ALWAYS have a zero-termination at the end */
103  if (NULL == (ret = MHD_calloc_ (1, sizeof (struct MHD_PostProcessor)
104  + buffer_size + 1)))
105  return NULL;
106  ret->connection = connection;
107  ret->ikvi = iter;
108  ret->cls = iter_cls;
109  ret->encoding = encoding;
110  ret->buffer_size = buffer_size;
111  ret->state = PP_Init;
112  ret->blen = blen;
113  ret->boundary = boundary;
114  ret->skip_rn = RN_Inactive;
115  return ret;
116 }
117 
118 
136 static void
138  const char *value_start,
139  const char *value_end,
140  const char *last_escape)
141 {
142  char xbuf[XBUF_SIZE + 1];
143  size_t xoff;
144 
145  mhd_assert (pp->xbuf_pos < sizeof (xbuf));
146  /* 'value_start' and 'value_end' must be either both non-NULL or both NULL */
147  mhd_assert ( (NULL == value_start) || (NULL != value_end) );
148  mhd_assert ( (NULL != value_start) || (NULL == value_end) );
149  mhd_assert ( (NULL == last_escape) || (NULL != value_start) );
150  /* move remaining input from previous round into processing buffer */
151  if (0 != pp->xbuf_pos)
152  memcpy (xbuf,
153  pp->xbuf,
154  pp->xbuf_pos);
155  xoff = pp->xbuf_pos;
156  pp->xbuf_pos = 0;
157  if ( (NULL != last_escape) &&
158  (((size_t) (value_end - last_escape)) < sizeof (pp->xbuf)) )
159  {
160  mhd_assert (value_end >= last_escape);
161  pp->xbuf_pos = (size_t) (value_end - last_escape);
162  memcpy (pp->xbuf,
163  last_escape,
164  (size_t) (value_end - last_escape));
165  value_end = last_escape;
166  }
167  while ( (value_start != value_end) ||
168  (pp->must_ikvi) ||
169  (xoff > 0) )
170  {
171  size_t delta = (size_t) (value_end - value_start);
172  bool cut = false;
173  size_t clen = 0;
174 
175  mhd_assert (value_end >= value_start);
176 
177  if (delta > XBUF_SIZE - xoff)
178  delta = XBUF_SIZE - xoff;
179  /* move (additional) input into processing buffer */
180  if (0 != delta)
181  {
182  memcpy (&xbuf[xoff],
183  value_start,
184  delta);
185  xoff += delta;
186  value_start += delta;
187  }
188  /* find if escape sequence is at the end of the processing buffer;
189  if so, exclude those from processing (reduce delta to point at
190  end of processed region) */
191  if ( (xoff > 0) &&
192  ('%' == xbuf[xoff - 1]) )
193  {
194  cut = (xoff != XBUF_SIZE);
195  xoff--;
196  if (cut)
197  {
198  /* move escape sequence into buffer for next function invocation */
199  pp->xbuf[0] = '%';
200  pp->xbuf_pos = 1;
201  }
202  else
203  {
204  /* just skip escape sequence for next loop iteration */
205  delta = xoff;
206  clen = 1;
207  }
208  }
209  else if ( (xoff > 1) &&
210  ('%' == xbuf[xoff - 2]) )
211  {
212  cut = (xoff != XBUF_SIZE);
213  xoff -= 2;
214  if (cut)
215  {
216  /* move escape sequence into buffer for next function invocation */
217  memcpy (pp->xbuf,
218  &xbuf[xoff],
219  2);
220  pp->xbuf_pos = 2;
221  }
222  else
223  {
224  /* just skip escape sequence for next loop iteration */
225  delta = xoff;
226  clen = 2;
227  }
228  }
229  mhd_assert (xoff < sizeof (xbuf));
230  /* unescape */
231  xbuf[xoff] = '\0'; /* 0-terminate in preparation */
232  if (0 != xoff)
233  {
235  xoff = MHD_http_unescape (xbuf);
236  }
237  /* finally: call application! */
238  if (pp->must_ikvi || (0 != xoff) )
239  {
240  pp->must_ikvi = false;
241  if (MHD_NO == pp->ikvi (pp->cls,
243  (const char *) &pp[1], /* key */
244  NULL,
245  NULL,
246  NULL,
247  xbuf,
248  pp->value_offset,
249  xoff))
250  {
251  pp->state = PP_Error;
252  return;
253  }
254  }
255  pp->value_offset += xoff;
256  if (cut)
257  break;
258  if (0 != clen)
259  {
260  xbuf[delta] = '%'; /* undo 0-termination */
261  memmove (xbuf,
262  &xbuf[delta],
263  clen);
264  }
265  xoff = clen;
266  }
267 }
268 
269 
278 static enum MHD_Result
280  const char *post_data,
281  size_t post_data_len)
282 {
283  char *kbuf = (char *) &pp[1];
284  size_t poff;
285  const char *start_key = NULL;
286  const char *end_key = NULL;
287  const char *start_value = NULL;
288  const char *end_value = NULL;
289  const char *last_escape = NULL;
290 
291  mhd_assert (PP_Callback != pp->state);
292 
293  poff = 0;
294  while ( ( (poff < post_data_len) ||
295  (pp->state == PP_Callback) ) &&
296  (pp->state != PP_Error) )
297  {
298  switch (pp->state)
299  {
300  case PP_Error:
301  /* clearly impossible as per while loop invariant */
302  abort ();
303  break; /* Unreachable */
304  case PP_Init:
305  /* initial phase */
306  mhd_assert (NULL == start_key);
307  mhd_assert (NULL == end_key);
308  mhd_assert (NULL == start_value);
309  mhd_assert (NULL == end_value);
310  switch (post_data[poff])
311  {
312  case '=':
313  /* Case: (no key)'=' */
314  /* Empty key with value */
315  pp->state = PP_Error;
316  continue;
317  case '&':
318  /* Case: (no key)'&' */
319  /* Empty key without value */
320  poff++;
321  continue;
322  case '\n':
323  case '\r':
324  /* Case: (no key)'\n' or (no key)'\r' */
325  pp->state = PP_Done;
326  poff++;
327  break;
328  default:
329  /* normal character, key start, advance! */
330  pp->state = PP_ProcessKey;
331  start_key = &post_data[poff];
332  pp->must_ikvi = true;
333  poff++;
334  continue;
335  }
336  break; /* end PP_Init */
337  case PP_ProcessKey:
338  /* key phase */
339  mhd_assert (NULL == start_value);
340  mhd_assert (NULL == end_value);
341  mhd_assert (NULL != start_key || 0 == poff);
342  mhd_assert (0 != poff || NULL == start_key);
343  mhd_assert (NULL == end_key);
344  switch (post_data[poff])
345  {
346  case '=':
347  /* Case: 'key=' */
348  if (0 != poff)
349  end_key = &post_data[poff];
350  poff++;
351  pp->state = PP_ProcessValue;
352  break;
353  case '&':
354  /* Case: 'key&' */
355  if (0 != poff)
356  end_key = &post_data[poff];
357  poff++;
358  pp->state = PP_Callback;
359  break;
360  case '\n':
361  case '\r':
362  /* Case: 'key\n' or 'key\r' */
363  if (0 != poff)
364  end_key = &post_data[poff];
365  /* No advance here, 'PP_Done' will be selected by next 'PP_Init' phase */
366  pp->state = PP_Callback;
367  break;
368  default:
369  /* normal character, advance! */
370  if (0 == poff)
371  start_key = post_data;
372  poff++;
373  break;
374  }
375  mhd_assert (NULL == end_key || NULL != start_key);
376  break; /* end PP_ProcessKey */
377  case PP_ProcessValue:
378  if (NULL == start_value)
379  start_value = &post_data[poff];
380  switch (post_data[poff])
381  {
382  case '=':
383  /* case 'key==' */
384  pp->state = PP_Error;
385  continue;
386  case '&':
387  /* case 'value&' */
388  end_value = &post_data[poff];
389  poff++;
390  if (pp->must_ikvi ||
391  (start_value != end_value) )
392  {
393  pp->state = PP_Callback;
394  }
395  else
396  {
397  pp->buffer_pos = 0;
398  pp->value_offset = 0;
399  pp->state = PP_Init;
400  start_value = NULL;
401  end_value = NULL;
402  }
403  continue;
404  case '\n':
405  case '\r':
406  /* Case: 'value\n' or 'value\r' */
407  end_value = &post_data[poff];
408  if (pp->must_ikvi ||
409  (start_value != end_value) )
410  pp->state = PP_Callback; /* No poff advance here to set PP_Done in the next iteration */
411  else
412  {
413  poff++;
414  pp->state = PP_Done;
415  }
416  break;
417  case '%':
418  last_escape = &post_data[poff];
419  poff++;
420  break;
421  case '0':
422  case '1':
423  case '2':
424  case '3':
425  case '4':
426  case '5':
427  case '6':
428  case '7':
429  case '8':
430  case '9':
431  /* character, may be part of escaping */
432  poff++;
433  continue;
434  default:
435  /* normal character, no more escaping! */
436  last_escape = NULL;
437  poff++;
438  continue;
439  }
440  break; /* end PP_ProcessValue */
441  case PP_Done:
442  switch (post_data[poff])
443  {
444  case '\n':
445  case '\r':
446  poff++;
447  continue;
448  }
449  /* unexpected data at the end, fail! */
450  pp->state = PP_Error;
451  break;
452  case PP_Callback:
453  mhd_assert ((NULL != end_key) || (NULL == start_key));
454  if (1)
455  {
456  const size_t key_len = (size_t) (end_key - start_key);
457  mhd_assert (end_key >= start_key);
458  if (0 != key_len)
459  {
460  if ( (pp->buffer_pos + key_len >= pp->buffer_size) ||
461  (pp->buffer_pos + key_len < pp->buffer_pos) )
462  {
463  /* key too long, cannot parse! */
464  pp->state = PP_Error;
465  continue;
466  }
467  /* compute key, if we have not already */
468  memcpy (&kbuf[pp->buffer_pos],
469  start_key,
470  key_len);
471  pp->buffer_pos += key_len;
472  start_key = NULL;
473  end_key = NULL;
474  pp->must_unescape_key = true;
475  }
476  }
477 #ifdef _DEBUG
478  else
479  mhd_assert (0 != pp->buffer_pos);
480 #endif /* _DEBUG */
481  if (pp->must_unescape_key)
482  {
483  kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
484  MHD_unescape_plus (kbuf);
485  MHD_http_unescape (kbuf);
486  pp->must_unescape_key = false;
487  }
488  process_value (pp,
489  start_value,
490  end_value,
491  NULL);
492  if (PP_Error == pp->state)
493  continue;
494  pp->value_offset = 0;
495  start_value = NULL;
496  end_value = NULL;
497  pp->buffer_pos = 0;
498  pp->state = PP_Init;
499  break;
500  case PP_NextBoundary:
504  case PP_PerformCleanup:
505  case PP_Nested_Init:
510  default:
511  MHD_PANIC (_ ("internal error.\n")); /* should never happen! */
512  }
513  mhd_assert ((end_key == NULL) || (start_key != NULL));
514  mhd_assert ((end_value == NULL) || (start_value != NULL));
515  }
516 
517  mhd_assert (PP_Callback != pp->state);
518 
519  if (PP_Error == pp->state)
520  {
521  /* State in error, returning failure */
522  return MHD_NO;
523  }
524 
525  /* save remaining data for next iteration */
526  if (NULL != start_key)
527  {
528  size_t key_len;
529  mhd_assert ((PP_ProcessKey == pp->state) || (NULL != end_key));
530  if (NULL == end_key)
531  end_key = &post_data[poff];
532  mhd_assert (end_key >= start_key);
533  key_len = (size_t) (end_key - start_key);
534  mhd_assert (0 != key_len); /* it must be always non-zero here */
535  if (pp->buffer_pos + key_len >= pp->buffer_size)
536  {
537  pp->state = PP_Error;
538  return MHD_NO;
539  }
540  memcpy (&kbuf[pp->buffer_pos],
541  start_key,
542  key_len);
543  pp->buffer_pos += key_len;
544  pp->must_unescape_key = true;
545  start_key = NULL;
546  end_key = NULL;
547  }
548  if ( (NULL != start_value) &&
549  (PP_ProcessValue == pp->state) )
550  {
551  /* compute key, if we have not already */
552  if (pp->must_unescape_key)
553  {
554  kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */
555  MHD_unescape_plus (kbuf);
556  MHD_http_unescape (kbuf);
557  pp->must_unescape_key = false;
558  }
559  if (NULL == end_value)
560  end_value = &post_data[poff];
561  if ( (NULL != last_escape) &&
562  (2 < (end_value - last_escape)) )
563  last_escape = NULL;
564  process_value (pp,
565  start_value,
566  end_value,
567  last_escape);
568  pp->must_ikvi = false;
569  }
570  if (PP_Error == pp->state)
571  {
572  /* State in error, returning failure */
573  return MHD_NO;
574  }
575  return MHD_YES;
576 }
577 
578 
589 static int
590 try_match_header (const char *prefix,
591  size_t prefix_len,
592  char *line,
593  char **suffix)
594 {
595  if (NULL != *suffix)
596  return MHD_NO;
597  while (0 != *line)
598  {
599  if (MHD_str_equal_caseless_n_ (prefix,
600  line,
601  prefix_len))
602  {
603  *suffix = strdup (&line[prefix_len]);
604  return MHD_YES;
605  }
606  ++line;
607  }
608  return MHD_NO;
609 }
610 
611 
625 static int
627  const char *boundary,
628  size_t blen,
629  size_t *ioffptr,
630  enum PP_State next_state,
631  enum PP_State next_dash_state)
632 {
633  char *buf = (char *) &pp[1];
634  const char *dash;
635 
636  if (pp->buffer_pos < 2 + blen)
637  {
638  if (pp->buffer_pos == pp->buffer_size)
639  pp->state = PP_Error; /* out of memory */
640  /* ++(*ioffptr); */
641  return MHD_NO; /* not enough data */
642  }
643  if ( (0 != memcmp ("--",
644  buf,
645  2)) ||
646  (0 != memcmp (&buf[2],
647  boundary,
648  blen)))
649  {
650  if (pp->state != PP_Init)
651  {
652  /* garbage not allowed */
653  pp->state = PP_Error;
654  }
655  else
656  {
657  /* skip over garbage (RFC 2046, 5.1.1) */
658  dash = memchr (buf,
659  '-',
660  pp->buffer_pos);
661  if (NULL == dash)
662  (*ioffptr) += pp->buffer_pos; /* skip entire buffer */
663  else if (dash == buf)
664  (*ioffptr)++; /* at least skip one byte */
665  else
666  (*ioffptr) += (size_t) (dash - buf); /* skip to first possible boundary */
667  }
668  return MHD_NO; /* expected boundary */
669  }
670  /* remove boundary from buffer */
671  (*ioffptr) += 2 + blen;
672  /* next: start with headers */
673  pp->skip_rn = RN_Dash;
674  pp->state = next_state;
675  pp->dash_state = next_dash_state;
676  return MHD_YES;
677 }
678 
679 
686 static void
687 try_get_value (const char *buf,
688  const char *key,
689  char **destination)
690 {
691  const char *spos;
692  const char *bpos;
693  const char *endv;
694  size_t klen;
695  size_t vlen;
696 
697  if (NULL != *destination)
698  return;
699  bpos = buf;
700  klen = strlen (key);
701  while (NULL != (spos = strstr (bpos, key)))
702  {
703  if ( (spos[klen] != '=') ||
704  ( (spos != buf) &&
705  (spos[-1] != ' ') ) )
706  {
707  /* no match */
708  bpos = spos + 1;
709  continue;
710  }
711  if (spos[klen + 1] != '"')
712  return; /* not quoted */
713  if (NULL == (endv = strchr (&spos[klen + 2],
714  '\"')))
715  return; /* no end-quote */
716  vlen = (size_t) (endv - spos) - klen - 1;
717  *destination = malloc (vlen);
718  if (NULL == *destination)
719  return; /* out of memory */
720  (*destination)[vlen - 1] = '\0';
721  memcpy (*destination,
722  &spos[klen + 2],
723  vlen - 1);
724  return; /* success */
725  }
726 }
727 
728 
744 static int
746  size_t *ioffptr,
747  enum PP_State next_state)
748 {
749  char *buf = (char *) &pp[1];
750  size_t newline;
751 
752  newline = 0;
753  while ( (newline < pp->buffer_pos) &&
754  (buf[newline] != '\r') &&
755  (buf[newline] != '\n') )
756  newline++;
757  if (newline == pp->buffer_size)
758  {
759  pp->state = PP_Error;
760  return MHD_NO; /* out of memory */
761  }
762  if (newline == pp->buffer_pos)
763  return MHD_NO; /* will need more data */
764  if (0 == newline)
765  {
766  /* empty line - end of headers */
767  pp->skip_rn = RN_Full;
768  pp->state = next_state;
769  return MHD_YES;
770  }
771  /* got an actual header */
772  if (buf[newline] == '\r')
773  pp->skip_rn = RN_OptN;
774  buf[newline] = '\0';
775  if (MHD_str_equal_caseless_n_ ("Content-disposition: ",
776  buf,
777  MHD_STATICSTR_LEN_ ("Content-disposition: ")))
778  {
779  try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
780  "name",
781  &pp->content_name);
782  try_get_value (&buf[MHD_STATICSTR_LEN_ ("Content-disposition: ")],
783  "filename",
784  &pp->content_filename);
785  }
786  else
787  {
788  try_match_header ("Content-type: ",
789  MHD_STATICSTR_LEN_ ("Content-type: "),
790  buf,
791  &pp->content_type);
792  try_match_header ("Content-Transfer-Encoding: ",
793  MHD_STATICSTR_LEN_ ("Content-Transfer-Encoding: "),
794  buf,
796  }
797  (*ioffptr) += newline + 1;
798  return MHD_YES;
799 }
800 
801 
818 static int
820  size_t *ioffptr,
821  const char *boundary,
822  size_t blen,
823  enum PP_State next_state,
824  enum PP_State next_dash_state)
825 {
826  char *buf = (char *) &pp[1];
827  size_t newline;
828  const char *r;
829 
830  /* all data in buf until the boundary
831  (\r\n--+boundary) is part of the value */
832  newline = 0;
833  while (1)
834  {
835  while (newline + 4 < pp->buffer_pos)
836  {
837  r = memchr (&buf[newline],
838  '\r',
839  pp->buffer_pos - newline - 4);
840  if (NULL == r)
841  {
842  newline = pp->buffer_pos - 4;
843  break;
844  }
845  newline = (size_t) (r - buf);
846  if (0 == memcmp ("\r\n--",
847  &buf[newline],
848  4))
849  break;
850  newline++;
851  }
852  if (newline + blen + 4 <= pp->buffer_pos)
853  {
854  /* can check boundary */
855  if (0 != memcmp (&buf[newline + 4],
856  boundary,
857  blen))
858  {
859  /* no boundary, "\r\n--" is part of content, skip */
860  newline += 4;
861  continue;
862  }
863  else
864  {
865  /* boundary found, process until newline then
866  skip boundary and go back to init */
867  pp->skip_rn = RN_Dash;
868  pp->state = next_state;
869  pp->dash_state = next_dash_state;
870  (*ioffptr) += blen + 4; /* skip boundary as well */
871  buf[newline] = '\0';
872  break;
873  }
874  }
875  else
876  {
877  /* cannot check for boundary, process content that
878  we have and check again later; except, if we have
879  no content, abort (out of memory) */
880  if ( (0 == newline) &&
881  (pp->buffer_pos == pp->buffer_size) )
882  {
883  pp->state = PP_Error;
884  return MHD_NO;
885  }
886  break;
887  }
888  }
889  /* newline is either at beginning of boundary or
890  at least at the last character that we are sure
891  is not part of the boundary */
892  if ( ( (pp->must_ikvi) ||
893  (0 != newline) ) &&
894  (MHD_NO == pp->ikvi (pp->cls,
896  pp->content_name,
897  pp->content_filename,
898  pp->content_type,
900  buf,
901  pp->value_offset,
902  newline)) )
903  {
904  pp->state = PP_Error;
905  return MHD_NO;
906  }
907  pp->must_ikvi = false;
908  pp->value_offset += newline;
909  (*ioffptr) += newline;
910  return MHD_YES;
911 }
912 
913 
918 static void
920 {
921  if ( (NULL != pp->content_name) &&
922  (0 == (pp->have & NE_content_name)) )
923  {
924  free (pp->content_name);
925  pp->content_name = NULL;
926  }
927  if ( (NULL != pp->content_type) &&
928  (0 == (pp->have & NE_content_type)) )
929  {
930  free (pp->content_type);
931  pp->content_type = NULL;
932  }
933  if ( (NULL != pp->content_filename) &&
934  (0 == (pp->have & NE_content_filename)) )
935  {
936  free (pp->content_filename);
937  pp->content_filename = NULL;
938  }
939  if ( (NULL != pp->content_transfer_encoding) &&
940  (0 == (pp->have & NE_content_transfer_encoding)) )
941  {
942  free (pp->content_transfer_encoding);
944  }
945 }
946 
947 
956 static enum MHD_Result
958  const char *post_data,
959  size_t post_data_len)
960 {
961  char *buf;
962  size_t max;
963  size_t ioff;
964  size_t poff;
965  int state_changed;
966 
967  buf = (char *) &pp[1];
968  ioff = 0;
969  poff = 0;
970  state_changed = 1;
971  while ( (poff < post_data_len) ||
972  ( (pp->buffer_pos > 0) &&
973  (0 != state_changed) ) )
974  {
975  /* first, move as much input data
976  as possible to our internal buffer */
977  max = pp->buffer_size - pp->buffer_pos;
978  if (max > post_data_len - poff)
979  max = post_data_len - poff;
980  memcpy (&buf[pp->buffer_pos],
981  &post_data[poff],
982  max);
983  poff += max;
984  pp->buffer_pos += max;
985  if ( (0 == max) &&
986  (0 == state_changed) &&
987  (poff < post_data_len) )
988  {
989  pp->state = PP_Error;
990  return MHD_NO; /* out of memory */
991  }
992  state_changed = 0;
993 
994  /* first state machine for '\r'-'\n' and '--' handling */
995  switch (pp->skip_rn)
996  {
997  case RN_Inactive:
998  break;
999  case RN_OptN:
1000  if (buf[0] == '\n')
1001  {
1002  ioff++;
1003  pp->skip_rn = RN_Inactive;
1004  goto AGAIN;
1005  }
1006  /* fall-through! */
1007  case RN_Dash:
1008  if (buf[0] == '-')
1009  {
1010  ioff++;
1011  pp->skip_rn = RN_Dash2;
1012  goto AGAIN;
1013  }
1014  pp->skip_rn = RN_Full;
1015  /* fall-through! */
1016  case RN_Full:
1017  if (buf[0] == '\r')
1018  {
1019  if ( (pp->buffer_pos > 1) &&
1020  ('\n' == buf[1]) )
1021  {
1022  pp->skip_rn = RN_Inactive;
1023  ioff += 2;
1024  }
1025  else
1026  {
1027  pp->skip_rn = RN_OptN;
1028  ioff++;
1029  }
1030  goto AGAIN;
1031  }
1032  if (buf[0] == '\n')
1033  {
1034  ioff++;
1035  pp->skip_rn = RN_Inactive;
1036  goto AGAIN;
1037  }
1038  pp->skip_rn = RN_Inactive;
1039  pp->state = PP_Error;
1040  return MHD_NO; /* no '\r\n' */
1041  case RN_Dash2:
1042  if (buf[0] == '-')
1043  {
1044  ioff++;
1045  pp->skip_rn = RN_Full;
1046  pp->state = pp->dash_state;
1047  goto AGAIN;
1048  }
1049  pp->state = PP_Error;
1050  break;
1051  }
1052 
1053  /* main state engine */
1054  switch (pp->state)
1055  {
1056  case PP_Error:
1057  return MHD_NO;
1058  case PP_Done:
1059  /* did not expect to receive more data */
1060  pp->state = PP_Error;
1061  return MHD_NO;
1062  case PP_Init:
1074  (void) find_boundary (pp,
1075  pp->boundary,
1076  pp->blen,
1077  &ioff,
1079  PP_Done);
1080  break;
1081  case PP_NextBoundary:
1082  if (MHD_NO == find_boundary (pp,
1083  pp->boundary,
1084  pp->blen,
1085  &ioff,
1087  PP_Done))
1088  {
1089  if (pp->state == PP_Error)
1090  return MHD_NO;
1091  goto END;
1092  }
1093  break;
1095  pp->must_ikvi = true;
1096  if (MHD_NO ==
1098  &ioff,
1100  {
1101  if (pp->state == PP_Error)
1102  return MHD_NO;
1103  else
1104  goto END;
1105  }
1106  state_changed = 1;
1107  break;
1109  if ( (NULL != pp->content_type) &&
1111  "multipart/mixed",
1112  MHD_STATICSTR_LEN_ ("multipart/mixed"))))
1113  {
1114  pp->nested_boundary = strstr (pp->content_type,
1115  "boundary=");
1116  if (NULL == pp->nested_boundary)
1117  {
1118  pp->state = PP_Error;
1119  return MHD_NO;
1120  }
1121  pp->nested_boundary =
1122  strdup (&pp->nested_boundary[MHD_STATICSTR_LEN_ ("boundary=")]);
1123  if (NULL == pp->nested_boundary)
1124  {
1125  /* out of memory */
1126  pp->state = PP_Error;
1127  return MHD_NO;
1128  }
1129  /* free old content type, we will need that field
1130  for the content type of the nested elements */
1131  free (pp->content_type);
1132  pp->content_type = NULL;
1133  pp->nlen = strlen (pp->nested_boundary);
1134  pp->state = PP_Nested_Init;
1135  state_changed = 1;
1136  break;
1137  }
1139  pp->value_offset = 0;
1140  state_changed = 1;
1141  break;
1143  if (MHD_NO == process_value_to_boundary (pp,
1144  &ioff,
1145  pp->boundary,
1146  pp->blen,
1148  PP_Done))
1149  {
1150  if (pp->state == PP_Error)
1151  return MHD_NO;
1152  break;
1153  }
1154  break;
1155  case PP_PerformCleanup:
1156  /* clean up state of one multipart form-data element! */
1157  pp->have = NE_none;
1158  free_unmarked (pp);
1159  if (NULL != pp->nested_boundary)
1160  {
1161  free (pp->nested_boundary);
1162  pp->nested_boundary = NULL;
1163  }
1165  state_changed = 1;
1166  break;
1167  case PP_Nested_Init:
1168  if (NULL == pp->nested_boundary)
1169  {
1170  pp->state = PP_Error;
1171  return MHD_NO;
1172  }
1173  if (MHD_NO == find_boundary (pp,
1174  pp->nested_boundary,
1175  pp->nlen,
1176  &ioff,
1178  PP_NextBoundary /* or PP_Error? */))
1179  {
1180  if (pp->state == PP_Error)
1181  return MHD_NO;
1182  goto END;
1183  }
1184  break;
1186  /* remember what headers were given
1187  globally */
1188  pp->have = NE_none;
1189  if (NULL != pp->content_name)
1190  pp->have |= NE_content_name;
1191  if (NULL != pp->content_type)
1192  pp->have |= NE_content_type;
1193  if (NULL != pp->content_filename)
1194  pp->have |= NE_content_filename;
1195  if (NULL != pp->content_transfer_encoding)
1198  state_changed = 1;
1199  break;
1201  pp->value_offset = 0;
1202  if (MHD_NO ==
1204  &ioff,
1206  {
1207  if (pp->state == PP_Error)
1208  return MHD_NO;
1209  else
1210  goto END;
1211  }
1212  state_changed = 1;
1213  break;
1215  if (MHD_NO == process_value_to_boundary (pp,
1216  &ioff,
1217  pp->nested_boundary,
1218  pp->nlen,
1220  PP_NextBoundary))
1221  {
1222  if (pp->state == PP_Error)
1223  return MHD_NO;
1224  break;
1225  }
1226  break;
1228  free_unmarked (pp);
1230  state_changed = 1;
1231  break;
1232  case PP_ProcessKey:
1233  case PP_ProcessValue:
1234  case PP_Callback:
1235  default:
1236  MHD_PANIC (_ ("internal error.\n")); /* should never happen! */
1237  }
1238 AGAIN:
1239  if (ioff > 0)
1240  {
1241  memmove (buf,
1242  &buf[ioff],
1243  pp->buffer_pos - ioff);
1244  pp->buffer_pos -= ioff;
1245  ioff = 0;
1246  state_changed = 1;
1247  }
1248  }
1249 END:
1250  if (0 != ioff)
1251  {
1252  memmove (buf,
1253  &buf[ioff],
1254  pp->buffer_pos - ioff);
1255  pp->buffer_pos -= ioff;
1256  }
1257  if (poff < post_data_len)
1258  {
1259  pp->state = PP_Error;
1260  return MHD_NO; /* serious error */
1261  }
1262  return MHD_YES;
1263 }
1264 
1265 
1268  const char *post_data,
1269  size_t post_data_len)
1270 {
1271  if (0 == post_data_len)
1272  return MHD_YES;
1273  if (NULL == pp)
1274  return MHD_NO;
1276  pp->encoding,
1279  return post_process_urlencoded (pp,
1280  post_data,
1281  post_data_len);
1283  pp->encoding,
1286  return post_process_multipart (pp,
1287  post_data,
1288  post_data_len);
1289  /* this should never be reached */
1290  return MHD_NO;
1291 }
1292 
1293 
1296 {
1297  enum MHD_Result ret;
1298 
1299  if (NULL == pp)
1300  return MHD_YES;
1301  if (PP_ProcessValue == pp->state)
1302  {
1303  /* key without terminated value left at the end of the
1304  buffer; fake receiving a termination character to
1305  ensure it is also processed */
1307  "\n",
1308  1);
1309  }
1310  /* These internal strings need cleaning up since
1311  the post-processing may have been interrupted
1312  at any stage */
1313  if ( (pp->xbuf_pos > 0) ||
1314  ( (pp->state != PP_Done) &&
1315  (pp->state != PP_Init) ) )
1316  ret = MHD_NO;
1317  else
1318  ret = MHD_YES;
1319  pp->have = NE_none;
1320  free_unmarked (pp);
1321  if (NULL != pp->nested_boundary)
1322  free (pp->nested_boundary);
1323  free (pp);
1324  return ret;
1325 }
1326 
1327 
1328 /* end of postprocessor.c */
#define MHD_HTTP_HEADER_CONTENT_TYPE
Definition: microhttpd.h:596
#define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
Definition: microhttpd.h:1196
#define MHD_HTTP_POST_ENCODING_FORM_URLENCODED
Definition: microhttpd.h:1194
_MHD_EXTERN enum MHD_Result MHD_destroy_post_processor(struct MHD_PostProcessor *pp)
_MHD_EXTERN enum MHD_Result MHD_post_process(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
_MHD_EXTERN struct MHD_PostProcessor * MHD_create_post_processor(struct MHD_Connection *connection, size_t buffer_size, MHD_PostDataIterator iter, void *iter_cls)
Definition: postprocessor.c:43
_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:1058
void MHD_unescape_plus(char *arg)
Definition: internal.c:123
#define MHD_PANIC(msg)
Definition: internal.h:69
#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
#define _(String)
Definition: mhd_options.h:42
#define _MHD_EXTERN
Definition: mhd_options.h:53
MHD internal shared structures.
macros for mhd_assert()
Header for platform missing functions.
Header for string manipulating helpers.
MHD_Result
Definition: microhttpd.h:158
@ MHD_YES
Definition: microhttpd.h:167
@ MHD_NO
Definition: microhttpd.h:162
_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:2908
@ MHD_POSTDATA_KIND
Definition: microhttpd.h:2276
@ MHD_HEADER_KIND
Definition: microhttpd.h:2260
static enum MHD_Result post_process_multipart(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
static int try_match_header(const char *prefix, size_t prefix_len, char *line, char **suffix)
static int find_boundary(struct MHD_PostProcessor *pp, const char *boundary, size_t blen, size_t *ioffptr, enum PP_State next_state, enum PP_State next_dash_state)
static int process_value_to_boundary(struct MHD_PostProcessor *pp, size_t *ioffptr, const char *boundary, size_t blen, enum PP_State next_state, enum PP_State next_dash_state)
#define XBUF_SIZE
Definition: postprocessor.c:39
static void process_value(struct MHD_PostProcessor *pp, const char *value_start, const char *value_end, const char *last_escape)
static void try_get_value(const char *buf, const char *key, char **destination)
static enum MHD_Result post_process_urlencoded(struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len)
static void free_unmarked(struct MHD_PostProcessor *pp)
static int process_multipart_headers(struct MHD_PostProcessor *pp, size_t *ioffptr, enum PP_State next_state)
Declarations for parsing POST data.
@ RN_Dash
Definition: postprocessor.h:86
@ RN_Inactive
Definition: postprocessor.h:68
@ RN_Full
Definition: postprocessor.h:80
@ RN_OptN
Definition: postprocessor.h:74
@ RN_Dash2
Definition: postprocessor.h:91
@ NE_content_name
@ NE_content_type
@ NE_content_transfer_encoding
@ NE_none
@ NE_content_filename
PP_State
Definition: postprocessor.h:35
@ PP_PerformCleanup
Definition: postprocessor.h:51
@ PP_Error
Definition: postprocessor.h:37
@ PP_Nested_PerformMarking
Definition: postprocessor.h:55
@ PP_ProcessKey
Definition: postprocessor.h:43
@ PP_Init
Definition: postprocessor.h:39
@ PP_PerformCheckMultipart
Definition: postprocessor.h:49
@ PP_Nested_Init
Definition: postprocessor.h:54
@ PP_ProcessValue
Definition: postprocessor.h:44
@ PP_Nested_ProcessEntryHeaders
Definition: postprocessor.h:56
@ PP_Nested_PerformCleanup
Definition: postprocessor.h:58
@ PP_NextBoundary
Definition: postprocessor.h:40
@ PP_ProcessEntryHeaders
Definition: postprocessor.h:48
@ PP_ProcessValueToBoundary
Definition: postprocessor.h:50
@ PP_Done
Definition: postprocessor.h:38
@ PP_Callback
Definition: postprocessor.h:45
@ PP_Nested_ProcessValueToBoundary
Definition: postprocessor.h:57
enum PP_State dash_state
MHD_PostDataIterator ikvi
struct MHD_Connection * connection
const char * boundary
char * content_transfer_encoding
enum NE_State have
enum RN_State skip_rn
enum PP_State state
const char * encoding