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 == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1],
00409 NULL, NULL, NULL, xbuf, pp->value_offset,
00410 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') || (post_data[poff] == '\r'))
00422 {
00423 pp->state = PP_ExpectNewLine;
00424 }
00425 else
00426 {
00427 poff++;
00428 pp->state = PP_Init;
00429 }
00430 }
00431 break;
00432 case PP_ExpectNewLine:
00433 if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
00434 {
00435 poff++;
00436
00437 pp->state = PP_Done;
00438 return MHD_YES;
00439 }
00440 return MHD_NO;
00441 default:
00442 abort ();
00443 }
00444 }
00445 return MHD_YES;
00446 }
00447
00454 static int
00455 try_match_header (const char *prefix, char *line, char **suffix)
00456 {
00457 if (NULL != *suffix)
00458 return MHD_NO;
00459 while (*line != 0)
00460 {
00461 if (0 == strncasecmp (prefix, line, strlen (prefix)))
00462 {
00463 *suffix = strdup (&line[strlen (prefix)]);
00464 return MHD_YES;
00465 }
00466 ++line;
00467 }
00468 return MHD_NO;
00469 }
00470
00471 static int
00472 find_boundary (struct MHD_PostProcessor *pp,
00473 const char *boundary,
00474 size_t blen,
00475 unsigned int *ioffptr,
00476 enum PP_State next_state, enum PP_State next_dash_state)
00477 {
00478 char *buf = (char *) &pp[1];
00479
00480 if (pp->buffer_pos < 2 + blen)
00481 {
00482 if (pp->buffer_pos == pp->buffer_size)
00483 pp->state = PP_Error;
00484 return MHD_NO;
00485 }
00486 if ((0 != memcmp ("--", buf, 2)) || (0 != memcmp (&buf[2], boundary, blen)))
00487 {
00488 pp->state = PP_Error;
00489 return MHD_NO;
00490 }
00491
00492 (*ioffptr) += 2 + blen;
00493
00494 pp->skip_rn = RN_Dash;
00495 pp->state = next_state;
00496 pp->dash_state = next_dash_state;
00497 return MHD_YES;
00498 }
00499
00508 static void
00509 try_get_value (const char *buf, const char *key, char **destination)
00510 {
00511 const char *spos;
00512 const char *bpos;
00513 const char *endv;
00514 size_t klen;
00515 size_t vlen;
00516
00517 if (NULL != *destination)
00518 return;
00519 bpos = buf;
00520 klen = strlen (key);
00521 while (NULL != (spos = strstr (bpos, key)))
00522 {
00523 if ((spos[klen] != '=') || ((spos != buf) && (spos[-1] != ' ')))
00524 {
00525
00526 bpos = spos + 1;
00527 continue;
00528 }
00529 if (spos[klen + 1] != '"')
00530 return;
00531 if (NULL == (endv = strstr (&spos[klen + 2], "\"")))
00532 return;
00533 vlen = endv - spos - klen - 1;
00534 *destination = malloc (vlen);
00535 if (NULL == *destination)
00536 return;
00537 (*destination)[vlen - 1] = '\0';
00538 memcpy (*destination, &spos[klen + 2], vlen - 1);
00539 return;
00540 }
00541 }
00542
00555 static int
00556 process_multipart_headers (struct MHD_PostProcessor *pp,
00557 unsigned int *ioffptr, enum PP_State next_state)
00558 {
00559 char *buf = (char *) &pp[1];
00560 unsigned int newline;
00561
00562 newline = 0;
00563 while ((newline < pp->buffer_pos) &&
00564 (buf[newline] != '\r') && (buf[newline] != '\n'))
00565 newline++;
00566 if (newline == pp->buffer_size)
00567 {
00568 pp->state = PP_Error;
00569 return MHD_NO;
00570 }
00571 if (newline == pp->buffer_pos)
00572 return MHD_NO;
00573 if (newline == 0)
00574 {
00575
00576 pp->skip_rn = RN_Full;
00577 pp->state = next_state;
00578 return MHD_YES;
00579 }
00580
00581 if (buf[newline] == '\r')
00582 pp->skip_rn = RN_OptN;
00583 buf[newline] = '\0';
00584 if (0 == strncasecmp ("Content-disposition: ",
00585 buf, strlen ("Content-disposition: ")))
00586 {
00587 try_get_value (&buf[strlen ("Content-disposition: ")],
00588 "name", &pp->content_name);
00589 try_get_value (&buf[strlen ("Content-disposition: ")],
00590 "filename", &pp->content_filename);
00591 }
00592 else
00593 {
00594 try_match_header ("Content-type: ", buf, &pp->content_type);
00595 try_match_header ("Content-Transfer-Encoding: ",
00596 buf, &pp->content_transfer_encoding);
00597 }
00598 (*ioffptr) += newline + 1;
00599 return MHD_YES;
00600 }
00601
00616 static int
00617 process_value_to_boundary (struct MHD_PostProcessor *pp,
00618 unsigned int *ioffptr,
00619 const char *boundary,
00620 size_t blen,
00621 enum PP_State next_state,
00622 enum PP_State next_dash_state)
00623 {
00624 char *buf = (char *) &pp[1];
00625 unsigned int newline;
00626
00627
00628
00629 newline = 0;
00630 while (1)
00631 {
00632 while ((newline + 4 < pp->buffer_pos) &&
00633 (0 != memcmp ("\r\n--", &buf[newline], 4)))
00634 newline++;
00635 if (newline + pp->blen + 4 <= pp->buffer_pos)
00636 {
00637
00638 if (0 != memcmp (&buf[newline + 4], boundary, pp->blen))
00639 {
00640
00641 newline += 4;
00642 continue;
00643 }
00644 else
00645 {
00646
00647
00648 pp->skip_rn = RN_Dash;
00649 pp->state = next_state;
00650 pp->dash_state = next_dash_state;
00651 (*ioffptr) += pp->blen + 4;
00652 break;
00653 }
00654 }
00655 else
00656 {
00657
00658
00659
00660 if ((newline == 0) && (pp->buffer_pos == pp->buffer_size))
00661 {
00662 pp->state = PP_Error;
00663 return MHD_NO;
00664 }
00665 break;
00666 }
00667 }
00668
00669
00670
00671 if (MHD_NO == pp->ikvi (pp->cls,
00672 MHD_POSTDATA_KIND,
00673 pp->content_name,
00674 pp->content_filename,
00675 pp->content_type,
00676 pp->content_transfer_encoding,
00677 buf, pp->value_offset, newline))
00678 {
00679 pp->state = PP_Error;
00680 return MHD_NO;
00681 }
00682 pp->value_offset += newline;
00683 (*ioffptr) += newline;
00684 return MHD_YES;
00685 }
00686
00687 static void
00688 free_unmarked (struct MHD_PostProcessor *pp)
00689 {
00690 if ((pp->content_name != NULL) && (0 == (pp->have & NE_content_name)))
00691 {
00692 free (pp->content_name);
00693 pp->content_name = NULL;
00694 }
00695 if ((pp->content_type != NULL) && (0 == (pp->have & NE_content_type)))
00696 {
00697 free (pp->content_type);
00698 pp->content_type = NULL;
00699 }
00700 if ((pp->content_filename != NULL) &&
00701 (0 == (pp->have & NE_content_filename)))
00702 {
00703 free (pp->content_filename);
00704 pp->content_filename = NULL;
00705 }
00706 if ((pp->content_transfer_encoding != NULL) &&
00707 (0 == (pp->have & NE_content_transfer_encoding)))
00708 {
00709 free (pp->content_transfer_encoding);
00710 pp->content_transfer_encoding = NULL;
00711 }
00712 }
00713
00717 static int
00718 post_process_multipart (struct MHD_PostProcessor *pp,
00719 const char *post_data, unsigned int post_data_len)
00720 {
00721 char *buf;
00722 unsigned int max;
00723 unsigned int ioff;
00724 unsigned int poff;
00725 int state_changed;
00726
00727 buf = (char *) &pp[1];
00728 ioff = 0;
00729 poff = 0;
00730 state_changed = 1;
00731 while ((poff < post_data_len) ||
00732 ((pp->buffer_pos > 0) && (state_changed != 0)))
00733 {
00734
00735
00736 max = pp->buffer_size - pp->buffer_pos;
00737 if (max > post_data_len - poff)
00738 max = post_data_len - poff;
00739 memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
00740 poff += max;
00741 pp->buffer_pos += max;
00742 if ((max == 0) && (state_changed == 0) && (poff < post_data_len))
00743 {
00744 pp->state = PP_Error;
00745 return MHD_NO;
00746 }
00747 state_changed = 0;
00748
00749
00750 switch (pp->skip_rn)
00751 {
00752 case RN_Inactive:
00753 break;
00754 case RN_OptN:
00755 if (buf[0] == '\n')
00756 {
00757 ioff++;
00758 pp->skip_rn = RN_Inactive;
00759 goto AGAIN;
00760 }
00761 case RN_Dash:
00762 if (buf[0] == '-')
00763 {
00764 ioff++;
00765 pp->skip_rn = RN_Dash2;
00766 goto AGAIN;
00767 }
00768 pp->skip_rn = RN_Full;
00769
00770 case RN_Full:
00771 if (buf[0] == '\r')
00772 {
00773 if ((pp->buffer_pos > 1) && (buf[1] == '\n'))
00774 {
00775 pp->skip_rn = RN_Inactive;
00776 ioff += 2;
00777 }
00778 else
00779 {
00780 pp->skip_rn = RN_OptN;
00781 ioff++;
00782 }
00783 goto AGAIN;
00784 }
00785 if (buf[0] == '\n')
00786 {
00787 ioff++;
00788 pp->skip_rn = RN_Inactive;
00789 goto AGAIN;
00790 }
00791 pp->skip_rn = RN_Inactive;
00792 pp->state = PP_Error;
00793 return MHD_NO;
00794 case RN_Dash2:
00795 if (buf[0] == '-')
00796 {
00797 ioff++;
00798 pp->skip_rn = RN_Full;
00799 pp->state = pp->dash_state;
00800 goto AGAIN;
00801 }
00802 pp->state = PP_Error;
00803 break;
00804 }
00805
00806
00807 switch (pp->state)
00808 {
00809 case PP_Error:
00810 return MHD_NO;
00811 case PP_Done:
00812
00813 pp->state = PP_Error;
00814 return MHD_NO;
00815 case PP_Init:
00816 if (MHD_NO == find_boundary (pp,
00817 pp->boundary,
00818 pp->blen,
00819 &ioff,
00820 PP_ProcessEntryHeaders, PP_Done))
00821 {
00822 if (pp->state == PP_Error)
00823 return MHD_NO;
00824 goto END;
00825 }
00826 break;
00827 case PP_ProcessEntryHeaders:
00828 if (MHD_NO ==
00829 process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart))
00830 {
00831 if (pp->state == PP_Error)
00832 return MHD_NO;
00833 else
00834 goto END;
00835 }
00836 state_changed = 1;
00837 break;
00838 case PP_PerformCheckMultipart:
00839 if ((pp->content_type != NULL) &&
00840 (0 == strncasecmp (pp->content_type,
00841 "multipart/mixed",
00842 strlen ("multipart/mixed"))))
00843 {
00844 pp->nested_boundary = strstr (pp->content_type, "boundary=");
00845 if (pp->nested_boundary == NULL)
00846 {
00847 pp->state = PP_Error;
00848 return MHD_NO;
00849 }
00850 pp->nested_boundary =
00851 strdup (&pp->nested_boundary[strlen ("boundary=")]);
00852 if (pp->nested_boundary == NULL)
00853 {
00854
00855 pp->state = PP_Error;
00856 return MHD_NO;
00857 }
00858
00859
00860 free (pp->content_type);
00861 pp->content_type = NULL;
00862 pp->nlen = strlen (pp->nested_boundary);
00863 pp->state = PP_Nested_Init;
00864 state_changed = 1;
00865 break;
00866 }
00867 pp->state = PP_ProcessValueToBoundary;
00868 pp->value_offset = 0;
00869 state_changed = 1;
00870 break;
00871 case PP_ProcessValueToBoundary:
00872 if (MHD_NO == process_value_to_boundary (pp,
00873 &ioff,
00874 pp->boundary,
00875 pp->blen,
00876 PP_PerformCleanup,
00877 PP_Done))
00878 {
00879 if (pp->state == PP_Error)
00880 return MHD_NO;
00881 break;
00882 }
00883 break;
00884 case PP_PerformCleanup:
00885
00886 pp->have = NE_none;
00887 free_unmarked (pp);
00888 if (pp->nested_boundary != NULL)
00889 {
00890 free (pp->nested_boundary);
00891 pp->nested_boundary = NULL;
00892 }
00893 pp->state = PP_ProcessEntryHeaders;
00894 state_changed = 1;
00895 break;
00896 case PP_Nested_Init:
00897 if (pp->nested_boundary == NULL)
00898 {
00899 pp->state = PP_Error;
00900 return MHD_NO;
00901 }
00902 if (MHD_NO == find_boundary (pp,
00903 pp->nested_boundary,
00904 pp->nlen,
00905 &ioff,
00906 PP_Nested_PerformMarking,
00907 PP_Init ))
00908 {
00909 if (pp->state == PP_Error)
00910 return MHD_NO;
00911 goto END;
00912 }
00913 break;
00914 case PP_Nested_PerformMarking:
00915
00916
00917 pp->have = NE_none;
00918 if (pp->content_name != NULL)
00919 pp->have |= NE_content_name;
00920 if (pp->content_type != NULL)
00921 pp->have |= NE_content_type;
00922 if (pp->content_filename != NULL)
00923 pp->have |= NE_content_filename;
00924 if (pp->content_transfer_encoding != NULL)
00925 pp->have |= NE_content_transfer_encoding;
00926 pp->state = PP_Nested_ProcessEntryHeaders;
00927 state_changed = 1;
00928 break;
00929 case PP_Nested_ProcessEntryHeaders:
00930 pp->value_offset = 0;
00931 if (MHD_NO ==
00932 process_multipart_headers (pp, &ioff,
00933 PP_Nested_ProcessValueToBoundary))
00934 {
00935 if (pp->state == PP_Error)
00936 return MHD_NO;
00937 else
00938 goto END;
00939 }
00940 state_changed = 1;
00941 break;
00942 case PP_Nested_ProcessValueToBoundary:
00943 if (MHD_NO == process_value_to_boundary (pp,
00944 &ioff,
00945 pp->nested_boundary,
00946 pp->nlen,
00947 PP_Nested_PerformCleanup,
00948 PP_Init))
00949 {
00950 if (pp->state == PP_Error)
00951 return MHD_NO;
00952 break;
00953 }
00954 break;
00955 case PP_Nested_PerformCleanup:
00956 free_unmarked (pp);
00957 pp->state = PP_Nested_ProcessEntryHeaders;
00958 state_changed = 1;
00959 break;
00960 default:
00961 abort ();
00962 }
00963 AGAIN:
00964 if (ioff > 0)
00965 {
00966 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
00967 pp->buffer_pos -= ioff;
00968 ioff = 0;
00969 state_changed = 1;
00970 }
00971 }
00972 END:
00973 if (ioff != 0)
00974 {
00975 memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
00976 pp->buffer_pos -= ioff;
00977 }
00978 if (poff < post_data_len)
00979 {
00980 pp->state = PP_Error;
00981 return MHD_NO;
00982 }
00983 return MHD_YES;
00984 }
00985
01000 int
01001 MHD_post_process (struct MHD_PostProcessor *pp,
01002 const char *post_data, unsigned int post_data_len)
01003 {
01004 if (post_data_len == 0)
01005 return MHD_YES;
01006 if (pp == NULL)
01007 return MHD_NO;
01008 if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, pp->encoding))
01009 return post_process_urlencoded (pp, post_data, post_data_len);
01010 if (0 ==
01011 strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, pp->encoding,
01012 strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
01013 return post_process_multipart (pp, post_data, post_data_len);
01014
01015 return MHD_NO;
01016 }
01017
01021 int
01022 MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
01023 {
01024 int ret;
01025
01026
01027
01028
01029 if ((pp->xbuf_pos > 0) || (pp->state != PP_Done))
01030 ret = MHD_NO;
01031 else
01032 ret = MHD_YES;
01033 pp->have = NE_none;
01034 free_unmarked (pp);
01035 if (pp->nested_boundary != NULL)
01036 free (pp->nested_boundary);
01037 free (pp);
01038 return ret;
01039 }
01040
01041