00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00026 #include "internal.h"
00027
00031 #define XBUF_SIZE 1024
00032
00036 enum PP_State
00037 {
00038
00039 PP_Error,
00040 PP_Done,
00041 PP_Init,
00042
00043
00044 PP_ProcessValue,
00045 PP_ExpectNewLine,
00046
00047
00048 PP_ProcessEntryHeaders,
00049 PP_PerformCheckMultipart,
00050 PP_ProcessValueToBoundary,
00051 PP_PerformCleanup,
00052
00053
00054 PP_Nested_Init,
00055 PP_Nested_PerformMarking,
00056 PP_Nested_ProcessEntryHeaders,
00057 PP_Nested_ProcessValueToBoundary,
00058 PP_Nested_PerformCleanup,
00059
00060 };
00061
00062 enum RN_State
00063 {
00067 RN_Inactive = 0,
00068
00073 RN_OptN = 1,
00074
00079 RN_Full = 2,
00080
00085 RN_Dash = 3,
00086
00090 RN_Dash2 = 4,
00091 };
00092
00098 enum NE_State
00099 {
00100 NE_none = 0,
00101 NE_content_name = 1,
00102 NE_content_type = 2,
00103 NE_content_filename = 4,
00104 NE_content_transfer_encoding = 8,
00105 };
00106
00111 struct MHD_PostProcessor
00112 {
00113
00118 struct MHD_Connection *connection;
00119
00123 MHD_PostDataIterator ikvi;
00124
00128 void *cls;
00129
00134 const char *encoding;
00135
00139 const char *boundary;
00140
00144 char *nested_boundary;
00145
00149 char *content_name;
00150
00154 char *content_type;
00155
00159 char *content_filename;
00160
00164 char *content_transfer_encoding;
00165
00170 char xbuf[8];
00171
00175 unsigned int buffer_size;
00176
00180 unsigned int buffer_pos;
00181
00185 unsigned int xbuf_pos;
00186
00190 unsigned int value_offset;
00191
00195 size_t blen;
00196
00200 size_t nlen;
00201
00205 enum PP_State state;
00206
00213 enum RN_State skip_rn;
00214
00219 enum PP_State dash_state;
00220
00225 enum NE_State have;
00226
00227 };
00228
00229
00248 struct MHD_PostProcessor *
00249 MHD_create_post_processor (struct MHD_Connection *connection,
00250 unsigned int buffer_size,
00251 MHD_PostDataIterator ikvi, void *cls)
00252 {
00253 struct MHD_PostProcessor *ret;
00254 const char *encoding;
00255 const char *boundary;
00256 size_t blen;
00257
00258 if ((buffer_size < 256) || (connection == NULL) || (ikvi == NULL))
00259 abort ();
00260 encoding = MHD_lookup_connection_value (connection,
00261 MHD_HEADER_KIND,
00262 MHD_HTTP_HEADER_CONTENT_TYPE);
00263 if (encoding == NULL)
00264 return NULL;
00265 boundary = NULL;
00266 if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, encoding))
00267 {
00268 if (0 !=
00269 strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding,
00270 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
00271 return NULL;
00272 boundary =
00273 &encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
00274
00275 if (NULL != strstr (boundary, "boundary="))
00276 boundary = strstr (boundary, "boundary=") + strlen ("boundary=");
00277 else
00278 return NULL;
00279 blen = strlen (boundary);
00280 if ((blen == 0) || (blen * 2 + 2 > buffer_size))
00281 return NULL;
00282 }
00283 else
00284 blen = 0;
00285 ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size + 1);
00286 if (ret == NULL)
00287 return NULL;
00288 memset (ret, 0, sizeof (struct MHD_PostProcessor) + buffer_size + 1);
00289 ret->connection = connection;
00290 ret->ikvi = ikvi;
00291 ret->cls = cls;
00292 ret->encoding = encoding;
00293 ret->buffer_size = buffer_size;
00294 ret->state = PP_Init;
00295 ret->blen = blen;
00296 ret->boundary = boundary;
00297 ret->skip_rn = RN_Inactive;
00298 return ret;
00299 }
00300
00304 static int
00305 post_process_urlencoded (struct MHD_PostProcessor *pp,
00306 const char *post_data, unsigned int post_data_len)
00307 {
00308 unsigned int equals;
00309 unsigned int amper;
00310 unsigned int poff;
00311 unsigned int xoff;
00312 unsigned int delta;
00313 int end_of_value_found;
00314 char *buf;
00315 char xbuf[XBUF_SIZE + 1];
00316
00317 buf = (char *) &pp[1];
00318 poff = 0;
00319 while (poff < post_data_len)
00320 {
00321 switch (pp->state)
00322 {
00323 case PP_Error:
00324 return MHD_NO;
00325 case PP_Done:
00326
00327 pp->state = PP_Error;
00328 return MHD_NO;
00329 case PP_Init:
00330 equals = 0;
00331 while ((equals + poff < post_data_len) &&
00332 (post_data[equals + poff] != '='))
00333 equals++;
00334 if (equals + pp->buffer_pos > pp->buffer_size)
00335 {
00336 pp->state = PP_Error;
00337 return MHD_NO;
00338 }
00339 memcpy (&buf[pp->buffer_pos], &post_data[poff], equals);
00340 pp->buffer_pos += equals;
00341 if (equals + poff == post_data_len)
00342 return MHD_YES;
00343 buf[pp->buffer_pos] = '\0';
00344 pp->buffer_pos = 0;
00345 MHD_http_unescape (buf);
00346 poff += equals + 1;
00347 pp->state = PP_ProcessValue;
00348 pp->value_offset = 0;
00349 break;
00350 case PP_ProcessValue:
00351
00352 memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
00353 xoff = pp->xbuf_pos;
00354 pp->xbuf_pos = 0;
00355
00356
00357 amper = 0;
00358 while ((amper + poff < post_data_len) &&
00359 (amper < XBUF_SIZE) &&
00360 (post_data[amper + poff] != '&') &&
00361 (post_data[amper + poff] != '\n') &&
00362 (post_data[amper + poff] != '\r'))
00363 amper++;
00364 end_of_value_found = ( (amper + poff < post_data_len) &&
00365 ( (post_data[amper + poff] == '&') ||
00366 (post_data[amper + poff] == '\n') ||
00367 (post_data[amper + poff] == '\r') ) );
00368
00369
00370 delta = amper;
00371 if (delta > XBUF_SIZE - xoff)
00372 delta = XBUF_SIZE - xoff;
00373
00374
00375 memcpy (&xbuf[xoff], &post_data[poff], delta);
00376 xoff += delta;
00377 poff += delta;
00378
00379
00380
00381
00382 delta = xoff;
00383 if ((delta > 0) && (xbuf[delta - 1] == '%'))
00384 delta--;
00385 else if ((delta > 1) && (xbuf[delta - 2] == '%'))
00386 delta -= 2;
00387
00388
00389
00390 if (delta < xoff)
00391 {
00392 memcpy (pp->xbuf, &xbuf[delta], xoff - delta);
00393 pp->xbuf_pos = xoff - delta;
00394 xoff = delta;
00395 }
00396
00397
00398
00399
00400 if ((xoff == 0) && (poff == post_data_len))
00401 continue;
00402
00403
00404 xbuf[xoff] = '\0';
00405 MHD_http_unescape (xbuf);
00406
00407
00408 if (MHD_NO ==
00409 pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1],
00410 NULL, NULL, NULL, xbuf, pp->value_offset, xoff))
00411 {
00412 pp->state = PP_Error;
00413 return MHD_NO;
00414 }
00415 pp->value_offset += xoff;
00416
00417
00418 if (end_of_value_found)
00419 {
00420
00421 if ((post_data[poff] == '\n') ||
00422 (post_data[poff] == '\r'))
00423 {
00424 pp->state = PP_ExpectNewLine;
00425 }
00426 else
00427 {
00428 poff++;
00429 pp->state = PP_Init;
00430 }
00431 }
00432 break;
00433 case PP_ExpectNewLine:
00434 if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
00435 {
00436 poff++;
00437
00438 pp->state = PP_Done;
00439 return MHD_YES;
00440 }
00441 return MHD_NO;
00442 default:
00443 abort ();
00444 }
00445 }
00446 return MHD_YES;
00447 }
00448
00455 static int
00456 try_match_header (const char *prefix, char *line, char **suffix)
00457 {
00458 if (NULL != *suffix)
00459 return MHD_NO;
00460 while (*line != 0)
00461 {
00462 if (0 == strncasecmp (prefix, line, strlen (prefix)))
00463 {
00464 *suffix = strdup (&line[strlen (prefix)]);
00465 return MHD_YES;
00466 }
00467 ++line;
00468 }
00469 return MHD_NO;
00470 }
00471
00472 static int
00473 find_boundary (struct MHD_PostProcessor *pp,
00474 const char *boundary,
00475 size_t blen,
00476 unsigned int *ioffptr,
00477 enum PP_State next_state, enum PP_State next_dash_state)
00478 {
00479 char *buf = (char *) &pp[1];
00480
00481 if (pp->buffer_pos < 2 + blen)
00482 {
00483 if (pp->buffer_pos == pp->buffer_size)
00484 pp->state = PP_Error;
00485 return MHD_NO;
00486 }
00487 if ((0 != memcmp ("--", buf, 2)) || (0 != memcmp (&buf[2], boundary, blen)))
00488 {
00489 pp->state = PP_Error;
00490 return MHD_NO;
00491 }
00492
00493 (*ioffptr) += 2 + blen;
00494
00495 pp->skip_rn = RN_Dash;
00496 pp->state = next_state;
00497 pp->dash_state = next_dash_state;
00498 return MHD_YES;
00499 }
00500
00509 static void
00510 try_get_value (const char *buf, const char *key, char **destination)
00511 {
00512 const char *spos;
00513 const char *bpos;
00514 const char *endv;
00515 size_t klen;
00516 size_t vlen;
00517
00518 if (NULL != *destination)
00519 return;
00520 bpos = buf;
00521 klen = strlen (key);
00522 while (NULL != (spos = strstr (bpos, key)))
00523 {
00524 if ((spos[klen] != '=') || ((spos != buf) && (spos[-1] != ' ')))
00525 {
00526
00527 bpos = spos + 1;
00528 continue;
00529 }
00530 if (spos[klen + 1] != '"')
00531 return;
00532 if (NULL == (endv = strstr (&spos[klen + 2], "\"")))
00533 return;
00534 vlen = endv - spos - klen - 1;
00535 *destination = malloc (vlen);
00536 if (NULL == *destination)
00537 return;
00538 (*destination)[vlen - 1] = '\0';
00539 memcpy (*destination, &spos[klen + 2], vlen - 1);
00540 return;
00541 }
00542 }
00543
00556 static int
00557 process_multipart_headers (struct MHD_PostProcessor *pp,
00558 unsigned int *ioffptr, enum PP_State next_state)
00559 {
00560 char *buf = (char *) &pp[1];
00561 unsigned int newline;
00562
00563 newline = 0;
00564 while ((newline < pp->buffer_pos) &&
00565 (buf[newline] != '\r') && (buf[newline] != '\n'))
00566 newline++;
00567 if (newline == pp->buffer_size)
00568 {
00569 pp->state = PP_Error;
00570 return MHD_NO;
00571 }
00572 if (newline == pp->buffer_pos)
00573 return MHD_NO;
00574 if (newline == 0)
00575 {
00576
00577 pp->skip_rn = RN_Full;
00578 pp->state = next_state;
00579 return MHD_YES;
00580 }
00581
00582 if (buf[newline] == '\r')
00583 pp->skip_rn = RN_OptN;
00584 buf[newline] = '\0';
00585 if (0 == strncasecmp ("Content-disposition: ",
00586 buf, strlen ("Content-disposition: ")))
00587 {
00588 try_get_value (&buf[strlen ("Content-disposition: ")],
00589 "name", &pp->content_name);
00590 try_get_value (&buf[strlen ("Content-disposition: ")],
00591 "filename", &pp->content_filename);
00592 }
00593 else
00594 {
00595 try_match_header ("Content-type: ", buf, &pp->content_type);
00596 try_match_header ("Content-Transfer-Encoding: ",
00597 buf, &pp->content_transfer_encoding);
00598 }
00599 (*ioffptr) += newline + 1;
00600 return MHD_YES;
00601 }
00602
00617 static int
00618 process_value_to_boundary (struct MHD_PostProcessor *pp,
00619 unsigned int *ioffptr,
00620 const char *boundary,
00621 size_t blen,
00622 enum PP_State next_state,
00623 enum PP_State next_dash_state)
00624 {
00625 char *buf = (char *) &pp[1];
00626 unsigned int newline;
00627
00628
00629
00630 newline = 0;
00631 while (1)
00632 {
00633 while ((newline + 4 < pp->buffer_pos) &&
00634 (0 != memcmp ("\r\n--", &buf[newline], 4)))
00635 newline++;
00636 if (newline + pp->blen + 4 <= pp->buffer_pos)
00637 {
00638
00639 if (0 != memcmp (&buf[newline + 4], boundary, pp->blen))
00640 {
00641
00642 newline += 4;
00643 continue;
00644 }
00645 else
00646 {
00647
00648
00649 pp->skip_rn = RN_Dash;
00650 pp->state = next_state;
00651 pp->dash_state = next_dash_state;
00652 (*ioffptr) += pp->blen + 4;
00653 break;
00654 }
00655 }
00656 else
00657 {
00658
00659
00660
00661 if ((newline == 0) && (pp->buffer_pos == pp->buffer_size))
00662 {
00663 pp->state = PP_Error;
00664 return MHD_NO;
00665 }
00666 break;
00667 }
00668 }
00669
00670
00671
00672 if (MHD_NO == pp->ikvi (pp->cls,
00673 MHD_POSTDATA_KIND,
00674 pp->content_name,
00675 pp->content_filename,
00676 pp->content_type,
00677 pp->content_transfer_encoding,
00678 buf, pp->value_offset, newline))
00679 {
00680 pp->state = PP_Error;
00681 return MHD_NO;
00682 }
00683 pp->value_offset += newline;
00684 (*ioffptr) += newline;
00685 return MHD_YES;
00686 }
00687
00688 static void
00689 free_unmarked (struct MHD_PostProcessor *pp)
00690 {
00691 if ((pp->content_name != NULL) && (0 == (pp->have & NE_content_name)))
00692 {
00693 free (pp->content_name);
00694 pp->content_name = NULL;
00695 }
00696 if ((pp->content_type != NULL) && (0 == (pp->have & NE_content_type)))
00697 {
00698 free (pp->content_type);
00699 pp->content_type = NULL;
00700 }
00701 if ((pp->content_filename != NULL) &&
00702 (0 == (pp->have & NE_content_filename)))
00703 {
00704 free (pp->content_filename);
00705 pp->content_filename = NULL;
00706 }
00707 if ((pp->content_transfer_encoding != NULL) &&
00708 (0 == (pp->have & NE_content_transfer_encoding)))
00709 {
00710 free (pp->content_transfer_encoding);
00711 pp->content_transfer_encoding = NULL;
00712 }
00713 }
00714
00718 static int
00719 post_process_multipart (struct MHD_PostProcessor *pp,
00720 const char *post_data, unsigned int post_data_len)
00721 {
00722 char *buf;
00723 unsigned int max;
00724 unsigned int ioff;
00725 unsigned int poff;
00726 int state_changed;
00727
00728 buf = (char *) &pp[1];
00729 ioff = 0;
00730 poff = 0;
00731 state_changed = 1;
00732 while ((poff < post_data_len) ||
00733 ((pp->buffer_pos > 0) && (state_changed != 0)))
00734 {
00735
00736
00737 max = pp->buffer_size - pp->buffer_pos;
00738 if (max > post_data_len - poff)
00739 max = post_data_len - poff;
00740 memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
00741 poff += max;
00742 pp->buffer_pos += max;
00743 if ((max == 0) && (state_changed == 0) && (poff < post_data_len))
00744 {
00745 pp->state = PP_Error;
00746 return MHD_NO;
00747 }
00748 state_changed = 0;
00749
00750
00751 switch (pp->skip_rn)
00752 {
00753 case RN_Inactive:
00754 break;
00755 case RN_OptN:
00756 if (buf[0] == '\n')
00757 {
00758 ioff++;
00759 pp->skip_rn = RN_Inactive;
00760 goto AGAIN;
00761 }
00762 case RN_Dash:
00763 if (buf[0] == '-')
00764 {
00765 ioff++;
00766 pp->skip_rn = RN_Dash2;
00767 goto AGAIN;
00768 }
00769 pp->skip_rn = RN_Full;
00770
00771 case RN_Full:
00772 if (buf[0] == '\r')
00773 {
00774 if ((pp->buffer_pos > 1) && (buf[1] == '\n'))
00775 {
00776 pp->skip_rn = RN_Inactive;
00777 ioff += 2;
00778 }
00779 else
00780 {
00781 pp->skip_rn = RN_OptN;
00782 ioff++;
00783 }
00784 goto AGAIN;
00785 }
00786 if (buf[0] == '\n')
00787 {
00788 ioff++;
00789 pp->skip_rn = RN_Inactive;
00790 goto AGAIN;
00791 }
00792 pp->skip_rn = RN_Inactive;
00793 pp->state = PP_Error;
00794 return MHD_NO;
00795 case RN_Dash2:
00796 if (buf[0] == '-')
00797 {
00798 ioff++;
00799 pp->skip_rn = RN_Full;
00800 pp->state = pp->dash_state;
00801 goto AGAIN;
00802 }
00803 pp->state = PP_Error;
00804 break;
00805 }
00806
00807
00808 switch (pp->state)
00809 {
00810 case PP_Error:
00811 return MHD_NO;
00812 case PP_Done:
00813
00814 pp->state = PP_Error;
00815 return MHD_NO;
00816 case PP_Init:
00817 if (MHD_NO == find_boundary (pp,
00818 pp->boundary,
00819 pp->blen,
00820 &ioff,
00821 PP_ProcessEntryHeaders, PP_Done))
00822 {
00823 if (pp->state == PP_Error)
00824 return MHD_NO;
00825 goto END;
00826 }
00827 break;
00828 case PP_ProcessEntryHeaders:
00829 if (MHD_NO ==
00830 process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart))
00831 {
00832 if (pp->state == PP_Error)
00833 return MHD_NO;
00834 else
00835 goto END;
00836 }
00837 state_changed = 1;
00838 break;
00839 case PP_PerformCheckMultipart:
00840 if ((pp->content_type != NULL) &&
00841 (0 == strncasecmp (pp->content_type,
00842 "multipart/mixed",
00843 strlen ("multipart/mixed"))))
00844 {
00845 pp->nested_boundary = strstr (pp->content_type, "boundary=");
00846 if (pp->nested_boundary == NULL)
00847 {
00848 pp->state = PP_Error;
00849 return MHD_NO;
00850 }
00851 pp->nested_boundary =
00852 strdup (&pp->nested_boundary[strlen ("boundary=")]);
00853 if (pp->nested_boundary == NULL)
00854 {
00855
00856 pp->state = PP_Error;
00857 return MHD_NO;
00858 }
00859
00860
00861 free (pp->content_type);
00862 pp->content_type = NULL;
00863 pp->nlen = strlen (pp->nested_boundary);
00864 pp->state = PP_Nested_Init;
00865 state_changed = 1;
00866 break;
00867 }
00868 pp->state = PP_ProcessValueToBoundary;
00869 pp->value_offset = 0;
00870 state_changed = 1;
00871 break;
00872 case PP_ProcessValueToBoundary:
00873 if (MHD_NO == process_value_to_boundary (pp,
00874 &ioff,
00875 pp->boundary,
00876 pp->blen,
00877 PP_PerformCleanup,
00878 PP_Done))
00879 {
00880 if (pp->state == PP_Error)
00881 return MHD_NO;
00882 break;
00883 }
00884 break;
00885 case PP_PerformCleanup:
00886
00887 pp->have = NE_none;
00888 free_unmarked (pp);
00889 if (pp->nested_boundary != NULL)
00890 {
00891 free (pp->nested_boundary);
00892 pp->nested_boundary = NULL;
00893 }
00894 pp->state = PP_ProcessEntryHeaders;
00895 state_changed = 1;
00896 break;
00897 case PP_Nested_Init:
00898 if (pp->nested_boundary == NULL)
00899 {
00900 pp->state = PP_Error;
00901 return MHD_NO;
00902 }
00903 if (MHD_NO == find_boundary (pp,
00904 pp->nested_boundary,
00905 pp->nlen,
00906 &ioff,
00907 PP_Nested_PerformMarking,
00908 PP_Init ))
00909 {
00910 if (pp->state == PP_Error)
00911 return MHD_NO;
00912 goto END;
00913 }
00914 break;
00915 case PP_Nested_PerformMarking:
00916
00917
00918 pp->have = NE_none;
00919 if (pp->content_name != NULL)
00920 pp->have |= NE_content_name;
00921 if (pp->content_type != NULL)
00922 pp->have |= NE_content_type;
00923 if (pp->content_filename != NULL)
00924 pp->have |= NE_content_filename;
00925 if (pp->content_transfer_encoding != NULL)
00926 pp->have |= NE_content_transfer_encoding;
00927 pp->state = PP_Nested_ProcessEntryHeaders;
00928 state_changed = 1;
00929 break;
00930 case PP_Nested_ProcessEntryHeaders:
00931 pp->value_offset = 0;
00932 if (MHD_NO ==
00933 process_multipart_headers (pp, &ioff,
00934 PP_Nested_ProcessValueToBoundary))
00935 {
00936 if (pp->state == PP_Error)
00937 return MHD_NO;
00938 else
00939 goto END;
00940 }
00941 state_changed = 1;
00942 break;
00943 case PP_Nested_ProcessValueToBoundary:
00944 if (MHD_NO == process_value_to_boundary (pp,
00945 &ioff,
00946 pp->nested_boundary,
00947 pp->nlen,
00948 PP_Nested_PerformCleanup,
00949 PP_Init))
00950 {
00951 if (pp->state == PP_Error)
00952 return MHD_NO;
00953 break;
00954 }
00955 break;
00956 case PP_Nested_PerformCleanup:
00957 free_unmarked (pp);
00958 pp->state = PP_Nested_ProcessEntryHeaders;
00959 state_changed = 1;
00960 break;
00961 default:
00962 abort ();
00963 }
00964 AGAIN:
00965 if (ioff > 0)
00966 {
00967 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
00968 pp->buffer_pos -= ioff;
00969 ioff = 0;
00970 state_changed = 1;
00971 }
00972 }
00973 END:
00974 if (ioff != 0)
00975 {
00976 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
00977 pp->buffer_pos -= ioff;
00978 }
00979 if (poff < post_data_len)
00980 {
00981 pp->state = PP_Error;
00982 return MHD_NO;
00983 }
00984 return MHD_YES;
00985 }
00986
01001 int
01002 MHD_post_process (struct MHD_PostProcessor *pp,
01003 const char *post_data, unsigned int post_data_len)
01004 {
01005 if (post_data_len == 0)
01006 return MHD_YES;
01007 if (pp == NULL)
01008 return MHD_NO;
01009 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, pp->encoding))
01010 return post_process_urlencoded (pp, post_data, post_data_len);
01011 if (0 ==
01012 strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, pp->encoding,
01013 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
01014 return post_process_multipart (pp, post_data, post_data_len);
01015
01016 return MHD_NO;
01017 }
01018
01022 int
01023 MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
01024 {
01025 int ret;
01026
01027
01028
01029
01030 if ( (pp->xbuf_pos > 0) ||
01031 (pp->state != PP_Done) )
01032 ret = MHD_NO;
01033 else
01034 ret = MHD_YES;
01035 pp->have = NE_none;
01036 free_unmarked (pp);
01037 if (pp->nested_boundary != NULL)
01038 free (pp->nested_boundary);
01039 free (pp);
01040 return ret;
01041 }
01042
01043