libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

post_parser_funcs.c (113869B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2024 Evgeny Grin (Karlson2k)
      5 
      6   GNU libmicrohttpd 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   GNU libmicrohttpd 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   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 
     39 /**
     40  * @file src/mhd2/post_parser_funcs.c
     41  * @brief  The implementation of internal POST parser functions
     42  * @author Karlson2k (Evgeny Grin)
     43  */
     44 
     45 
     46 #include "mhd_sys_options.h"
     47 
     48 #include "post_parser_funcs.h"
     49 
     50 #include "mhd_assert.h"
     51 #include "mhd_unreachable.h"
     52 
     53 #include "mhd_post_parser.h"
     54 
     55 #include <string.h>
     56 
     57 #include "mhd_action.h"
     58 #include "mhd_request.h"
     59 #include "mhd_connection.h"
     60 #include "mhd_daemon.h"
     61 
     62 #include "mhd_str_macros.h"
     63 
     64 #include "mhd_str.h"
     65 #include "daemon_logger.h"
     66 #include "stream_funcs.h"
     67 #include "stream_process_request.h"
     68 
     69 #include "daemon_funcs.h"
     70 #include "request_get_value.h"
     71 
     72 /**
     73  * The result of 'multipart/form-data' processing
     74  */
     75 enum MHD_FIXED_ENUM_ mhd_MPartDetectResult
     76 {
     77   /**
     78    * Sting processed successfully, boundary detected
     79    */
     80   mhd_MPART_DET_OK = 0
     81   ,
     82   /**
     83    * Error processing string, the error result is set
     84    */
     85   mhd_MPART_DET_ERROR_SET
     86   ,
     87   /**
     88    * The string is not 'multipart/form-data' header
     89    */
     90   mhd_MPART_DET_NO_MPART
     91 };
     92 
     93 
     94 /**
     95  * Process 'Content-Type:' header value as 'multipart/form-data' data to
     96  * prepare POST parsing data, including setting correct 'boundary' value
     97  * @param c the stream to use
     98  * @param h_cnt_tp the 'Content-Type:' header value string
     99  * @return 'mhd_MPART_DET_OK' if processed successfully and boundary has been
    100  *                             detected and set,
    101  *         'mhd_MPART_DET_ERROR_SET' is has some error in processing which
    102  *                                    resulted in specific error set in
    103  *                                    the stream,
    104  *         'mhd_MPART_DET_NO_MPART' is string is not 'multipart/form-data' data
    105  */
    106 static MHD_FN_PAR_NONNULL_ALL_ enum mhd_MPartDetectResult
    107 process_mpart_header (struct MHD_Connection *restrict c,
    108                       const struct MHD_String *restrict h_cnt_tp)
    109 {
    110   static const struct MHD_String mpart_token =
    111     mhd_MSTR_INIT ("multipart/form-data");
    112   static const struct MHD_String mpart_bound_par =
    113     mhd_MSTR_INIT ("boundary");
    114   struct mhd_BufferConst mpart_bound;
    115   bool mpart_bound_quoted;
    116   enum mhd_StingStartsWithTokenResult res;
    117   char *buf;
    118 
    119   mhd_assert (NULL != h_cnt_tp->cstr);
    120 
    121   res = mhd_str_starts_with_token_req_param (h_cnt_tp,
    122                                              &mpart_token,
    123                                              &mpart_bound_par,
    124                                              &mpart_bound,
    125                                              &mpart_bound_quoted);
    126 
    127   if (mhd_STR_STARTS_W_TOKEN_NO_TOKEN == res)
    128     return mhd_MPART_DET_NO_MPART;
    129 
    130   if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT == res)
    131   {
    132     mhd_LOG_PRINT (c->daemon, \
    133                    MHD_SC_REQ_POST_PARSE_FAILED_HEADER_MISFORMED, \
    134                    mhd_LOG_FMT ("The request POST data cannot be parsed " \
    135                                 "because 'Content-Type: " \
    136                                 "multipart/form-data' header is " \
    137                                 "misformed: %.*s%s"), \
    138                    (int) ((h_cnt_tp->len <= 127) ? h_cnt_tp->len : 127),
    139                    h_cnt_tp->cstr,
    140                    (h_cnt_tp->len <= 127) ? "" : "...");
    141     c->rq.u_proc.post.parse_result =
    142       MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED;
    143     return mhd_MPART_DET_ERROR_SET;
    144   }
    145 
    146   mhd_assert (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN == res);
    147 
    148   if (0 == mpart_bound.size)
    149   {
    150     mhd_LOG_MSG (c->daemon, \
    151                  MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NO_BOUNDARY, \
    152                  "The request POST data cannot be parsed because " \
    153                  "'Content-Type: multipart/form-data' header has " \
    154                  "no 'boundary' parameter value.");
    155     c->rq.u_proc.post.parse_result =
    156       MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY;
    157     return mhd_MPART_DET_ERROR_SET;
    158   }
    159 
    160   mhd_assert (NULL != mpart_bound.data);
    161 
    162   buf = (char *)
    163         mhd_stream_alloc_memory (c,
    164                                  mpart_bound.size + 4);
    165   if (NULL == buf)
    166   {
    167     /* It is very low probability that pool would not have memory just
    168      * to held the small boundary string. While it could be possible
    169      * to allocate memory from "large buffer", it would over-complicate
    170      * code here and at freeing part. */
    171     mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM, \
    172                  "The request POST data cannot be parsed because " \
    173                  "there is not enough pool memory.");
    174     c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM;
    175     return mhd_MPART_DET_ERROR_SET;
    176   }
    177 
    178   c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
    179 
    180   buf[0] = '\r';
    181   buf[1] = '\n';
    182   buf[2] = '-';
    183   buf[3] = '-';
    184 
    185   if (! mpart_bound_quoted)
    186   {
    187     memcpy (buf + 4,
    188             mpart_bound.data,
    189             mpart_bound.size);
    190     c->rq.u_proc.post.e_d.m_form.delim.data = buf;
    191     c->rq.u_proc.post.e_d.m_form.delim.size = mpart_bound.size + 4;
    192   }
    193   else
    194   {
    195     size_t unq_size;
    196     mhd_assert (2 <= mpart_bound.size); /* At least one char and at least one '\' */
    197 
    198     unq_size = mhd_str_unquote (mpart_bound.data,
    199                                 mpart_bound.size,
    200                                 buf + 4);
    201     c->rq.u_proc.post.e_d.m_form.delim.data = buf;
    202     c->rq.u_proc.post.e_d.m_form.delim.size = unq_size + 4;
    203   }
    204   mhd_assert (4 < c->rq.u_proc.post.e_d.m_form.delim.size);
    205   return mhd_MPART_DET_OK;
    206 }
    207 
    208 
    209 /**
    210  * Detect used POST encoding and 'boundary' for 'multipart/form-data'.
    211  * @param c the stream to use
    212  * @return 'true' if detected successfully,
    213  *         'false' if POST encoding cannot be detected
    214  */
    215 static MHD_FN_PAR_NONNULL_ (1) bool
    216 detect_post_enc (struct MHD_Connection *restrict c)
    217 {
    218   struct MHD_StringNullable hdr_cnt_tp;
    219 
    220   mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage);
    221 
    222   if (! mhd_request_get_value_st (&(c->rq),
    223                                   MHD_VK_HEADER,
    224                                   MHD_HTTP_HEADER_CONTENT_TYPE,
    225                                   &hdr_cnt_tp))
    226   {
    227     mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \
    228                  "The request POST data cannot be parsed because " \
    229                  "the request has no 'Content-Type:' header and no " \
    230                  "explicit POST encoding is set.");
    231     c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE;
    232     return false;  /* The "Content-Type:" is not defined by the client */
    233   }
    234 
    235   mhd_assert (NULL != hdr_cnt_tp.cstr);
    236 
    237   if (mhd_str_equal_caseless_n_st ("application/x-www-form-urlencoded",
    238                                    hdr_cnt_tp.cstr,
    239                                    hdr_cnt_tp.len))
    240   {
    241     c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
    242     return true;
    243   }
    244 
    245   if (1)
    246   {
    247     enum mhd_MPartDetectResult res;
    248 
    249     res = process_mpart_header (c,
    250                                 (const struct MHD_String *) (const void *)
    251                                 &hdr_cnt_tp);
    252 
    253     if (mhd_MPART_DET_OK == res)
    254       return true;
    255 
    256     if (mhd_MPART_DET_ERROR_SET == res)
    257       return false;
    258 
    259     mhd_assert (mhd_MPART_DET_NO_MPART == res);
    260   }
    261 
    262   if (1)
    263   {
    264     static const struct MHD_String txt_tkn = mhd_MSTR_INIT ("text/plain");
    265     struct MHD_String h_cnt_tp_copy;
    266     mhd_assert (NULL != hdr_cnt_tp.cstr);
    267     h_cnt_tp_copy.len = hdr_cnt_tp.len;
    268     h_cnt_tp_copy.cstr = hdr_cnt_tp.cstr;
    269 
    270     if (mhd_str_starts_with_token_opt_param (&h_cnt_tp_copy,
    271                                              &txt_tkn))
    272     {
    273       c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_TEXT_PLAIN;
    274       return true;
    275     }
    276   }
    277   mhd_LOG_MSG (c->daemon, \
    278                MHD_SC_REQ_POST_PARSE_FAILED_UNKNOWN_CNTN_TYPE, \
    279                "The request POST data cannot be parsed because " \
    280                "'Content-Type' header value is unknown or unsupported.");
    281   c->rq.u_proc.post.parse_result =
    282     MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE;
    283   return false;
    284 }
    285 
    286 
    287 /**
    288  * Detect 'boundary' for 'multipart/form-data' POST encoding.
    289  * @param c the stream to use
    290  * @return 'true' if succeed,
    291  *         'false' if failed and error result is set
    292  */
    293 static MHD_FN_PAR_NONNULL_ALL_ bool
    294 detect_mpart_boundary_from_the_header (struct MHD_Connection *restrict c)
    295 {
    296   struct MHD_StringNullable hdr_cnt_tp;
    297   enum mhd_MPartDetectResult res;
    298 
    299   mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == \
    300               c->rq.app_act.head_act.data.post_parse.enc);
    301 
    302   if (! mhd_request_get_value_st (&(c->rq),
    303                                   MHD_VK_HEADER,
    304                                   MHD_HTTP_HEADER_CONTENT_TYPE,
    305                                   &hdr_cnt_tp))
    306   {
    307     mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \
    308                  "The request POST data cannot be parsed because " \
    309                  "'multipart/form-data' requires 'boundary' parameter, but " \
    310                  "the request has no 'Content-Type:' header.");
    311     c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE;
    312     return false;
    313   }
    314 
    315   mhd_assert (NULL != hdr_cnt_tp.cstr);
    316 
    317   res = process_mpart_header (c,
    318                               (const struct MHD_String *) (const void *)
    319                               &hdr_cnt_tp);
    320 
    321   if (mhd_MPART_DET_OK == res)
    322     return true;
    323 
    324   if (mhd_MPART_DET_NO_MPART == res)
    325   {
    326     mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NOT_MPART, \
    327                  "The request POST data cannot be parsed because " \
    328                  "'multipart/form-data' requires 'boundary' parameter, but " \
    329                  "the request has no 'Content-Type: multipart/form-data' " \
    330                  "header.");
    331     c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART;
    332     return false;
    333   }
    334 
    335   mhd_assert (mhd_MPART_DET_ERROR_SET == res);
    336   return false;
    337 }
    338 
    339 
    340 /**
    341  * Reset field parsing data for "application/x-www-form-urlencoded"
    342  * @param pdata the parsing data
    343  */
    344 static MHD_FN_PAR_NONNULL_ (1) void
    345 reset_parse_field_data_urlenc (struct mhd_PostParserData *pdata)
    346 {
    347   mhd_assert (MHD_HTTP_POST_ENCODING_FORM_URLENCODED == pdata->enc);
    348   memset (&(pdata->e_d.u_enc), 0, sizeof(pdata->e_d.u_enc));
    349   pdata->field_start = 0;
    350 }
    351 
    352 
    353 /**
    354  * Initial reset field parsing data for "multipart/form-data"
    355  * @param pdata the parsing data
    356  */
    357 static MHD_FN_PAR_NONNULL_ (1) void
    358 reset_parse_field_data_mpart_init (struct mhd_PostParserData *pdata)
    359 {
    360   mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == pdata->enc);
    361   memset (&(pdata->e_d.m_form.f), 0, sizeof(pdata->e_d.m_form.f));
    362   pdata->e_d.m_form.st = mhd_POST_MPART_ST_NOT_STARTED;
    363   pdata->e_d.m_form.line_start = mhd_POST_INVALID_POS;
    364   pdata->e_d.m_form.delim_check_start = mhd_POST_INVALID_POS;
    365   mhd_assert (NULL != pdata->e_d.m_form.delim.data);
    366   mhd_assert (4 < pdata->e_d.m_form.delim.size);
    367   mhd_assert (0 == memcmp (pdata->e_d.m_form.delim.data, "\r\n--", 4));
    368   mhd_assert (NULL == memchr (pdata->e_d.m_form.delim.data + 4, '\r', \
    369                               pdata->e_d.m_form.delim.size - 4));
    370   mhd_assert (NULL == memchr (pdata->e_d.m_form.delim.data + 4, '\n', \
    371                               pdata->e_d.m_form.delim.size - 4));
    372   pdata->field_start = 0;
    373 }
    374 
    375 
    376 /**
    377  * Reset field parsing data for "multipart/form-data" after processing
    378  * previous field
    379  * @param pdata the parsing data
    380  * @param final 'true' if last field was "closed" by the "final" delimiter
    381  */
    382 static MHD_FN_PAR_NONNULL_ (1) void
    383 reset_parse_field_data_mpart_cont (struct mhd_PostParserData *pdata,
    384                                    bool final)
    385 {
    386   mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == pdata->enc);
    387   memset (&(pdata->e_d.m_form.f), 0, sizeof(pdata->e_d.m_form.f));
    388   pdata->e_d.m_form.st = final ?
    389                          mhd_POST_MPART_ST_EPILOGUE :
    390                          mhd_POST_MPART_ST_PART_START;
    391   pdata->field_start = 0;
    392 }
    393 
    394 
    395 /**
    396  * Reset field parsing data for "text/plain"
    397  * @param pdata the parsing data
    398  */
    399 static MHD_FN_PAR_NONNULL_ (1) void
    400 reset_parse_field_data_text (struct mhd_PostParserData *pdata)
    401 {
    402   mhd_assert (MHD_HTTP_POST_ENCODING_TEXT_PLAIN == pdata->enc);
    403   memset (&(pdata->e_d.text), 0, sizeof(pdata->e_d.text));
    404   pdata->field_start = 0;
    405 }
    406 
    407 
    408 /**
    409  * Finish initialisation of data for POST parsing
    410  * @param c the stream to use
    411  */
    412 static MHD_FN_PAR_NONNULL_ (1) void
    413 init_post_parse_data (struct MHD_Connection *restrict c)
    414 {
    415   struct mhd_PostParserData *const pdata =
    416     &(c->rq.u_proc.post);
    417 
    418   mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
    419   mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != \
    420               c->rq.u_proc.post.enc);
    421   mhd_assert (0 == pdata->lbuf_used);
    422 
    423   pdata->lbuf_limit = c->rq.app_act.head_act.data.post_parse.buffer_size;
    424 
    425   switch (pdata->enc)
    426   {
    427   case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
    428     reset_parse_field_data_urlenc (pdata);
    429     break;
    430   case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
    431     reset_parse_field_data_mpart_init (pdata);
    432     break;
    433   case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
    434     reset_parse_field_data_text (pdata);
    435     break;
    436   case MHD_HTTP_POST_ENCODING_OTHER:
    437   default:
    438     mhd_UNREACHABLE ();
    439     break;
    440   }
    441 }
    442 
    443 
    444 MHD_INTERNAL
    445 MHD_FN_PAR_NONNULL_ (1) bool
    446 mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c)
    447 {
    448   mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
    449   if (MHD_HTTP_POST_ENCODING_OTHER ==
    450       c->rq.app_act.head_act.data.post_parse.enc)
    451   {
    452     if (! detect_post_enc (c))
    453     {
    454       mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result);
    455       c->discard_request = true;
    456       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
    457       return false;
    458     }
    459   }
    460   else if (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ==
    461            c->rq.app_act.head_act.data.post_parse.enc)
    462   {
    463     if (! detect_mpart_boundary_from_the_header (c))
    464     {
    465       mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result);
    466       c->discard_request = true;
    467       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
    468       return false;
    469     }
    470   }
    471   else
    472     c->rq.u_proc.post.enc = c->rq.app_act.head_act.data.post_parse.enc;
    473 
    474   mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != \
    475               c->rq.u_proc.post.enc);
    476   mhd_assert ((MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
    477                c->rq.u_proc.post.enc) || \
    478               (4 < c->rq.u_proc.post.e_d.m_form.delim.size));
    479 
    480   init_post_parse_data (c);
    481 
    482   return true;
    483 }
    484 
    485 
    486 #if 0 /* Disable unused functions */
    487 
    488 /**
    489  * Allocate memory from "large shared buffer" for POST parsing
    490  * @param c the stream to use
    491  * @param alloc_size the size to allocate
    492  * @param[out] buf the buffer to allocate
    493  * @return 'true' if succeed,
    494  *         'false' otherwise
    495  */
    496 static MHD_FN_PAR_NONNULL_ALL_
    497 MHD_FN_PAR_INOUT_ (3) bool
    498 get_lbuf_fixed_size (struct MHD_Connection *restrict c,
    499                      size_t alloc_size,
    500                      struct mhd_Buffer *restrict buf)
    501 {
    502   mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
    503   mhd_assert (0 == buf->size);
    504   mhd_assert (NULL == buf->data);
    505 
    506   if (alloc_size > c->rq.u_proc.post.lbuf_limit)
    507     return false;
    508 
    509   return mhd_daemon_get_lbuf (c->daemon,
    510                               alloc_size,
    511                               buf);
    512 }
    513 
    514 
    515 /**
    516  * Grow the allocated memory from "large shared buffer" for POST parsing
    517  * @param c the stream to use
    518  * @param grow_size the size to grow
    519  * @param[in,out] buf the buffer to grow
    520  * @return 'true' if succeed,
    521  *         'false' otherwise
    522  */
    523 static MHD_FN_PAR_NONNULL_ALL_
    524 MHD_FN_PAR_INOUT_ (3) bool
    525 grow_lbuf_fixed_size (struct MHD_Connection *restrict c,
    526                       size_t grow_size,
    527                       struct mhd_Buffer *restrict buf)
    528 {
    529   mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
    530   mhd_assert (0 != buf->size);
    531   mhd_assert (NULL != buf->data);
    532   mhd_assert (c->rq.u_proc.post.lbuf_limit >= buf->size);
    533 
    534   if (buf->size + grow_size > c->rq.u_proc.post.lbuf_limit)
    535     return false;
    536 
    537   return mhd_daemon_grow_lbuf (c->daemon,
    538                                grow_size,
    539                                buf);
    540 }
    541 
    542 
    543 #endif /* Disable unused functions */
    544 
    545 /**
    546  * Grow or allocate the new memory from "large shared buffer" for POST parsing
    547  * If the requested grow size is not possible, grow up to max possible size.
    548  * @param c the stream to use
    549  * @param desired_grow_size the desired size to grow
    550  * @param[in,out] buf the buffer to grow
    551  * @return the resulting grow size
    552  */
    553 static MHD_FN_PAR_NONNULL_ALL_
    554 MHD_FN_PAR_INOUT_ (3) size_t
    555 extend_lbuf_up_to (struct MHD_Connection *restrict c,
    556                    size_t desired_grow_size,
    557                    struct mhd_Buffer *restrict buf)
    558 {
    559   mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
    560   mhd_assert (c->rq.u_proc.post.lbuf_limit >= buf->size);
    561 
    562   if (buf->size + desired_grow_size > c->rq.u_proc.post.lbuf_limit)
    563     desired_grow_size = c->rq.u_proc.post.lbuf_limit - buf->size;
    564 
    565   if (0 == desired_grow_size)
    566     return 0;
    567 
    568   return mhd_daemon_extend_lbuf_up_to (c->daemon,
    569                                        desired_grow_size,
    570                                        buf);
    571 }
    572 
    573 
    574 /**
    575  * Report "no memory in larger shared buffer".
    576  * Set parsing status accordingly
    577  * @param c the stream to use
    578  * @return 'true' if the stream state has been changed,
    579  *         'false' otherwise
    580  */
    581 static MHD_FN_PAR_NONNULL_ALL_ bool
    582 report_low_lbuf_mem (struct MHD_Connection *restrict c)
    583 {
    584   mhd_LOG_MSG (c->daemon, \
    585                MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM, \
    586                "Not enough large shared buffer memory to " \
    587                "parse POST request.");
    588   c->rq.u_proc.post.parse_result =
    589     MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM;
    590   if (c->stage < mhd_HTTP_STAGE_FULL_REQ_RECEIVED)
    591   {
    592     c->discard_request = true;
    593     c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
    594 
    595     return true;
    596   }
    597   return false;
    598 }
    599 
    600 
    601 /**
    602  * Report broken POST encoding termination
    603  * @param c the stream to use
    604  */
    605 static MHD_FN_PAR_NONNULL_ALL_ void
    606 report_invalid_termination (struct MHD_Connection *restrict c)
    607 {
    608   mhd_LOG_MSG (c->daemon, \
    609                MHD_SC_REQ_POST_PARSE_OK_BAD_TERMINATION, \
    610                "The POST request content has invalid termination / ending. " \
    611                "The last parsed field may be incorrect.");
    612   c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_OK_BAD_TERMINATION;
    613 }
    614 
    615 
    616 /**
    617  * Test whether current incomplete value must be provided to the "stream"
    618  * reader.
    619  * @param c the connection to use
    620  * @param field_cur_size the current size of the current field
    621  * @return 'true' if the value must be provided via the "stream" reader,
    622  *         'false' otherwise.
    623  */
    624 mhd_static_inline MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ bool
    625 is_value_streaming_needed (struct MHD_Connection *restrict c,
    626                            size_t field_cur_size)
    627 {
    628   struct mhd_PostParseActionData *const p_par =
    629     &(c->rq.app_act.head_act.data.post_parse);
    630   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
    631 
    632   if (NULL == p_par->stream_reader)
    633   {
    634     mhd_assert (0 == p_data->value_off);
    635     mhd_assert (! p_data->force_streamed);
    636     return false; /* No value streaming possible */
    637   }
    638 
    639   /* If part of the value has been already provided to "stream"
    640      reader, the rest of the value should be provided
    641      in the same way */
    642   mhd_assert ((0 == p_data->value_off) || p_data->force_streamed);
    643   if (p_data->force_streamed)
    644     return true;
    645 
    646   return (p_par->max_nonstream_size < field_cur_size);
    647 }
    648 
    649 
    650 /**
    651  * Add parsed POST field to the list of request's fields
    652  * @param c the stream to use
    653  * @param name the name of the field
    654  * @param filename the filename of the field
    655  * @param content_type the "Content-Type:" of the field
    656  * @param transfer_encoding the "Transfer-Encoding:" of the field
    657  * @param value the value of the field
    658  * @return 'true' if succeed,
    659  *         'false' if memory allocation failed (no pool memory in the stream)
    660  */
    661 static MHD_FN_PAR_NONNULL_ALL_ bool
    662 add_parsed_post_field (struct MHD_Connection *restrict c,
    663                        struct mhd_PositionAndLength *restrict name,
    664                        struct mhd_PositionAndLength *restrict filename,
    665                        struct mhd_PositionAndLength *restrict content_type,
    666                        struct mhd_PositionAndLength *restrict transfer_encoding,
    667                        struct mhd_PositionAndLength *restrict value)
    668 {
    669   struct mhd_RequestPostField *pfield;
    670 
    671   mhd_assert ((0 != filename->pos) || (0 == filename->len));
    672   mhd_assert ((0 != content_type->pos) || (0 == content_type->len));
    673   mhd_assert ((0 != transfer_encoding->pos) || \
    674               (0 == transfer_encoding->len));
    675   mhd_assert ((0 != value->pos) || (0 == value->len));
    676 
    677   pfield = (struct mhd_RequestPostField *)
    678            mhd_stream_alloc_memory (c,
    679                                     sizeof (struct mhd_RequestPostField));
    680   if (NULL == pfield)
    681     return false;
    682 
    683   pfield->field.name = *name;
    684   pfield->field.value = *value;
    685   pfield->field.filename = *filename;
    686   pfield->field.content_type = *content_type;
    687   pfield->field.transfer_encoding = *transfer_encoding;
    688 
    689   mhd_DLINKEDL_INIT_LINKS (pfield, post_fields);
    690 
    691   mhd_DLINKEDL_INS_LAST (&(c->rq), pfield, post_fields);
    692 
    693   return true;
    694 }
    695 
    696 
    697 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_
    698 MHD_FN_PAR_IN_ (1)
    699 MHD_FN_PAR_OUT_ (10) MHD_FN_PAR_OUT_ (11)
    700 MHD_FN_PAR_OUT_ (12) MHD_FN_PAR_OUT_ (13) void
    701 make_post_strings_from_buf_and_indices (const char *restrict buf,
    702                                         size_t name_start,
    703                                         size_t name_len,
    704                                         size_t filename_start,
    705                                         size_t filename_len,
    706                                         size_t cntn_type_start,
    707                                         size_t cntn_type_len,
    708                                         size_t enc_start,
    709                                         size_t enc_len,
    710                                         struct MHD_String *name,
    711                                         struct MHD_StringNullable *filename,
    712                                         struct MHD_StringNullable *content_type,
    713                                         struct MHD_StringNullable *encoding)
    714 {
    715   name->len = name_len;
    716   name->cstr = buf + name_start;
    717 
    718   if (0 != filename_start)
    719   {
    720     filename->len = filename_len;
    721     filename->cstr = buf + filename_start;
    722   }
    723   else
    724   {
    725     filename->len = 0;
    726     filename->cstr = NULL;
    727   }
    728   if (0 != cntn_type_start)
    729   {
    730     content_type->len = cntn_type_len;
    731     content_type->cstr = buf + cntn_type_start;
    732   }
    733   else
    734   {
    735     content_type->len = 0;
    736     content_type->cstr = NULL;
    737   }
    738   if (0 != enc_start)
    739   {
    740     encoding->len = enc_len;
    741     encoding->cstr = buf + enc_start;
    742   }
    743   else
    744   {
    745     encoding->len = 0;
    746     encoding->cstr = NULL;
    747   }
    748 }
    749 
    750 
    751 /**
    752  * Process new full parsed POST field
    753  * @param c the stream to use
    754  * @param buf the buffer, where the data is
    755  * @param pfield_next_pos the pointer to variable holding index of
    756  *                        the next field to be parsed
    757  * @param pdata_size the pointer to variable holding the size of the data
    758  * @param field_start the start of the current field in the @a buf
    759  * @param name_start the start of the name of the field in the @a buf
    760  * @param name_len the length of the name, not including mandatory terminating
    761  *                 zero
    762  * @param filename_start the start of the filename of the field in the @a buf,
    763  *                       zero if no filename is provided
    764  * @param filename_len the length of the filename, not including mandatory
    765  *                     terminating zero
    766  * @param cntn_type_start the start of the Content-Type value of the field in
    767  *                        the @a buf, zero if no Content-Type is provided
    768  * @param cntn_type_len the length of the Content-Type value, not including
    769  *                      mandatory terminating zero
    770  * @param enc_start the start of the Content-Encoding value of the field in
    771  *                  the @a buf, zero if no Content-Encoding is provided
    772  * @param enc_len the length of the Content-Encoding value, not including
    773  *                    mandatory terminating zero
    774  * @param value_start the start of the field value in the @a buf, zero if
    775  *                    no value is provided
    776  * @param value_len the length of the field value, not including mandatory
    777  *                  terminating zero
    778  * @return 'true' if stream state has been changed,
    779  *         'false' to continue parsing
    780  */
    781 static MHD_FN_PAR_NONNULL_ALL_ bool
    782 process_complete_field_all (struct MHD_Connection *restrict c,
    783                             char *restrict buf,
    784                             size_t *restrict pfield_next_pos,
    785                             size_t *restrict pdata_size,
    786                             size_t field_start,
    787                             size_t name_start,
    788                             size_t name_len,
    789                             size_t filename_start,
    790                             size_t filename_len,
    791                             size_t cntn_type_start,
    792                             size_t cntn_type_len,
    793                             size_t enc_start,
    794                             size_t enc_len,
    795                             size_t value_start,
    796                             size_t value_len)
    797 {
    798   struct mhd_PostParseActionData *const p_par =
    799     &(c->rq.app_act.head_act.data.post_parse);
    800   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
    801 
    802   mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
    803 
    804   mhd_assert ((0 == filename_start) || \
    805               (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
    806   mhd_assert ((0 == cntn_type_start) || \
    807               (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
    808   mhd_assert ((0 == enc_start) || \
    809               (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
    810 
    811   mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage);
    812   mhd_assert (value_start + value_len <= *pfield_next_pos);
    813   mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \
    814               (value_start + value_len < *pfield_next_pos));
    815   mhd_assert (*pfield_next_pos <= *pdata_size);
    816   mhd_assert ((name_start + name_len < value_start) || \
    817               (0 == value_start));
    818   mhd_assert (value_start + value_len <= *pfield_next_pos);
    819   mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \
    820               (name_start + name_len < *pfield_next_pos));
    821   mhd_assert ((filename_start + filename_len < value_start) || \
    822               (0 == value_start));
    823   mhd_assert (filename_start + filename_len <= *pfield_next_pos);
    824   mhd_assert ((cntn_type_start + cntn_type_len < value_start) || \
    825               (0 == value_start));
    826   mhd_assert (cntn_type_start + cntn_type_len <= *pfield_next_pos);
    827   mhd_assert ((enc_start + enc_len < value_start) || \
    828               (0 == value_start));
    829   mhd_assert (enc_start + enc_len <= *pfield_next_pos);
    830   mhd_assert (field_start <= name_start);
    831   mhd_assert ((field_start <= filename_start) || (0 == filename_start));
    832   mhd_assert ((field_start <= cntn_type_start) || (0 == cntn_type_start));
    833   mhd_assert ((field_start <= enc_start) || (0 == enc_start));
    834   mhd_assert ((field_start <= value_start) || (0 == value_start));
    835   mhd_assert ((0 != filename_start) || (0 == filename_len));
    836   mhd_assert ((0 != cntn_type_start) || (0 == cntn_type_len));
    837   mhd_assert ((0 != enc_start) || (0 == enc_len));
    838   mhd_assert ((0 != value_start) || (0 == value_len));
    839   mhd_assert (0 == buf [name_start + name_len]);
    840 
    841   p_data->some_data_provided = true;
    842 
    843   if (is_value_streaming_needed (c, (*pfield_next_pos - field_start)))
    844   {
    845     bool res;
    846     const struct MHD_UploadAction *act;
    847     struct MHD_String name;
    848     struct MHD_StringNullable filename;
    849     struct MHD_StringNullable content_type;
    850     struct MHD_StringNullable encoding;
    851 
    852     make_post_strings_from_buf_and_indices (buf,
    853                                             name_start,
    854                                             name_len,
    855                                             filename_start,
    856                                             filename_len,
    857                                             cntn_type_start,
    858                                             cntn_type_len,
    859                                             enc_start,
    860                                             enc_len,
    861                                             &name,
    862                                             &filename,
    863                                             &content_type,
    864                                             &encoding);
    865 
    866     act = p_par->stream_reader (&(c->rq),
    867                                 p_par->reader_cls,
    868                                 &name,
    869                                 &filename,
    870                                 &content_type,
    871                                 &encoding,
    872                                 value_len,
    873                                 buf + value_start,
    874                                 p_data->value_off,
    875                                 MHD_YES);
    876     p_data->some_data_provided = true;
    877     res = mhd_stream_process_upload_action (c, act, false);
    878     if (c->suspended)
    879       return true;
    880     p_data->force_streamed = false;
    881     p_data->value_off = 0;
    882     if (*pdata_size > *pfield_next_pos)
    883     {
    884       size_t consumed_size;
    885       memmove (buf + field_start,
    886                buf + *pfield_next_pos,
    887                *pdata_size - *pfield_next_pos);
    888       consumed_size = *pfield_next_pos - field_start;
    889       *pfield_next_pos = field_start;
    890       *pdata_size -= consumed_size;
    891     }
    892     else
    893     {
    894       *pfield_next_pos = field_start;
    895       *pdata_size = field_start;
    896     }
    897     return res;
    898   }
    899   else
    900   {
    901     struct mhd_PositionAndLength name_i;
    902     struct mhd_PositionAndLength filename_i;
    903     struct mhd_PositionAndLength content_type_i;
    904     struct mhd_PositionAndLength encoding_i;
    905     struct mhd_PositionAndLength value_i;
    906 
    907     mhd_assert (! p_data->force_streamed);
    908     mhd_assert (0 == p_data->value_off);
    909     mhd_assert ((0 == value_start) || (0 == buf [value_start + value_len]));
    910 
    911     name_i.pos = name_start;
    912     name_i.len = name_len;
    913     filename_i.pos = filename_start;
    914     filename_i.len = filename_len;
    915     content_type_i.pos = cntn_type_start;
    916     content_type_i.len = cntn_type_len;
    917     encoding_i.pos = enc_start;
    918     encoding_i.len = enc_len;
    919     value_i.pos = value_start;
    920     value_i.len = value_len;
    921 
    922     if (! add_parsed_post_field (c,
    923                                  &name_i,
    924                                  &filename_i,
    925                                  &content_type_i,
    926                                  &encoding_i,
    927                                  &value_i))
    928     {
    929       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
    930       mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM, \
    931                    "The request POST data cannot be parsed completely " \
    932                    "because there is not enough pool memory.");
    933       p_data->parse_result = MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM;
    934       return true;
    935     }
    936 
    937     p_data->some_data_provided = true;
    938   }
    939 
    940   return false; /* Continue parsing */
    941 }
    942 
    943 
    944 /**
    945  * Process new full parsed POST field
    946  * @param c the stream to use
    947  * @param buf the buffer, where the data is
    948  * @param pfield_next_pos the pointer to variable holding index of
    949  *                        the next field to be parsed
    950  * @param pdata_size the pointer to variable holding the size of the data
    951  * @param field_start the start of the current field in the @a buf
    952  * @param name_start the start of the name of the field in the @a buf
    953  * @param name_len the length of the name, not including mandatory terminating
    954  *                 zero
    955  * @param value_start the start of the field value in the @a buf, zero if
    956  *                    no value is provided
    957  * @param value_len the length of the field value, not including mandatory
    958  *                  terminating zero
    959  * @return 'true' if stream state has been changed,
    960  *         'false' to continue parsing
    961  */
    962 static MHD_FN_PAR_NONNULL_ALL_ bool
    963 process_complete_field (struct MHD_Connection *restrict c,
    964                         char *restrict buf,
    965                         size_t *restrict pfield_next_pos,
    966                         size_t *restrict pdata_size,
    967                         size_t field_start,
    968                         size_t name_start,
    969                         size_t name_len,
    970                         size_t value_start,
    971                         size_t value_len)
    972 {
    973   mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage);
    974   mhd_assert (value_start + value_len <= *pfield_next_pos);
    975   mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \
    976               (value_start + value_len < *pfield_next_pos));
    977   mhd_assert ((name_start + name_len < value_start) || \
    978               (0 == value_start));
    979   mhd_assert (name_start + name_len <= *pfield_next_pos);
    980   mhd_assert ((mhd_HTTP_STAGE_FULL_REQ_RECEIVED <= c->stage) || \
    981               (name_start + name_len < *pfield_next_pos));
    982   mhd_assert (field_start <= name_start);
    983   mhd_assert ((field_start <= value_start) || (0 == value_start));
    984 
    985   mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
    986               c->rq.u_proc.post.enc);
    987 
    988   return process_complete_field_all (c,
    989                                      buf,
    990                                      pfield_next_pos,
    991                                      pdata_size,
    992                                      field_start,
    993                                      name_start,
    994                                      name_len,
    995                                      0, 0, 0, 0, 0, 0,
    996                                      value_start,
    997                                      value_len);
    998 }
    999 
   1000 
   1001 /**
   1002  * Process the part of the POST value.
   1003  *
   1004  * The part of the value are be provided for "streaming" processing by
   1005  * the application callback and removed from the buffer (the remaining of
   1006  * the data in the buffer is shifted backward).
   1007  * The function must be called only when streaming is the partial value is
   1008  * needed.
   1009  *
   1010  * @param c the connection to use
   1011  * @param buf the pointer to the buffer
   1012  * @param pnext_pos the position of the next character to be processed
   1013  *                  in the buffer
   1014  * @param pdata_size the size of the data in the buffer
   1015  * @param name_start the position of the "name", must be zero-terminated
   1016  * @param name_len the length of the "name", not including zero-termination
   1017  * @param filename_start the position of the filename, zero if not
   1018  *                       provided / set
   1019  * @param filename_len the length of the filename
   1020  * @param cntn_type_start the position of field "Content-Type" value, zero
   1021  *                        if not provided / set
   1022  * @param cntn_type_len the length of the field "Content-Type" value
   1023  * @param enc_start the position of the field "Content-Encoding" value, zero
   1024  *                        if not provided / set
   1025  * @param enc_len the length of  the field "Content-Encoding" value
   1026  * @param part_value_start the position of partial value data, does not
   1027  *                         need to be zero-terminated
   1028  * @param part_value_len the length of the partial value data
   1029  * @return 'true' if connection/stream state has been changed,
   1030  *         'false' indicates the need to continuation of POST data parsing
   1031  */
   1032 static MHD_FN_PAR_NONNULL_ALL_ bool
   1033 process_partial_value_all (struct MHD_Connection *restrict c,
   1034                            char *restrict buf,
   1035                            size_t *restrict pnext_pos,
   1036                            size_t *restrict pdata_size,
   1037                            size_t name_start,
   1038                            size_t name_len,
   1039                            size_t filename_start,
   1040                            size_t filename_len,
   1041                            size_t cntn_type_start,
   1042                            size_t cntn_type_len,
   1043                            size_t enc_start,
   1044                            size_t enc_len,
   1045                            size_t part_value_start,
   1046                            size_t part_value_len)
   1047 {
   1048   struct mhd_PostParseActionData *const p_par =
   1049     &(c->rq.app_act.head_act.data.post_parse);
   1050   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   1051   struct MHD_String name;
   1052   struct MHD_StringNullable filename;
   1053   struct MHD_StringNullable content_type;
   1054   struct MHD_StringNullable encoding;
   1055   const struct MHD_UploadAction *act;
   1056   bool res;
   1057 
   1058   mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage);
   1059   mhd_assert (*pnext_pos <= *pdata_size);
   1060   mhd_assert (part_value_start + part_value_len <= *pnext_pos);
   1061   mhd_assert (0 != part_value_start);
   1062   mhd_assert (0 != part_value_len);
   1063   mhd_assert (name_start + name_len < *pnext_pos);
   1064   mhd_assert (filename_start + filename_len < part_value_start);
   1065   mhd_assert (filename_start + filename_len < *pnext_pos);
   1066   mhd_assert (cntn_type_start + cntn_type_len < part_value_start);
   1067   mhd_assert (cntn_type_start + cntn_type_len < *pnext_pos);
   1068   mhd_assert (enc_start + enc_len < part_value_start);
   1069   mhd_assert (enc_start + enc_len < *pnext_pos);
   1070   mhd_assert ((0 != filename_start) || (0 == filename_len));
   1071   mhd_assert ((0 != cntn_type_start) || (0 == cntn_type_len));
   1072   mhd_assert ((0 != enc_start) || (0 == enc_len));
   1073   mhd_assert (NULL != p_par->stream_reader);
   1074 
   1075   make_post_strings_from_buf_and_indices (buf,
   1076                                           name_start,
   1077                                           name_len,
   1078                                           filename_start,
   1079                                           filename_len,
   1080                                           cntn_type_start,
   1081                                           cntn_type_len,
   1082                                           enc_start,
   1083                                           enc_len,
   1084                                           &name,
   1085                                           &filename,
   1086                                           &content_type,
   1087                                           &encoding);
   1088 
   1089   act = p_par->stream_reader (&(c->rq),
   1090                               p_par->reader_cls,
   1091                               &name,
   1092                               &filename,
   1093                               &content_type,
   1094                               &encoding,
   1095                               part_value_len,
   1096                               buf + part_value_start,
   1097                               p_data->value_off,
   1098                               MHD_NO);
   1099 
   1100   p_data->some_data_provided = true;
   1101   p_data->force_streamed = true;
   1102 
   1103   res = mhd_stream_process_upload_action (c, act, false);
   1104   if (c->suspended)
   1105     return true;
   1106 
   1107   p_data->value_off += part_value_len;
   1108   if (*pdata_size > *pnext_pos)
   1109   {
   1110     size_t consumed_size;
   1111 
   1112     memmove (buf + part_value_start,
   1113              buf + *pnext_pos,
   1114              *pdata_size - *pnext_pos);
   1115     consumed_size = *pnext_pos - part_value_start;
   1116     *pnext_pos = part_value_start;
   1117     *pdata_size -= consumed_size;
   1118   }
   1119   else
   1120   {
   1121     mhd_assert (*pdata_size == *pnext_pos);
   1122     *pnext_pos = part_value_start;
   1123     *pdata_size = part_value_start;
   1124   }
   1125   return res;
   1126 }
   1127 
   1128 
   1129 /**
   1130  * Process the part of the POST value.
   1131  * The part of the value are be provided for "streaming" processing by
   1132  * the application callback and removed from the buffer (the remaining of
   1133  * the data in the buffer is shifted backward).
   1134  * The function must be called only when streaming is the partial value is
   1135  * needed.
   1136  * @param c the connection to use
   1137  * @param buf the pointer to the buffer
   1138  * @param pnext_pos the position of the next character to be processed
   1139  *                  in the buffer
   1140  * @param pdata_size the size of the data in the buffer
   1141  * @param name_start the position of the "name", must be zero-terminated
   1142  * @param name_len the length of the "name", not including zero-termination
   1143  * @param part_value_start the position of partial value data, does not
   1144  *                         need to be zero-terminated
   1145  * @param part_value_len the length of the partial value data
   1146  * @return 'true' if connection/stream state has been changed,
   1147  *         'false' indicates the need to continuation of POST data parsing
   1148  */
   1149 static MHD_FN_PAR_NONNULL_ALL_ bool
   1150 process_partial_value (struct MHD_Connection *restrict c,
   1151                        char *restrict buf,
   1152                        size_t *restrict pnext_pos,
   1153                        size_t *restrict pdata_size,
   1154                        size_t name_start,
   1155                        size_t name_len,
   1156                        size_t part_value_start,
   1157                        size_t part_value_len)
   1158 {
   1159   mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= c->stage);
   1160   mhd_assert (part_value_start + part_value_len <= *pnext_pos);
   1161   mhd_assert (name_start + name_len < part_value_start);
   1162   mhd_assert (0 != part_value_start);
   1163   mhd_assert (0 != part_value_len);
   1164   mhd_assert (name_start + name_len < *pnext_pos);
   1165 
   1166 
   1167   mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
   1168               c->rq.u_proc.post.enc);
   1169 
   1170   return process_partial_value_all (c,
   1171                                     buf,
   1172                                     pnext_pos,
   1173                                     pdata_size,
   1174                                     name_start,
   1175                                     name_len,
   1176                                     0, 0, 0, 0, 0, 0,
   1177                                     part_value_start,
   1178                                     part_value_len);
   1179 }
   1180 
   1181 
   1182 /**
   1183  * Parse "application/x-www-form-urlencoded" data
   1184  * @param c the stream to use
   1185  * @param pdata_size the pointer to variable holding the size of the data in
   1186  *                   the @a buf
   1187  * @param buf the buffer with the data
   1188  * @return 'true' if stream state changed,
   1189  *         'false' to continue parsing
   1190  */
   1191 static MHD_FN_PAR_NONNULL_ALL_
   1192 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
   1193 parse_post_urlenc (struct MHD_Connection *restrict c,
   1194                    size_t *restrict pdata_size,
   1195                    char *restrict buf)
   1196 {
   1197   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   1198   struct mhd_PostParserUrlEncData *const uf = &(p_data->e_d.u_enc); /**< the current "url-enc" field */
   1199   size_t i;
   1200 
   1201   mhd_assert (MHD_HTTP_POST_ENCODING_FORM_URLENCODED == c->rq.u_proc.post.enc);
   1202   mhd_assert (MHD_POST_PARSE_RES_OK == p_data->parse_result);
   1203   mhd_assert (! c->discard_request);
   1204   mhd_assert (p_data->next_parse_pos < *pdata_size);
   1205 
   1206   if ((mhd_POST_UENC_ST_VALUE == uf->st) &&
   1207       (0 != uf->value_len))
   1208   {
   1209     /* The 'value' was partially decoded, but not processed because application
   1210      * asked for 'suspend' action */
   1211     mhd_assert (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader);
   1212     if (process_partial_value (c,
   1213                                buf,
   1214                                &p_data->next_parse_pos,
   1215                                pdata_size,
   1216                                uf->name_idx,
   1217                                uf->name_len,
   1218                                uf->value_idx,
   1219                                uf->value_len))
   1220       return true;
   1221     uf->value_len = 0;
   1222   }
   1223 
   1224   i = p_data->next_parse_pos;
   1225   while (*pdata_size > i)
   1226   {
   1227     switch (uf->st)
   1228     {
   1229     case mhd_POST_UENC_ST_NOT_STARTED:
   1230       mhd_assert (0 == p_data->field_start);
   1231       mhd_assert (0 == p_data->value_off);
   1232       p_data->field_start = i;
   1233       uf->name_idx = i;
   1234       uf->last_pct_idx = mhd_POST_INVALID_POS;
   1235       uf->st = mhd_POST_UENC_ST_NAME;
   1236       mhd_FALLTHROUGH;
   1237     /* Intentional fallthrough */
   1238     case mhd_POST_UENC_ST_NAME:
   1239       do /* Fast local loop */
   1240       {
   1241         if ('+' == buf[i])
   1242           buf[i] = ' ';
   1243         else if ('%' == buf[i])
   1244           uf->last_pct_idx = i;
   1245         else if ('=' == buf[i])
   1246         {
   1247           uf->st = mhd_POST_UENC_ST_AT_EQ;
   1248           break;
   1249         }
   1250         else if ('&' == buf[i])
   1251         {
   1252           uf->st = mhd_POST_UENC_ST_AT_AMPRSND;
   1253           break;
   1254         }
   1255       } while (*pdata_size > ++i);
   1256       mhd_assert ((*pdata_size == i) || \
   1257                   (mhd_POST_UENC_ST_AT_EQ == uf->st) || \
   1258                   (mhd_POST_UENC_ST_AT_AMPRSND == uf->st) );
   1259       continue;
   1260     case mhd_POST_UENC_ST_AT_EQ:
   1261       mhd_assert (i > uf->name_idx);
   1262       mhd_assert (0 == uf->name_len);
   1263       mhd_assert (uf->last_pct_idx >= p_data->field_start);
   1264       mhd_assert (uf->last_pct_idx >= uf->name_idx);
   1265       mhd_assert ((uf->last_pct_idx == mhd_POST_INVALID_POS) || \
   1266                   (uf->last_pct_idx < i));
   1267       mhd_assert (0 == uf->value_len);
   1268       if (uf->last_pct_idx != mhd_POST_INVALID_POS)
   1269         uf->name_len = mhd_str_pct_decode_lenient_n (buf + uf->name_idx,
   1270                                                      i - uf->name_idx,
   1271                                                      buf + uf->name_idx,
   1272                                                      i - uf->name_idx,
   1273                                                      NULL);
   1274       else
   1275         uf->name_len = i - uf->name_idx;
   1276       buf[uf->name_idx + uf->name_len] = 0; /* Zero-terminate the name */
   1277 
   1278       uf->st = mhd_POST_UENC_ST_EQ_FOUND;
   1279       ++i; /* Process the next char */
   1280       continue; /* Check whether the next char is available */
   1281     case mhd_POST_UENC_ST_EQ_FOUND:
   1282       mhd_assert (0 == p_data->value_off);
   1283       mhd_assert (0 == uf->value_idx);
   1284       mhd_assert (0 == uf->value_len);
   1285       mhd_assert (0 != i && "the 'value' should follow the 'name'");
   1286       uf->last_pct_idx = mhd_POST_INVALID_POS;
   1287       uf->value_idx = i;
   1288       uf->st = mhd_POST_UENC_ST_VALUE;
   1289       mhd_FALLTHROUGH;
   1290     /* Intentional fallthrough */
   1291     case mhd_POST_UENC_ST_VALUE:
   1292       do /* Fast local loop */
   1293       {
   1294         if ('+' == buf[i])
   1295           buf[i] = ' ';
   1296         else if ('%' == buf[i])
   1297           uf->last_pct_idx = i;
   1298         else if ('&' == buf[i])
   1299         {
   1300           uf->st = mhd_POST_UENC_ST_AT_AMPRSND;
   1301           break;
   1302         }
   1303       } while (*pdata_size > ++i);
   1304       mhd_assert ((*pdata_size == i) || \
   1305                   (mhd_POST_UENC_ST_AT_AMPRSND == uf->st));
   1306       continue;
   1307     case mhd_POST_UENC_ST_AT_AMPRSND:
   1308       mhd_assert (0 == uf->value_len);
   1309       mhd_assert ((uf->last_pct_idx == mhd_POST_INVALID_POS) || \
   1310                   (uf->last_pct_idx < i));
   1311       mhd_assert ((uf->last_pct_idx == mhd_POST_INVALID_POS) || \
   1312                   ((uf->name_idx + uf->name_len) < i));
   1313       if (0 != uf->value_idx)
   1314       {
   1315         /* Have 'name' and 'value' */
   1316         if (uf->last_pct_idx != mhd_POST_INVALID_POS)
   1317           uf->value_len = mhd_str_pct_decode_lenient_n (buf + uf->value_idx,
   1318                                                         i - uf->value_idx,
   1319                                                         buf + uf->value_idx,
   1320                                                         i - uf->value_idx,
   1321                                                         NULL);
   1322         else
   1323           uf->value_len = i - uf->value_idx;
   1324         buf[uf->value_idx + uf->value_len] = 0; /* Zero-terminate the value */
   1325       }
   1326       else
   1327       {
   1328         /* Have 'name' only (without any 'value') */
   1329         if (uf->last_pct_idx != mhd_POST_INVALID_POS)
   1330           uf->name_len = mhd_str_pct_decode_lenient_n (buf + uf->name_idx,
   1331                                                        i - uf->name_idx,
   1332                                                        buf + uf->name_idx,
   1333                                                        i - uf->name_idx,
   1334                                                        NULL);
   1335         else
   1336           uf->name_len = i - uf->name_idx;
   1337         buf[uf->name_idx + uf->name_len] = 0; /* Zero-terminate the name */
   1338       }
   1339       uf->st = mhd_POST_UENC_ST_FULL_FIELD_FOUND;
   1340       mhd_FALLTHROUGH;
   1341     /* Intentional fallthrough */
   1342     case mhd_POST_UENC_ST_FULL_FIELD_FOUND:
   1343       ++i; /* Consume current character,
   1344               advance to the next char to be checked */
   1345       if (process_complete_field (c,
   1346                                   buf,
   1347                                   &i,
   1348                                   pdata_size,
   1349                                   p_data->field_start,
   1350                                   uf->name_idx,
   1351                                   uf->name_len,
   1352                                   uf->value_idx,
   1353                                   uf->value_len))
   1354       {
   1355         if (c->suspended)
   1356           --i; /* Go back to the same position */
   1357         else
   1358           reset_parse_field_data_urlenc (p_data);
   1359         p_data->next_parse_pos = i;
   1360         return true;
   1361       }
   1362       mhd_assert (*pdata_size >= i);
   1363       reset_parse_field_data_urlenc (p_data);
   1364       continue; /* Process the next char */
   1365     default:
   1366       mhd_UNREACHABLE ();
   1367       break;
   1368     }
   1369     mhd_assert (0 && "Should be unreachable");
   1370     mhd_UNREACHABLE ();
   1371     break;
   1372   }
   1373 
   1374   mhd_assert (*pdata_size == i);
   1375 
   1376   mhd_assert (mhd_POST_UENC_ST_AT_EQ != uf->st);
   1377   mhd_assert (mhd_POST_UENC_ST_AT_AMPRSND != uf->st);
   1378   mhd_assert (mhd_POST_UENC_ST_FULL_FIELD_FOUND != uf->st);
   1379   mhd_assert ((mhd_POST_UENC_ST_VALUE != uf->st) || \
   1380               (0 == uf->value_len));
   1381 
   1382   mhd_assert (*pdata_size == i);
   1383 
   1384   if ((mhd_POST_UENC_ST_VALUE == uf->st) &&
   1385       (i != uf->value_idx) && /* Encoded value position must be larger then zero */
   1386       is_value_streaming_needed (c, i - p_data->field_start))
   1387   {
   1388     size_t len_of_value_part;
   1389     if (uf->last_pct_idx != mhd_POST_INVALID_POS)
   1390     {
   1391       mhd_assert (uf->last_pct_idx < i);
   1392       mhd_assert (uf->last_pct_idx >= uf->value_idx);
   1393 
   1394       if (2 >= (i - uf->last_pct_idx))
   1395         i = uf->last_pct_idx;  /* The last percent-encoded character is incomplete */
   1396 
   1397       len_of_value_part =
   1398         mhd_str_pct_decode_lenient_n (buf + uf->value_idx,
   1399                                       i - uf->value_idx,
   1400                                       buf + uf->value_idx,
   1401                                       i - uf->value_idx,
   1402                                       NULL);
   1403     }
   1404     else
   1405       len_of_value_part = i - uf->value_idx;
   1406 
   1407     if (0 != len_of_value_part)
   1408     {
   1409       bool proc_res;
   1410 
   1411       proc_res =
   1412         process_partial_value (c,
   1413                                buf,
   1414                                &i,
   1415                                pdata_size,
   1416                                uf->name_idx,
   1417                                uf->name_len,
   1418                                uf->value_idx,
   1419                                len_of_value_part);
   1420 
   1421       /* Reset position of last '%' char: it was already decoded or
   1422        * 'i' points to it and it will be processed again next time */
   1423       uf->last_pct_idx = mhd_POST_INVALID_POS;
   1424 
   1425       if (proc_res)
   1426       {
   1427         if (c->suspended)
   1428           uf->value_len = len_of_value_part; /* Indicate that value has been
   1429                                                 partially decoded and needs
   1430                                                 to be "streamed" again */
   1431         p_data->next_parse_pos = i;
   1432         return true;
   1433       }
   1434     }
   1435   }
   1436 
   1437   p_data->next_parse_pos = i;
   1438   return false; /* Continue parsing */
   1439 }
   1440 
   1441 
   1442 /**
   1443  * Parse "multipart/form-data" data
   1444  * @param c the stream to use
   1445  * @param pdata_size the pointer to variable holding the size of the data in
   1446  *                   the @a buf
   1447  * @param buf the buffer with the data
   1448  * @return 'true' if stream state changed,
   1449  *         'false' to continue parsing
   1450  */
   1451 static MHD_FN_PAR_NONNULL_ALL_
   1452 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
   1453 parse_post_mpart (struct MHD_Connection *restrict c,
   1454                   size_t *restrict pdata_size,
   1455                   char *restrict buf)
   1456 {
   1457   const int discp_lvl = c->daemon->req_cfg.strictness;
   1458   const bool bare_lf_as_crlf = (-2 >= discp_lvl); /* Bare LF termination is dangerous when used in "multipart/form-data" */
   1459   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   1460   struct mhd_PostParserMPartFormData *const mf = &(p_data->e_d.m_form); /**< the current "form-data" parsing details */
   1461   size_t i;
   1462 
   1463   mhd_assert (NULL != mf->delim.data);
   1464   mhd_assert (4 < mf->delim.size);
   1465   mhd_assert (0 == memcmp (mf->delim.data, "\r\n--", 4));
   1466   mhd_assert (NULL == memchr (mf->delim.data + 4, '\r', mf->delim.size - 4));
   1467   mhd_assert (NULL == memchr (mf->delim.data + 4, '\n', mf->delim.size - 4));
   1468   mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == \
   1469               c->rq.u_proc.post.enc);
   1470   mhd_assert (MHD_POST_PARSE_RES_OK == p_data->parse_result);
   1471   mhd_assert (mhd_POST_MPART_ST_FORMAT_ERROR != mf->st);
   1472   mhd_assert (! c->discard_request);
   1473   mhd_assert (p_data->next_parse_pos < *pdata_size);
   1474 
   1475   i = p_data->next_parse_pos;
   1476   while (*pdata_size > i)
   1477   {
   1478     switch (mf->st)
   1479     {
   1480     case mhd_POST_MPART_ST_BACK_TO_PREAMBL:
   1481       mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   1482       mf->line_start = mhd_POST_INVALID_POS;
   1483       mf->st = mhd_POST_MPART_ST_PREAMBL;
   1484       mhd_FALLTHROUGH;
   1485     /* Intentional fallthrough */
   1486     case mhd_POST_MPART_ST_PREAMBL:
   1487       mhd_assert (0 == p_data->value_off);
   1488       mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
   1489       mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
   1490 #ifdef HAVE_MEMMEM
   1491       if (mf->delim.size <= *pdata_size - i)
   1492       {
   1493         const char *delim_ptr;
   1494         if (! bare_lf_as_crlf)
   1495           delim_ptr = (const char *) memmem (buf + i,
   1496                                              *pdata_size - i,
   1497                                              mf->delim.data,
   1498                                              mf->delim.size);
   1499         else
   1500           delim_ptr = (const char *) memmem (buf + i,
   1501                                              *pdata_size - i,
   1502                                              mf->delim.data + 1,
   1503                                              mf->delim.size - 1);
   1504         if (NULL != delim_ptr)
   1505         {
   1506           size_t delim_pos;
   1507 
   1508           mhd_assert (delim_ptr >= buf + i);
   1509           mhd_assert (delim_ptr + mf->delim.size - 1 <= buf + *pdata_size);
   1510 
   1511           delim_pos = (size_t) (delim_ptr - buf);
   1512 
   1513           mhd_assert (i <= delim_pos);
   1514 
   1515           if (! bare_lf_as_crlf)
   1516           {
   1517             mf->line_start = delim_pos + 2u; /* '2' for CRLF */
   1518             i = delim_pos + mf->delim.size;
   1519           }
   1520           else
   1521           {
   1522             mf->line_start = delim_pos + 1u; /* '1' for LF */
   1523             i = delim_pos + mf->delim.size - 1;
   1524           }
   1525           mf->st = mhd_POST_MPART_ST_FIRST_DELIM_FOUND;
   1526           continue;
   1527         }
   1528         i = *pdata_size - mf->delim.size + 1u; /* '+ 1u' to move to then next position */
   1529         if (! bare_lf_as_crlf)
   1530           i += 1u;
   1531       }
   1532 #endif /* HAVE_MEMMEM */
   1533       do /* Fast local loop */
   1534       {
   1535 #ifndef MHD_FAVOR_SMALL_CODE
   1536         const char *lf_ptr;
   1537         size_t lf_pos;
   1538 
   1539         lf_ptr = (const char *) memchr (buf + i, '\n', *pdata_size - i);
   1540         if (NULL == lf_ptr)
   1541         {
   1542           if ('\r' == buf[*pdata_size - 1])
   1543             mf->st = mhd_POST_MPART_ST_PREAMBL_CR_FOUND;
   1544           i = *pdata_size;
   1545           break;
   1546         }
   1547         lf_pos = (size_t) (lf_ptr - buf);
   1548         mhd_assert (i <= lf_pos);
   1549         mhd_assert (*pdata_size > i);
   1550         if (bare_lf_as_crlf)
   1551         {
   1552           i = lf_pos + 1;
   1553           mf->st = mhd_POST_MPART_ST_PREAMBL_LINE_START;
   1554           break;
   1555         }
   1556         else if ((i < lf_pos) &&
   1557                  ('\r' == buf[lf_pos - 1]))
   1558         {
   1559           i = lf_pos + 1;
   1560           mf->st = mhd_POST_MPART_ST_PREAMBL_LINE_START;
   1561           break;
   1562         }
   1563         i = lf_pos;
   1564 #else  /* MHD_FAVOR_SMALL_CODE */
   1565         if ('\r' == buf[i])
   1566         {
   1567           mf->st = mhd_POST_MPART_ST_PREAMBL_CR_FOUND;
   1568           ++i; /* Go to the next char */
   1569           break;
   1570         }
   1571         else if ('\n' == buf[i] && bare_lf_as_crlf)
   1572         {
   1573           mf->st = mhd_POST_MPART_ST_PREAMBL_LINE_START;
   1574           ++i; /* Go to the next char */
   1575           break;
   1576         }
   1577 #endif /* MHD_FAVOR_SMALL_CODE */
   1578       } while (*pdata_size > ++i);
   1579       mhd_assert ((*pdata_size == i) || \
   1580                   (mhd_POST_MPART_ST_PREAMBL_CR_FOUND == mf->st) || \
   1581                   (mhd_POST_MPART_ST_PREAMBL_LINE_START == mf->st) );
   1582       continue;
   1583     case mhd_POST_MPART_ST_PREAMBL_CR_FOUND:
   1584       mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   1585       mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
   1586       if ('\n' == buf[i])
   1587       {
   1588         mf->st = mhd_POST_MPART_ST_PREAMBL_LINE_START;
   1589         ++i;
   1590       }
   1591       else
   1592         mf->st = mhd_POST_MPART_ST_PREAMBL;
   1593       continue;
   1594     case mhd_POST_MPART_ST_NOT_STARTED:
   1595       mhd_assert (0 == p_data->field_start);
   1596       mhd_assert (0 == p_data->value_off);
   1597       mf->delim_check_start = mhd_POST_INVALID_POS; /* Ignored for first delimiter */
   1598       p_data->field_start = i;
   1599       mhd_FALLTHROUGH;
   1600     /* Intentional fallthrough */
   1601     case mhd_POST_MPART_ST_PREAMBL_LINE_START:
   1602       mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
   1603       mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
   1604       mf->line_start = i;
   1605 #ifndef MHD_FAVOR_SMALL_CODE
   1606       if (*pdata_size - i >= mf->delim.size - 2) /* Exclude CRLF prefix for the first delimiter */
   1607       { /* Exclude CRLF prefix for the first delimiter */
   1608         if (0 == memcmp (buf + i, mf->delim.data + 2, mf->delim.size - 2))
   1609         {
   1610           mf->st = mhd_POST_MPART_ST_FIRST_DELIM_FOUND;
   1611           i += mf->delim.size - 2;
   1612         }
   1613         else
   1614           mf->st = mhd_POST_MPART_ST_BACK_TO_PREAMBL;
   1615         continue;
   1616       }
   1617 #endif /* ! MHD_FAVOR_SMALL_CODE */
   1618       mf->st = mhd_POST_MPART_ST_PREAMBL_CHECKING_FOR_DELIM;
   1619       mhd_FALLTHROUGH;
   1620     /* Intentional fallthrough */
   1621     case mhd_POST_MPART_ST_PREAMBL_CHECKING_FOR_DELIM:
   1622       mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start); /* Ignored for first delimiter */
   1623       mhd_assert (i >= mf->line_start);
   1624       mhd_assert (*pdata_size >= mf->line_start);
   1625       mhd_assert (i < mf->line_start + (mf->delim.size - 2));
   1626       if (*pdata_size - mf->line_start >= (mf->delim.size - 2))
   1627       {
   1628         /* Enough data for the delimiter */
   1629         if (0 == memcmp (buf + mf->line_start,
   1630                          mf->delim.data + 2,
   1631                          mf->delim.size - 2))
   1632         {
   1633           mf->st = mhd_POST_MPART_ST_FIRST_DELIM_FOUND;
   1634           i = mf->line_start + (mf->delim.size - 2);
   1635         }
   1636         else
   1637           mf->st = mhd_POST_MPART_ST_BACK_TO_PREAMBL;
   1638       }
   1639       else
   1640       {
   1641         /* Not enough data for the delimiter */
   1642         if (0 == memcmp (buf + mf->line_start,
   1643                          mf->delim.data + 2,
   1644                          *pdata_size - mf->line_start))
   1645           i = *pdata_size;
   1646         else
   1647           mf->st = mhd_POST_MPART_ST_BACK_TO_PREAMBL;
   1648       }
   1649       mhd_assert ((*pdata_size == i) || \
   1650                   (mhd_POST_MPART_ST_FIRST_DELIM_FOUND == mf->st) || \
   1651                   (mhd_POST_MPART_ST_BACK_TO_PREAMBL == mf->st));
   1652       continue;
   1653     case mhd_POST_MPART_ST_FIRST_DELIM_FOUND:
   1654       mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start); /* Ignored for first delimiter */
   1655       mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
   1656       mhd_assert (i >= mf->line_start + mf->delim.size - 2);
   1657       do /* Fast local loop */
   1658       {
   1659         if ('\n' == buf[i])
   1660         {
   1661           if (bare_lf_as_crlf ||
   1662               ('\r' == buf [i - 1]))
   1663           {
   1664             mf->st = mhd_POST_MPART_ST_FIRST_PART_START;
   1665             ++i;
   1666           }
   1667           else
   1668             mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1669 
   1670           break;
   1671         }
   1672         else if ('\r' == buf [i - 1])
   1673         {
   1674           mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1675           break;
   1676         }
   1677         else if ((i == mf->line_start + (mf->delim.size - 2) + 1) &&
   1678                  ('-' == buf [i - 1]) &&
   1679                  ('-' == buf [i]))
   1680         {
   1681           mf->st = mhd_POST_MPART_ST_EPILOGUE;
   1682           break;
   1683         }
   1684       } while (*pdata_size > ++i);
   1685       mhd_assert ((*pdata_size == i) || \
   1686                   (mhd_POST_MPART_ST_FIRST_PART_START == mf->st) || \
   1687                   (mhd_POST_MPART_ST_FORMAT_ERROR == mf->st) || \
   1688                   (mhd_POST_MPART_ST_EPILOGUE == mf->st));
   1689       continue;
   1690     case mhd_POST_MPART_ST_FIRST_PART_START:
   1691       mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start); /* Ignored for first delimiter */
   1692       mhd_assert (i > p_data->field_start);
   1693       mhd_assert (*pdata_size > i);
   1694       if ((c->rq.app_act.head_act.data.post_parse.max_nonstream_size <
   1695            i - p_data->field_start) ||
   1696           (*pdata_size - i < i - p_data->field_start))
   1697       {
   1698         /* Discard unused data */
   1699         memmove (buf + p_data->field_start,
   1700                  buf + i,
   1701                  *pdata_size - i);
   1702         *pdata_size -= (i - p_data->field_start);
   1703         i = p_data->field_start;
   1704       }
   1705       mf->delim_check_start = p_data->field_start;
   1706       mhd_FALLTHROUGH;
   1707     /* Intentional fallthrough */
   1708     case mhd_POST_MPART_ST_PART_START:
   1709       mhd_assert (0 == mf->f.name_len);
   1710       mhd_assert (0 == p_data->value_off);
   1711       mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   1712       p_data->field_start = mf->delim_check_start;
   1713       mf->delim_check_start = mhd_POST_INVALID_POS;
   1714       mhd_FALLTHROUGH;
   1715     /* Intentional fallthrough */
   1716     case mhd_POST_MPART_ST_HEADER_LINE_START:
   1717       mf->line_start = i;
   1718       mf->st = mhd_POST_MPART_ST_HEADER_LINE;
   1719       mhd_FALLTHROUGH;
   1720     /* Intentional fallthrough */
   1721     case mhd_POST_MPART_ST_HEADER_LINE:
   1722       mhd_assert (i >= mf->line_start);
   1723       mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
   1724       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   1725       do /* Fast local loop */
   1726       {
   1727         if ('\r' == buf[i])
   1728         {
   1729           mf->st = mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND;
   1730           ++i;
   1731           break;
   1732         }
   1733         else if ('\n' == buf[i])
   1734         {
   1735           if (bare_lf_as_crlf)
   1736             mf->st = mhd_POST_MPART_ST_HEADER_LINE_END;
   1737           else
   1738             mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1739           break;
   1740         }
   1741         else if (mf->line_start + (mf->delim.size - 2) == i + 1)
   1742         {
   1743           if (0 == memcmp (buf + mf->line_start,
   1744                            mf->delim.data + 2,
   1745                            mf->delim.size - 2))
   1746           {
   1747             /* The delimiter before the end of the header */
   1748             if (2 > mf->line_start)
   1749               mf->delim_check_start = mf->line_start;
   1750             else if (! bare_lf_as_crlf)
   1751               mf->delim_check_start = mf->line_start - 2;
   1752             else
   1753               mf->delim_check_start = mf->line_start - 1; /* Actually can be one char earlier */
   1754             mf->st = mhd_POST_MPART_ST_DELIM_FOUND;
   1755             break;
   1756           }
   1757         }
   1758       } while (*pdata_size > ++i);
   1759       mhd_assert ((*pdata_size == i) || \
   1760                   (mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND == mf->st) || \
   1761                   (mhd_POST_MPART_ST_HEADER_LINE_END == mf->st) || \
   1762                   (mhd_POST_MPART_ST_DELIM_FOUND == mf->st) || \
   1763                   (mhd_POST_MPART_ST_FORMAT_ERROR == mf->st) );
   1764       continue;
   1765     case mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND:
   1766       if ('\n' != buf[i])
   1767       {
   1768         mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1769         continue;
   1770       }
   1771       mf->st = mhd_POST_MPART_ST_HEADER_LINE_END;
   1772       mhd_FALLTHROUGH;
   1773     /* Intentional fallthrough */
   1774     case mhd_POST_MPART_ST_HEADER_LINE_END:
   1775       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   1776       mhd_assert (i >= mf->line_start);
   1777       mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
   1778       if (1)
   1779       {
   1780         size_t line_len;
   1781         if (i == mf->line_start)
   1782           line_len = 0;
   1783         else if ('\r' == buf[i - 1])
   1784           line_len = i - mf->line_start - 1;
   1785         else
   1786           line_len = i - mf->line_start;
   1787 
   1788         if (0 == line_len)
   1789         {
   1790           ++i;
   1791           mf->st = mhd_POST_MPART_ST_VALUE_START;
   1792           continue;
   1793         }
   1794         else
   1795         {
   1796           static const struct MHD_String hdr =
   1797             mhd_MSTR_INIT ("Content-Disposition:");
   1798           static const struct MHD_String tkn = mhd_MSTR_INIT ("form-data");
   1799           static const struct MHD_String n_par = mhd_MSTR_INIT ("name");
   1800 
   1801           if ((hdr.len + tkn.len + n_par.len + 2 <= line_len) &&
   1802               mhd_str_equal_caseless_bin_n (hdr.cstr,
   1803                                             buf + mf->line_start,
   1804                                             hdr.len))
   1805           {
   1806             size_t hdr_val_start;
   1807             struct MHD_String hdr_val;
   1808             enum mhd_StingStartsWithTokenResult res;
   1809             struct mhd_BufferConst name_buf;
   1810             bool hdr_has_name;
   1811             bool name_needs_unq;
   1812 
   1813             buf [mf->line_start + line_len] = 0; /* Zero-terminate the header line */
   1814             hdr_val_start = mf->line_start + hdr.len;
   1815             /* Skip all whitespace chars */
   1816             while ((' ' == buf[hdr_val_start]) || ('\t' == buf[hdr_val_start]))
   1817               ++hdr_val_start;
   1818 
   1819             mhd_assert (hdr_val_start <= i);
   1820 
   1821             hdr_val.cstr = buf + hdr_val_start;
   1822             hdr_val.len = mf->line_start + line_len - hdr_val_start;
   1823 
   1824             res = mhd_str_starts_with_token_req_param (&hdr_val,
   1825                                                        &tkn,
   1826                                                        &n_par,
   1827                                                        &name_buf,
   1828                                                        &name_needs_unq);
   1829             if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT == res)
   1830             {
   1831               /* Found two names for one field */
   1832               mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1833               continue;
   1834             }
   1835             else if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN == res)
   1836             {
   1837               static const struct MHD_String fn_par =
   1838                 mhd_MSTR_INIT ("filename");
   1839               struct mhd_BufferConst fname_buf;
   1840               bool fname_needs_unq;
   1841 
   1842               if (NULL != name_buf.data)
   1843               {
   1844                 mhd_assert (buf < name_buf.data);
   1845                 if (0 != mf->f.name_idx)
   1846                 {
   1847                   /* Found two names for one field */
   1848                   mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1849                   continue;
   1850                 }
   1851                 mf->f.name_idx = (size_t) (name_buf.data - buf);
   1852                 mf->f.name_len = name_buf.size;
   1853                 hdr_has_name = true;
   1854 
   1855                 /* Do not process (unquote, url-decode, zero-terminate) here yet
   1856                  * as it may break the header format */
   1857               }
   1858               else
   1859                 hdr_has_name = false;
   1860 
   1861               res = mhd_str_starts_with_token_req_param (&hdr_val,
   1862                                                          &tkn,
   1863                                                          &fn_par,
   1864                                                          &fname_buf,
   1865                                                          &fname_needs_unq);
   1866               if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT == res)
   1867               {
   1868                 mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1869                 continue;
   1870               }
   1871               else if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN == res)
   1872               {
   1873                 if (NULL != fname_buf.data)
   1874                 {
   1875                   mhd_assert (buf < fname_buf.data);
   1876                   if (0 != mf->f.filename_idx)
   1877                   {
   1878                     /* Found two filenames for one field */
   1879                     mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1880                     continue;
   1881                   }
   1882                   mf->f.filename_idx = (size_t) (fname_buf.data - buf);
   1883                   if (! fname_needs_unq)
   1884                     mf->f.filename_len = fname_buf.size;
   1885                   else
   1886                   {
   1887                     mf->f.filename_len =
   1888                       mhd_str_unquote (fname_buf.data,
   1889                                        fname_buf.size,
   1890                                        buf + mf->f.filename_idx);
   1891                     if ((0 == mf->f.filename_len) && (0 != fname_buf.size))
   1892                     {
   1893                       mhd_assert (0 && "broken quoting must be detected " \
   1894                                   "earlier by " \
   1895                                   "mhd_str_starts_with_token_req_param()");
   1896                       mhd_UNREACHABLE ();
   1897                       mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1898                       continue;
   1899                     }
   1900                   }
   1901                   mhd_assert (mf->f.filename_idx + mf->f.filename_len <= i);
   1902                   mf->f.filename_len =
   1903                     mhd_str_pct_decode_lenient_n (buf + mf->f.filename_idx,
   1904                                                   mf->f.filename_len,
   1905                                                   buf + mf->f.filename_idx,
   1906                                                   mf->f.filename_len,
   1907                                                   NULL);
   1908                   mhd_assert (mf->f.filename_idx + mf->f.filename_len <= i);
   1909 
   1910                   buf[mf->f.filename_idx + mf->f.filename_len] = 0; /* Zero-terminate the filename */
   1911                 }
   1912               }
   1913               else
   1914               {
   1915                 mhd_assert (mhd_STR_STARTS_W_TOKEN_NO_TOKEN == res);
   1916                 mhd_assert (0 && "The presence of the token was "
   1917                             "checked earlier");
   1918                 mhd_UNREACHABLE ();
   1919               }
   1920 
   1921               if (hdr_has_name)
   1922               {
   1923                 if (name_needs_unq)
   1924                 {
   1925                   mf->f.name_len = mhd_str_unquote (buf + mf->f.name_idx,
   1926                                                     mf->f.name_len,
   1927                                                     buf + mf->f.name_idx);
   1928                   if ((0 == mf->f.name_len) && (0 != name_buf.size))
   1929                   {
   1930                     mhd_assert (0 && "broken quoting must be detected " \
   1931                                 "earlier by " \
   1932                                 "mhd_str_starts_with_token_req_param()");
   1933                     mhd_UNREACHABLE ();
   1934                     mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   1935                     continue;
   1936                   }
   1937                 }
   1938                 mhd_assert (mf->f.name_idx + mf->f.name_len <= i);
   1939                 mf->f.name_len =
   1940                   mhd_str_pct_decode_lenient_n (buf + mf->f.name_idx,
   1941                                                 mf->f.name_len,
   1942                                                 buf + mf->f.name_idx,
   1943                                                 mf->f.name_len,
   1944                                                 NULL);
   1945                 mhd_assert (mf->f.name_idx + mf->f.name_len <= i);
   1946 
   1947                 buf[mf->f.name_idx + mf->f.name_len] = 0; /* Zero-terminate the name */
   1948               }
   1949             }
   1950           }
   1951         }
   1952       }
   1953       ++i;
   1954       mf->st = mhd_POST_MPART_ST_HEADER_LINE_START;
   1955       continue;
   1956     case mhd_POST_MPART_ST_VALUE_START:
   1957       mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
   1958       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   1959       mhd_assert (0 == p_data->value_off);
   1960       mhd_assert (0 == mf->f.value_idx);
   1961       mhd_assert (0 == mf->f.value_len);
   1962       mhd_assert (0 != i && "the 'value' should follow the 'name'");
   1963       if (0 == mf->f.name_idx)
   1964       {
   1965         mhd_LOG_MSG (c->daemon, \
   1966                      p_data->some_data_provided ? \
   1967                      MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT : \
   1968                      MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT, \
   1969                      "The request 'multipart/form-data' POST field has no " \
   1970                      "name of the field.");
   1971         p_data->parse_result =
   1972           p_data->some_data_provided ?
   1973           MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT :
   1974           MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
   1975         c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   1976         return true; /* Stop parsing the upload */
   1977       }
   1978       mhd_assert (0 != mf->f.name_len);
   1979       mhd_assert (i > mf->f.name_idx);
   1980       mf->f.value_idx = i;
   1981       mhd_FALLTHROUGH;
   1982     /* Intentional fallthrough */
   1983     case mhd_POST_MPART_ST_BACK_TO_VALUE:
   1984       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   1985       mf->line_start = mhd_POST_INVALID_POS;
   1986       mf->delim_check_start = mhd_POST_INVALID_POS;
   1987       mf->st = mhd_POST_MPART_ST_VALUE;
   1988       mhd_FALLTHROUGH;
   1989     /* Intentional fallthrough */
   1990     case mhd_POST_MPART_ST_VALUE:
   1991       mhd_assert (mhd_POST_INVALID_POS == mf->delim_check_start);
   1992       mhd_assert (mhd_POST_INVALID_POS == mf->line_start);
   1993       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   1994 #ifdef HAVE_MEMMEM
   1995       if (mf->delim.size <= *pdata_size - i)
   1996       {
   1997         const char *delim_ptr;
   1998         if (! bare_lf_as_crlf)
   1999           delim_ptr = (const char *) memmem (buf + i,
   2000                                              *pdata_size - i,
   2001                                              mf->delim.data,
   2002                                              mf->delim.size);
   2003         else
   2004           delim_ptr = (const char *) memmem (buf + i,
   2005                                              *pdata_size - i,
   2006                                              mf->delim.data + 1,
   2007                                              mf->delim.size - 1);
   2008         if (NULL != delim_ptr)
   2009         {
   2010           size_t delim_pos;
   2011 
   2012           mhd_assert (delim_ptr >= buf + i);
   2013           mhd_assert (delim_ptr + mf->delim.size - 1 <= buf + *pdata_size);
   2014 
   2015           delim_pos = (size_t) (delim_ptr - buf);
   2016 
   2017           mhd_assert (i <= delim_pos);
   2018 
   2019           if (! bare_lf_as_crlf)
   2020           {
   2021             mf->line_start = delim_pos + 2;
   2022             i = delim_pos + mf->delim.size;
   2023           }
   2024           else
   2025           {
   2026             mf->line_start = delim_pos + 1;
   2027             i = delim_pos + mf->delim.size - 1;
   2028             if ((delim_pos > i) &&
   2029                 ('\r' == buf[delim_pos - 1]))
   2030               --delim_pos;
   2031           }
   2032           mf->delim_check_start = delim_pos;
   2033           mf->st = mhd_POST_MPART_ST_DELIM_FOUND;
   2034           continue;
   2035         }
   2036         i = *pdata_size - mf->delim.size + 1u; /* '+ 1u' to move to then next position */
   2037         if (! bare_lf_as_crlf)
   2038           i += 1u;
   2039       }
   2040 #endif /* HAVE_MEMMEM */
   2041       do /* Fast local loop */
   2042       {
   2043 #ifndef MHD_FAVOR_SMALL_CODE
   2044         const char *lf_ptr;
   2045         size_t lf_pos;
   2046 
   2047         lf_ptr = (const char *) memchr (buf + i, '\n', *pdata_size - i);
   2048         if (NULL == lf_ptr)
   2049         {
   2050           if ('\r' == buf[*pdata_size - 1])
   2051             mf->st = mhd_POST_MPART_ST_VALUE_CR_FOUND;
   2052           i = *pdata_size;
   2053           break;
   2054         }
   2055         lf_pos = (size_t) (lf_ptr - buf);
   2056         mhd_assert (i <= lf_pos);
   2057         mhd_assert (*pdata_size > i);
   2058         if ((i < lf_pos) &&
   2059             ('\r' == buf[lf_pos - 1]))
   2060         {
   2061           mf->delim_check_start = lf_pos - 1;
   2062           mf->st = mhd_POST_MPART_ST_VALUE_LINE_START;
   2063           i = lf_pos + 1;
   2064           break;
   2065         }
   2066         else if (bare_lf_as_crlf)
   2067         {
   2068           mf->delim_check_start = lf_pos;
   2069           mf->st = mhd_POST_MPART_ST_VALUE_LINE_START;
   2070           i = lf_pos + 1;
   2071           break;
   2072         }
   2073         i = lf_pos;
   2074 #else  /* MHD_FAVOR_SMALL_CODE */
   2075         if ('\r' == buf[i])
   2076         {
   2077           mf->delim_check_start = i;
   2078           mf->st = mhd_POST_MPART_ST_VALUE_CR_FOUND;
   2079           ++i;
   2080           break;
   2081         }
   2082         else if (bare_lf_as_crlf && '\n' == buf[i])
   2083         {
   2084           mf->delim_check_start = i;
   2085           mf->st = mhd_POST_MPART_ST_VALUE_LINE_START;
   2086           ++i;
   2087           break;
   2088         }
   2089 #endif /* MHD_FAVOR_SMALL_CODE */
   2090       } while (*pdata_size > ++i);
   2091       mhd_assert ((*pdata_size == i) || \
   2092                   (mhd_POST_MPART_ST_VALUE_CR_FOUND == mf->st) || \
   2093                   (mhd_POST_MPART_ST_VALUE_LINE_START == mf->st));
   2094       continue;
   2095     case mhd_POST_MPART_ST_VALUE_CR_FOUND:
   2096       if ('\n' != buf[i])
   2097       {
   2098         mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
   2099         continue;
   2100       }
   2101       mf->st = mhd_POST_MPART_ST_VALUE_LINE_START;
   2102       ++i;
   2103       continue;
   2104     case mhd_POST_MPART_ST_VALUE_LINE_START:
   2105       mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   2106       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   2107       mf->line_start = i;
   2108 #ifndef MHD_FAVOR_SMALL_CODE
   2109       if (*pdata_size - i >= mf->delim.size - 2)
   2110       {
   2111         if (0 == memcmp (buf + i, mf->delim.data + 2, mf->delim.size - 2))
   2112         {
   2113           mf->st = mhd_POST_MPART_ST_DELIM_FOUND;
   2114           i += mf->delim.size - 2;
   2115         }
   2116         else
   2117           mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
   2118         continue;
   2119       }
   2120 #endif /* ! MHD_FAVOR_SMALL_CODE */
   2121       mf->st = mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM;
   2122       mhd_FALLTHROUGH;
   2123     /* Intentional fallthrough */
   2124     case mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM:
   2125       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   2126       mhd_assert (i >= mf->line_start);
   2127       mhd_assert (*pdata_size >= mf->line_start);
   2128       mhd_assert (i < mf->line_start + (mf->delim.size - 2));
   2129       if (*pdata_size - mf->line_start >= (mf->delim.size - 2))
   2130       {
   2131         /* Enough data for the delimiter */
   2132         if (0 == memcmp (buf + mf->line_start,
   2133                          mf->delim.data + 2,
   2134                          mf->delim.size - 2))
   2135         {
   2136           mf->st = mhd_POST_MPART_ST_DELIM_FOUND;
   2137           i = mf->line_start + (mf->delim.size - 2);
   2138         }
   2139         else
   2140           mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
   2141       }
   2142       else
   2143       {
   2144         /* Not enough data for the delimiter */
   2145         if (0 == memcmp (buf + mf->line_start,
   2146                          mf->delim.data + 2,
   2147                          *pdata_size - mf->line_start))
   2148           i = *pdata_size;
   2149         else
   2150           mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
   2151       }
   2152       mhd_assert ((*pdata_size == i) || \
   2153                   (mhd_POST_MPART_ST_DELIM_FOUND == mf->st) || \
   2154                   (mhd_POST_MPART_ST_BACK_TO_VALUE == mf->st));
   2155       continue;
   2156     case mhd_POST_MPART_ST_DELIM_FOUND:
   2157       mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   2158       mhd_assert (mhd_POST_INVALID_POS != mf->line_start);
   2159       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   2160       mhd_assert (i >= mf->line_start + mf->delim.size - 2);
   2161       do /* Fast local loop */
   2162       {
   2163         if ('\n' == buf[i])
   2164         {
   2165           if (bare_lf_as_crlf ||
   2166               ('\r' == buf [i - 1]))
   2167             mf->st = mhd_POST_MPART_ST_VALUE_END_FOUND;
   2168           else
   2169             mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   2170 
   2171           break;
   2172         }
   2173         else if ('\r' == buf [i - 1])
   2174         {
   2175           mf->st = mhd_POST_MPART_ST_FORMAT_ERROR;
   2176           break;
   2177         }
   2178         else if ((i == mf->line_start + (mf->delim.size - 2) + 1) &&
   2179                  ('-' == buf [i - 1]) &&
   2180                  ('-' == buf [i]))
   2181         {
   2182           mf->st = mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL;
   2183           break;
   2184         }
   2185       } while (*pdata_size > ++i);
   2186       mhd_assert ((*pdata_size == i) || \
   2187                   (mhd_POST_MPART_ST_VALUE_END_FOUND == mf->st) || \
   2188                   (mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL == mf->st) || \
   2189                   (mhd_POST_MPART_ST_FORMAT_ERROR == mf->st));
   2190       continue;
   2191     case mhd_POST_MPART_ST_VALUE_END_FOUND:
   2192     case mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL:
   2193       mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   2194       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   2195       mhd_assert (mf->f.value_idx <= mf->delim_check_start);
   2196       mhd_assert (0 == mf->f.value_len);
   2197       mhd_assert (0 != mf->f.name_len);
   2198       mhd_assert (i > mf->f.name_idx);
   2199       mhd_assert (i > mf->delim_check_start);
   2200       if (0 != mf->f.value_idx)
   2201       {
   2202         mf->f.value_len = mf->delim_check_start - mf->f.value_idx;
   2203         buf[mf->f.value_idx + mf->f.value_len] = 0; /* Zero-terminate the value */
   2204         ++mf->delim_check_start; /* Shift start of the delimiter to add space for zero-termination */
   2205       }
   2206       if (mhd_POST_MPART_ST_VALUE_END_FOUND == mf->st)
   2207         mf->st = mhd_POST_MPART_ST_FULL_FIELD_FOUND;
   2208       else
   2209         mf->st = mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL;
   2210       mhd_FALLTHROUGH;
   2211     /* Intentional fallthrough */
   2212     case mhd_POST_MPART_ST_FULL_FIELD_FOUND:
   2213     case mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL:
   2214       mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   2215       mhd_assert (mhd_POST_INVALID_POS != p_data->field_start);
   2216       if (1)
   2217       {
   2218         size_t new_delim_check_start;
   2219         bool state_changed;
   2220 
   2221         ++i; /* Consume current character */
   2222         new_delim_check_start = mf->delim_check_start;
   2223         state_changed =
   2224           process_complete_field_all (c,
   2225                                       buf,
   2226                                       &new_delim_check_start,
   2227                                       pdata_size,
   2228                                       p_data->field_start,
   2229                                       mf->f.name_idx,
   2230                                       mf->f.name_len,
   2231                                       mf->f.filename_idx,
   2232                                       mf->f.filename_len,
   2233                                       mf->f.cntn_type_idx,
   2234                                       mf->f.cntn_type_len,
   2235                                       mf->f.enc_idx,
   2236                                       mf->f.enc_len,
   2237                                       mf->f.value_idx,
   2238                                       mf->f.value_len);
   2239         if (c->suspended)
   2240         {
   2241           mhd_assert (mf->delim_check_start == new_delim_check_start);
   2242           mhd_assert (state_changed);
   2243           p_data->next_parse_pos = --i; /* Restore position */
   2244           return true;
   2245         }
   2246 
   2247         if (mf->delim_check_start != new_delim_check_start)
   2248         {
   2249           size_t shift_size;
   2250           mhd_assert (mf->delim_check_start > new_delim_check_start);
   2251 
   2252           shift_size = mf->delim_check_start - new_delim_check_start;
   2253           mf->delim_check_start = new_delim_check_start;
   2254           i -= shift_size;
   2255         }
   2256 
   2257         mhd_assert (*pdata_size >= i);
   2258 
   2259         reset_parse_field_data_mpart_cont (
   2260           p_data,
   2261           mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL == mf->st);
   2262 
   2263         if (state_changed)
   2264         {
   2265           p_data->next_parse_pos = i;
   2266           return true;
   2267         }
   2268       }
   2269       continue; /* Process the next char */
   2270     case mhd_POST_MPART_ST_EPILOGUE:
   2271       /* Discard the rest of the content data */
   2272       *pdata_size = i;
   2273       p_data->next_parse_pos = i;
   2274       return false;
   2275     case mhd_POST_MPART_ST_FORMAT_ERROR:
   2276       if (p_data->some_data_provided)
   2277       {
   2278         mhd_LOG_MSG (c->daemon, \
   2279                      MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT, \
   2280                      "The request POST has broken encoding or format and " \
   2281                      "was parsed only partially.");
   2282         p_data->parse_result =
   2283           MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
   2284       }
   2285       else
   2286       {
   2287         mhd_LOG_MSG (c->daemon, \
   2288                      MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT, \
   2289                      "The request POST has broken encoding or format and " \
   2290                      "cannot be parsed.");
   2291         p_data->parse_result =
   2292           MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
   2293       }
   2294       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   2295       return true;
   2296     default:
   2297       mhd_UNREACHABLE ();
   2298       break;
   2299     }
   2300     mhd_assert (0 && "Should be unreachable");
   2301     mhd_UNREACHABLE ();
   2302     break;
   2303   }
   2304 
   2305   mhd_assert (*pdata_size == i);
   2306 
   2307   mhd_assert (mhd_POST_MPART_ST_NOT_STARTED != mf->st);
   2308   mhd_assert (mhd_POST_MPART_ST_BACK_TO_PREAMBL != mf->st);
   2309   mhd_assert (mhd_POST_MPART_ST_PREAMBL_LINE_START != mf->st);
   2310   mhd_assert (mhd_POST_MPART_ST_HEADER_LINE_END != mf->st);
   2311   mhd_assert (mhd_POST_MPART_ST_BACK_TO_VALUE != mf->st);
   2312   mhd_assert (mhd_POST_MPART_ST_VALUE_END_FOUND != mf->st);
   2313   mhd_assert (mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL != mf->st);
   2314   mhd_assert ((mhd_POST_MPART_ST_VALUE != mf->st) || \
   2315               (0 == mf->f.value_len));
   2316 
   2317   mhd_assert (*pdata_size == i);
   2318 
   2319   if ((0 != mf->f.value_idx) &&
   2320       (((mhd_POST_MPART_ST_VALUE == mf->st) &&
   2321         (i != mf->f.value_idx) &&
   2322         is_value_streaming_needed (c, i - p_data->field_start)) ||
   2323        (((mhd_POST_MPART_ST_VALUE_CR_FOUND == mf->st) ||
   2324          (mhd_POST_MPART_ST_VALUE_LINE_START == mf->st) ||
   2325          (mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM == mf->st)) &&
   2326         (i != mf->delim_check_start) &&
   2327         is_value_streaming_needed (c, i - mf->delim_check_start))))
   2328   {
   2329     bool proc_res;
   2330 
   2331     mhd_assert ((mhd_POST_MPART_ST_VALUE == mf->st) || \
   2332                 (i >= mf->delim_check_start));
   2333     mhd_assert ((mhd_POST_MPART_ST_VALUE == mf->st) || \
   2334                 (mhd_POST_INVALID_POS != mf->delim_check_start));
   2335     if (mhd_POST_MPART_ST_VALUE != mf->st)
   2336     {
   2337       i = mf->delim_check_start; /* Reset position */
   2338       mf->st = mhd_POST_MPART_ST_BACK_TO_VALUE;
   2339     }
   2340 
   2341     proc_res =
   2342       process_partial_value_all (c,
   2343                                  buf,
   2344                                  &i,
   2345                                  pdata_size,
   2346                                  mf->f.name_idx,
   2347                                  mf->f.name_len,
   2348                                  mf->f.filename_idx,
   2349                                  mf->f.filename_len,
   2350                                  mf->f.cntn_type_idx,
   2351                                  mf->f.cntn_type_len,
   2352                                  mf->f.enc_idx,
   2353                                  mf->f.enc_len,
   2354                                  mf->f.value_idx,
   2355                                  i - mf->f.value_idx);
   2356 
   2357     p_data->next_parse_pos = i;
   2358 
   2359     return proc_res;
   2360   }
   2361 
   2362   p_data->next_parse_pos = i;
   2363   return false; /* Continue parsing */
   2364 }
   2365 
   2366 
   2367 static MHD_FN_PAR_NONNULL_ALL_
   2368 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
   2369 parse_post_text (struct MHD_Connection *restrict c,
   2370                  size_t *restrict pdata_size,
   2371                  char *restrict buf)
   2372 {
   2373   const int discp_lvl = c->daemon->req_cfg.strictness;
   2374   /* Treat bare LF as the end of the line.
   2375      The same logic used here as for parsing HTTP headers.
   2376      Bare LF is processed as the end of the line or rejected as broken
   2377      request. */
   2378   const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
   2379   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   2380   struct mhd_PostParserTextData *const tf = &(p_data->e_d.text); /**< the current "text" field */
   2381   size_t i;
   2382   bool enc_broken;
   2383 
   2384   mhd_assert (MHD_HTTP_POST_ENCODING_TEXT_PLAIN == c->rq.u_proc.post.enc);
   2385   mhd_assert (MHD_POST_PARSE_RES_OK == p_data->parse_result);
   2386   mhd_assert (! c->discard_request);
   2387   mhd_assert (p_data->next_parse_pos < *pdata_size);
   2388 
   2389   enc_broken = false;
   2390   i = p_data->next_parse_pos;
   2391   while (*pdata_size > i)
   2392   {
   2393     switch (tf->st)
   2394     {
   2395     case mhd_POST_TEXT_ST_NOT_STARTED:
   2396       mhd_assert (0 == p_data->field_start);
   2397       mhd_assert (0 == p_data->value_off);
   2398       p_data->field_start = i;
   2399       tf->name_idx = i;
   2400       tf->st = mhd_POST_TEXT_ST_NAME;
   2401       mhd_FALLTHROUGH;
   2402     /* Intentional fallthrough */
   2403     case mhd_POST_TEXT_ST_NAME:
   2404       do /* Fast local loop */
   2405       {
   2406         if ('=' == buf[i])
   2407         {
   2408           tf->st = mhd_POST_TEXT_ST_AT_EQ;
   2409           break;
   2410         }
   2411         else if ('\r' == buf[i])
   2412         {
   2413           tf->st = mhd_POST_TEXT_ST_AT_CR;
   2414           break;
   2415         }
   2416         else if ('\n' == buf[i])
   2417         {
   2418           tf->st = mhd_POST_TEXT_ST_AT_LF_BARE;
   2419           break;
   2420         }
   2421       } while (*pdata_size > ++i);
   2422       mhd_assert ((*pdata_size == i) || \
   2423                   (mhd_POST_TEXT_ST_NAME != tf->st));
   2424       continue;
   2425     case mhd_POST_TEXT_ST_AT_EQ:
   2426       mhd_assert (i > tf->name_idx);
   2427       mhd_assert (0 == tf->name_len);
   2428       mhd_assert (0 == tf->value_len);
   2429       buf[i] = 0; /* Zero-terminate the name */
   2430       tf->name_len = i - tf->name_idx;
   2431       tf->st = mhd_POST_TEXT_ST_EQ_FOUND;
   2432       ++i; /* Process the next char */
   2433       continue;
   2434     case mhd_POST_TEXT_ST_EQ_FOUND:
   2435       mhd_assert (0 == p_data->value_off);
   2436       mhd_assert (0 == tf->value_idx);
   2437       mhd_assert (0 == tf->value_len);
   2438       mhd_assert (0 != i && "the 'value' should follow the 'name'");
   2439       tf->value_idx = i;
   2440       tf->st = mhd_POST_TEXT_ST_VALUE;
   2441       mhd_FALLTHROUGH;
   2442     /* Intentional fallthrough */
   2443     case mhd_POST_TEXT_ST_VALUE:
   2444       do /* Fast local loop */
   2445       {
   2446         if ('\r' == buf[i])
   2447         {
   2448           tf->st = mhd_POST_TEXT_ST_AT_CR;
   2449           break;
   2450         }
   2451         else if ('\n' == buf[i])
   2452         {
   2453           tf->st = mhd_POST_TEXT_ST_AT_LF_BARE;
   2454           break;
   2455         }
   2456       } while (*pdata_size > ++i);
   2457       mhd_assert ((*pdata_size == i) || \
   2458                   (mhd_POST_TEXT_ST_AT_CR == tf->st) || \
   2459                   (mhd_POST_TEXT_ST_AT_LF_BARE == tf->st));
   2460       continue;
   2461     case mhd_POST_TEXT_ST_AT_LF_BARE:
   2462       if (! bare_lf_as_crlf)
   2463       {
   2464         enc_broken = true;
   2465         break;
   2466       }
   2467       mhd_FALLTHROUGH;
   2468     /* Intentional fallthrough */
   2469     case mhd_POST_TEXT_ST_AT_CR:
   2470       mhd_assert (0 == tf->value_len);
   2471       buf[i] = 0; /* Zero-terminate the value (or the name) */
   2472       if (0 != tf->value_idx)
   2473         tf->value_len = i - tf->value_idx;
   2474       else
   2475         tf->name_len = i - tf->name_idx;
   2476       if ((0 == tf->name_len) && (0 == tf->value_len))
   2477       { /* Empty line */
   2478         ++i; /* Advance to the next char to be checked */
   2479         reset_parse_field_data_text (p_data);
   2480         tf->st = mhd_POST_TEXT_ST_NOT_STARTED;
   2481       }
   2482       else if (mhd_POST_TEXT_ST_AT_LF_BARE == tf->st)
   2483         tf->st = mhd_POST_TEXT_ST_FULL_LINE_FOUND;
   2484       else
   2485       {
   2486         tf->st = mhd_POST_TEXT_ST_CR_FOUND;
   2487         ++i; /* Process the next char */
   2488       }
   2489       continue;
   2490     case mhd_POST_TEXT_ST_CR_FOUND:
   2491       if ('\n' != buf[i])
   2492       {
   2493         enc_broken = true;
   2494         break;
   2495       }
   2496       tf->st = mhd_POST_TEXT_ST_FULL_LINE_FOUND;
   2497       mhd_FALLTHROUGH;
   2498     /* Intentional fallthrough */
   2499     case mhd_POST_TEXT_ST_FULL_LINE_FOUND:
   2500       ++i; /* Advance to the next char to be checked */
   2501       if (process_complete_field (c,
   2502                                   buf,
   2503                                   &i,
   2504                                   pdata_size,
   2505                                   p_data->field_start,
   2506                                   tf->name_idx,
   2507                                   tf->name_len,
   2508                                   tf->value_idx,
   2509                                   tf->value_len))
   2510       {
   2511         if (c->suspended)
   2512           --i; /* Go back to the same position */
   2513         else
   2514           reset_parse_field_data_text (p_data);
   2515         p_data->next_parse_pos = i;
   2516         return true;
   2517       }
   2518       mhd_assert (*pdata_size >= i);
   2519       reset_parse_field_data_text (p_data);
   2520       continue; /* Process the next char */
   2521     default:
   2522       mhd_UNREACHABLE ();
   2523       enc_broken = true;
   2524       break;
   2525     }
   2526     mhd_assert (enc_broken);
   2527     break;
   2528   }
   2529 
   2530   mhd_assert ((*pdata_size == i) || enc_broken);
   2531 
   2532   if (enc_broken)
   2533   {
   2534     if (p_data->some_data_provided)
   2535     {
   2536       mhd_LOG_MSG (c->daemon, \
   2537                    MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT, \
   2538                    "The request POST has broken encoding or format and " \
   2539                    "was parsed only partially.");
   2540       p_data->parse_result =
   2541         MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
   2542     }
   2543     else
   2544     {
   2545       mhd_LOG_MSG (c->daemon, \
   2546                    MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT, \
   2547                    "The request POST has broken encoding or format and " \
   2548                    "cannot be parsed.");
   2549       p_data->parse_result =
   2550         MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
   2551     }
   2552     tf->st = mhd_POST_TEXT_ST_NOT_STARTED;
   2553     c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   2554     return true;
   2555   }
   2556 
   2557   mhd_assert (mhd_POST_TEXT_ST_AT_EQ != tf->st);
   2558   mhd_assert (mhd_POST_TEXT_ST_AT_CR != tf->st);
   2559   mhd_assert (mhd_POST_TEXT_ST_AT_LF_BARE != tf->st);
   2560   mhd_assert (mhd_POST_TEXT_ST_FULL_LINE_FOUND != tf->st);
   2561 
   2562   mhd_assert (*pdata_size == i);
   2563 
   2564   if ((mhd_POST_TEXT_ST_VALUE == tf->st) &&
   2565       (i != tf->value_idx) &&
   2566       is_value_streaming_needed (c, i - p_data->field_start))
   2567   {
   2568     if (process_partial_value (c,
   2569                                buf,
   2570                                &i,
   2571                                pdata_size,
   2572                                tf->name_idx,
   2573                                tf->name_len,
   2574                                tf->value_idx,
   2575                                i - tf->value_idx))
   2576     {
   2577       p_data->next_parse_pos = i;
   2578       return true;
   2579     }
   2580   }
   2581 
   2582   p_data->next_parse_pos = i;
   2583   return false; /* Continue parsing */
   2584 }
   2585 
   2586 
   2587 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   2588 MHD_FN_PAR_INOUT_ (3) bool
   2589 mhd_stream_post_parse (struct MHD_Connection *restrict c,
   2590                        size_t *restrict pdata_size,
   2591                        char *restrict buf)
   2592 {
   2593   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   2594   size_t lbuf_left;
   2595 
   2596   mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != p_data->enc);
   2597   mhd_assert (c->rq.cntn.lbuf.size <= p_data->lbuf_limit);
   2598 
   2599   if ((MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc) &&
   2600       (mhd_POST_MPART_ST_EPILOGUE == p_data->e_d.m_form.st))
   2601   {
   2602     /* No need to process the data */
   2603     *pdata_size = 0; /* All data has been "processed" */
   2604     return false; /* Continue normal processing */
   2605   }
   2606 
   2607   // TODO: support process in the connection buffer
   2608 
   2609   mhd_assert (c->rq.cntn.lbuf.size >= p_data->lbuf_used);
   2610   lbuf_left = c->rq.cntn.lbuf.size - p_data->lbuf_used;
   2611 
   2612   if (*pdata_size + 1 > lbuf_left)
   2613     (void) extend_lbuf_up_to (c,
   2614                               *pdata_size + 1 - lbuf_left,
   2615                               &(c->rq.cntn.lbuf));
   2616 
   2617   while ((0 != *pdata_size) &&
   2618          (c->rq.cntn.lbuf.size > p_data->lbuf_used))
   2619   {
   2620     size_t data_size_before_parse;
   2621     size_t copy_size = *pdata_size;
   2622     lbuf_left = c->rq.cntn.lbuf.size - p_data->lbuf_used;
   2623     if (lbuf_left < copy_size)
   2624       copy_size = lbuf_left;
   2625 
   2626     memcpy (c->rq.cntn.lbuf.data + p_data->lbuf_used,
   2627             buf,
   2628             copy_size);
   2629     p_data->lbuf_used += copy_size;
   2630     *pdata_size -= copy_size;
   2631 
   2632     data_size_before_parse = p_data->lbuf_used;
   2633     switch (p_data->enc)
   2634     {
   2635     case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
   2636       if (parse_post_urlenc (c,
   2637                              &(p_data->lbuf_used),
   2638                              c->rq.cntn.lbuf.data))
   2639         return true;
   2640       break;
   2641     case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
   2642       if (parse_post_mpart (c,
   2643                             &(p_data->lbuf_used),
   2644                             c->rq.cntn.lbuf.data))
   2645         return true;
   2646       break;
   2647     case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
   2648       if (parse_post_text (c,
   2649                            &(p_data->lbuf_used),
   2650                            c->rq.cntn.lbuf.data))
   2651         return true;
   2652       break;
   2653     case MHD_HTTP_POST_ENCODING_OTHER:
   2654     default:
   2655       mhd_UNREACHABLE ();
   2656       p_data->parse_result =
   2657         MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
   2658       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   2659       return true;
   2660     }
   2661     if (data_size_before_parse == p_data->lbuf_used)
   2662       break; /* Nothing consumed, not possible to add new data to the buffer now */
   2663   }
   2664 
   2665   if (0 != *pdata_size)
   2666     return report_low_lbuf_mem (c);
   2667 
   2668   return false; /* Continue normal processing */
   2669 }
   2670 
   2671 
   2672 /**
   2673  * Check whether some unprocessed or partially processed data left in buffers
   2674  * for urlencoding POST encoding.
   2675  * @param c the stream to use
   2676  * @param pdata_size the pointer to the size of the data in the buffer
   2677  * @param buf the buffer with the data
   2678  * @return 'true' if stream state was changed,
   2679  *         'false' to continue normal processing
   2680  */
   2681 static MHD_FN_PAR_NONNULL_ALL_
   2682 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
   2683 check_post_leftovers_urlenc (struct MHD_Connection *restrict c,
   2684                              size_t *restrict pdata_size,
   2685                              char *restrict buf)
   2686 {
   2687   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   2688   struct mhd_PostParserUrlEncData *const uf = &(p_data->e_d.u_enc); /**< the current "text" field */
   2689   size_t pos;
   2690   size_t name_start;
   2691   size_t name_len;
   2692   size_t value_start;
   2693   size_t value_len;
   2694   bool have_extra_space;
   2695   bool need_value_stream;
   2696 
   2697   pos = p_data->next_parse_pos;  /* Points to the char AFTER the data */
   2698   mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
   2699   mhd_assert (*pdata_size >= pos);
   2700   have_extra_space = (pos < c->rq.cntn.lbuf.size);
   2701   need_value_stream = false; /**< Value cannot be zero-terminated and must be streamed */
   2702   switch (uf->st)
   2703   {
   2704   case mhd_POST_UENC_ST_NOT_STARTED:
   2705     mhd_assert (pos == *pdata_size);
   2706     return false; /* Continue processing */
   2707   case mhd_POST_UENC_ST_NAME:
   2708     mhd_assert (pos == *pdata_size);
   2709     /* Unfinished name */
   2710     name_start = uf->name_idx;
   2711     if (uf->last_pct_idx != mhd_POST_INVALID_POS)
   2712       name_len = mhd_str_pct_decode_lenient_n (buf + uf->name_idx,
   2713                                                pos - uf->name_idx,
   2714                                                buf + uf->name_idx,
   2715                                                pos - uf->name_idx,
   2716                                                NULL);
   2717     else
   2718       name_len = pos - uf->name_idx;
   2719     mhd_assert (name_start + name_len <= pos);
   2720     if (! have_extra_space &&
   2721         (name_start + name_len == pos))
   2722       return report_low_lbuf_mem (c);
   2723     buf[name_start + name_len] = 0; /* Zero-terminate the result, an extra byte is available */
   2724     value_start = 0;
   2725     value_len = 0;
   2726     break;
   2727   case mhd_POST_UENC_ST_EQ_FOUND:
   2728     mhd_assert (pos == *pdata_size);
   2729     name_start = uf->name_idx;
   2730     name_len = uf->name_len;
   2731     value_start = pos;
   2732     value_len = 0;
   2733     if (! have_extra_space)
   2734       need_value_stream = true;
   2735     else
   2736       buf[value_start] = 0; /* Zero-terminate the result, an extra byte is available */
   2737     break;
   2738   case mhd_POST_UENC_ST_VALUE:
   2739     mhd_assert (0 != uf->value_idx);
   2740     name_start = uf->name_idx;
   2741     name_len = uf->name_len;
   2742     mhd_assert (0 == buf[name_start + name_len]);
   2743     if (0 != uf->value_len)
   2744     {
   2745       /* The value was partially decoded and then application requested stream
   2746        * suspending. */
   2747       mhd_assert (pos < *pdata_size);
   2748       mhd_assert (2 >= *pdata_size - pos);
   2749       value_start = uf->value_idx;
   2750       if (uf->value_idx + uf->value_len != pos)
   2751         memmove (buf + uf->value_idx + uf->value_len,
   2752                  buf + pos,
   2753                  *pdata_size - pos);
   2754       value_len = uf->value_len + *pdata_size - pos;
   2755     }
   2756     else
   2757     {
   2758       /* The value has not been decoded yet */
   2759       mhd_assert (pos == *pdata_size);
   2760       value_start = uf->value_idx;
   2761       if (uf->last_pct_idx != mhd_POST_INVALID_POS)
   2762         value_len = mhd_str_pct_decode_lenient_n (buf + uf->value_idx,
   2763                                                   pos - uf->value_idx,
   2764                                                   buf + uf->value_idx,
   2765                                                   pos - uf->value_idx,
   2766                                                   NULL);
   2767       else
   2768         value_len = pos - uf->value_idx;
   2769     }
   2770     if (! have_extra_space &&
   2771         (value_start + value_len == pos))
   2772       need_value_stream = true;
   2773     else
   2774       buf[value_start + value_len] = 0; /* Zero-terminate the result, an extra byte is available in the buffer */
   2775     break;
   2776   case mhd_POST_UENC_ST_FULL_FIELD_FOUND:
   2777     /* Full value was found, but the stream has been suspended by
   2778      * the application */
   2779     mhd_assert (pos + 1 == *pdata_size);
   2780     mhd_assert (0 != uf->value_idx);
   2781     mhd_assert (pos != uf->value_idx);
   2782     name_start = uf->name_idx;
   2783     name_len = uf->name_len;
   2784     value_start = uf->value_idx;
   2785     value_len = uf->value_len;
   2786     mhd_assert (0 == buf[name_start + name_len]);
   2787     mhd_assert (0 == buf[value_start + value_len]);
   2788     ++pos;
   2789     mhd_assert (pos == *pdata_size);
   2790     break;
   2791   case mhd_POST_UENC_ST_AT_EQ:
   2792   case mhd_POST_UENC_ST_AT_AMPRSND:
   2793   default:
   2794     mhd_UNREACHABLE ();
   2795     p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
   2796     return false;
   2797   }
   2798 
   2799   /* Have field data */
   2800 
   2801   p_data->next_parse_pos = pos;
   2802 
   2803   if (need_value_stream)
   2804   {
   2805     if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
   2806       p_data->force_streamed = true;
   2807     else
   2808       return report_low_lbuf_mem (c);
   2809   }
   2810 
   2811   if (process_complete_field (c,
   2812                               buf,
   2813                               &(p_data->next_parse_pos),
   2814                               pdata_size,
   2815                               p_data->field_start,
   2816                               name_start,
   2817                               name_len,
   2818                               value_start,
   2819                               value_len))
   2820     return true;
   2821 
   2822   reset_parse_field_data_urlenc (p_data);
   2823 
   2824   return false; /* Continue normal processing */
   2825 }
   2826 
   2827 
   2828 /**
   2829  * Check whether some unprocessed or partially processed data left in buffers
   2830  * for "multipart/form-data" POST encoding.
   2831  * @param c the stream to use
   2832  * @param pdata_size the pointer to the size of the data in the buffer
   2833  * @param buf the buffer with the data
   2834  * @return 'true' if stream state was changed,
   2835  *         'false' to continue normal processing
   2836  */
   2837 static MHD_FN_PAR_NONNULL_ALL_
   2838 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
   2839 check_post_leftovers_mpart (struct MHD_Connection *restrict c,
   2840                             size_t *restrict pdata_size,
   2841                             char *restrict buf)
   2842 {
   2843   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   2844   struct mhd_PostParserMPartFormData *const mf = &(p_data->e_d.m_form); /**< the current "form-data" parsing details */
   2845   size_t pos;
   2846   bool not_terminated;
   2847   bool add_field;
   2848   size_t value_pos;
   2849   size_t value_len;
   2850 
   2851   pos = p_data->next_parse_pos;  /* Points to the char AFTER the data, valid location as buffer is always at least one byte larger */
   2852   mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
   2853   mhd_assert (*pdata_size >= pos);
   2854 
   2855   not_terminated = false;
   2856   add_field = false;
   2857   value_pos = 0;
   2858   value_len = 0;
   2859 
   2860   switch (mf->st)
   2861   {
   2862   case mhd_POST_MPART_ST_NOT_STARTED:
   2863   case mhd_POST_MPART_ST_PREAMBL:
   2864   case mhd_POST_MPART_ST_PREAMBL_CR_FOUND:
   2865   case mhd_POST_MPART_ST_PREAMBL_CHECKING_FOR_DELIM:
   2866     mhd_assert (pos == *pdata_size);
   2867     return false; /* Continue processing */
   2868   case mhd_POST_MPART_ST_FIRST_DELIM_FOUND:
   2869   case mhd_POST_MPART_ST_FIRST_PART_START:
   2870   case mhd_POST_MPART_ST_PART_START:
   2871     mhd_assert (pos == *pdata_size);
   2872     not_terminated = true;
   2873     break;
   2874   case mhd_POST_MPART_ST_HEADER_LINE_START:
   2875   case mhd_POST_MPART_ST_HEADER_LINE:
   2876   case mhd_POST_MPART_ST_HEADER_LINE_CR_FOUND:
   2877   case mhd_POST_MPART_ST_VALUE_START:
   2878     mhd_assert (pos == *pdata_size);
   2879     not_terminated = true;
   2880     add_field = (0 != mf->f.name_idx);
   2881     break;
   2882   case mhd_POST_MPART_ST_VALUE:
   2883   case mhd_POST_MPART_ST_VALUE_CR_FOUND:
   2884   case mhd_POST_MPART_ST_VALUE_LINE_START:
   2885   case mhd_POST_MPART_ST_VALUE_CHECKING_FOR_DELIM:
   2886     mhd_assert (0 != mf->f.name_idx);
   2887     mhd_assert (0 != mf->f.value_idx);
   2888     not_terminated = true;
   2889     add_field = true;
   2890     value_pos = mf->f.value_idx;
   2891     value_len = pos - value_len;
   2892     break;
   2893   case mhd_POST_MPART_ST_DELIM_FOUND:
   2894     mhd_assert (0 != mf->f.name_idx);
   2895     mhd_assert (mhd_POST_INVALID_POS != mf->delim_check_start);
   2896     mhd_assert (pos > mf->delim_check_start);
   2897     not_terminated = true;
   2898     add_field = true;
   2899     if (0 != mf->f.value_idx)
   2900     {
   2901       value_pos = mf->f.value_idx;
   2902       value_len = mf->delim_check_start - mf->f.value_idx;
   2903     }
   2904     break;
   2905   case mhd_POST_MPART_ST_FULL_FIELD_FOUND:
   2906     not_terminated = true;
   2907     mhd_FALLTHROUGH;
   2908   /* Intentional fallthrough */
   2909   case mhd_POST_MPART_ST_FULL_FIELD_FOUND_FINAL:
   2910     mhd_assert (0 != mf->f.name_idx);
   2911     add_field = true;
   2912     if (0 != mf->f.value_idx)
   2913     {
   2914       value_pos = mf->f.value_idx;
   2915       value_len = mf->f.value_len;
   2916     }
   2917     break;
   2918   case mhd_POST_MPART_ST_EPILOGUE:
   2919   case mhd_POST_MPART_ST_FORMAT_ERROR:
   2920     return false; /* Continue processing */
   2921   case mhd_POST_MPART_ST_BACK_TO_PREAMBL:
   2922   case mhd_POST_MPART_ST_PREAMBL_LINE_START:
   2923   case mhd_POST_MPART_ST_HEADER_LINE_END:
   2924   case mhd_POST_MPART_ST_BACK_TO_VALUE:
   2925   case mhd_POST_MPART_ST_VALUE_END_FOUND:
   2926   case mhd_POST_MPART_ST_VALUE_END_FOUND_FINAL:
   2927   default:
   2928     mhd_UNREACHABLE ();
   2929     p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
   2930     return false;
   2931   }
   2932 
   2933   if (not_terminated)
   2934     report_invalid_termination (c); /* The "closing" delimiter is missing */
   2935 
   2936   if (add_field)
   2937   {
   2938     if (c->rq.cntn.lbuf.size > (value_pos + value_len))
   2939       buf [value_pos + value_len] = 0; /* Zero-terminate the value */
   2940     else if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
   2941       p_data->force_streamed = true;
   2942     else
   2943       return report_low_lbuf_mem (c);
   2944 
   2945     p_data->next_parse_pos = pos;
   2946 
   2947     if (process_complete_field_all (c,
   2948                                     buf,
   2949                                     &p_data->next_parse_pos,
   2950                                     pdata_size,
   2951                                     p_data->field_start,
   2952                                     mf->f.name_idx,
   2953                                     mf->f.name_len,
   2954                                     mf->f.filename_idx,
   2955                                     mf->f.filename_len,
   2956                                     mf->f.cntn_type_idx,
   2957                                     mf->f.cntn_type_len,
   2958                                     mf->f.enc_idx,
   2959                                     mf->f.enc_len,
   2960                                     value_pos,
   2961                                     value_len))
   2962       return true;
   2963   }
   2964 
   2965   reset_parse_field_data_mpart_cont (p_data,
   2966                                      ! not_terminated);
   2967 
   2968   return false; /* Continue normal processing */
   2969 }
   2970 
   2971 
   2972 /**
   2973  * Check whether some unprocessed or partially processed data left in buffers
   2974  * for "text" POST encoding.
   2975  * @param c the stream to use
   2976  * @param pdata_size the pointer to the size of the data in the buffer
   2977  * @param buf the buffer with the data
   2978  * @return 'true' if stream state was changed,
   2979  *         'false' to continue normal processing
   2980  */
   2981 static MHD_FN_PAR_NONNULL_ALL_
   2982 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) bool
   2983 check_post_leftovers_text (struct MHD_Connection *restrict c,
   2984                            size_t *restrict pdata_size,
   2985                            char *restrict buf)
   2986 {
   2987   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   2988   struct mhd_PostParserTextData *const tf = &(p_data->e_d.text); /**< the current "text" field */
   2989   size_t pos;
   2990   size_t name_start;
   2991   size_t name_len;
   2992   size_t value_start;
   2993   size_t value_len;
   2994 
   2995   pos = p_data->next_parse_pos;  /* Points to the char AFTER the data, valid location as buffer is always at least one byte larger */
   2996   mhd_assert (pos <= c->rq.cntn.lbuf.size); // TODO: support processing in connection buffer
   2997   switch (tf->st)
   2998   {
   2999   case mhd_POST_TEXT_ST_NOT_STARTED:
   3000     mhd_assert (pos == *pdata_size);
   3001     return false; /* Continue processing */
   3002   case mhd_POST_TEXT_ST_NAME:
   3003     /* Unfinished name */
   3004     mhd_assert (pos == *pdata_size);
   3005     name_start = tf->name_idx;
   3006     name_len = pos - name_start;
   3007     if (pos == c->rq.cntn.lbuf.size)
   3008       return report_low_lbuf_mem (c);
   3009     buf[pos] = 0; /* Zero-terminate the result, an extra byte is available */
   3010     value_start = 0;
   3011     value_len = 0;
   3012     break;
   3013   case mhd_POST_TEXT_ST_EQ_FOUND:
   3014     mhd_assert (pos == *pdata_size);
   3015     name_start = tf->name_idx;
   3016     name_len = tf->name_len;
   3017     value_start = pos;
   3018     value_len = 0;
   3019     break;
   3020   case mhd_POST_TEXT_ST_VALUE:
   3021     mhd_assert (pos == *pdata_size);
   3022     mhd_assert (0 != tf->value_idx);
   3023     mhd_assert (pos != tf->value_idx);
   3024     name_start = tf->name_idx;
   3025     name_len = tf->name_len;
   3026     value_start = tf->value_idx;
   3027     value_len = pos - value_start;
   3028     break;
   3029   case mhd_POST_TEXT_ST_CR_FOUND:
   3030     mhd_assert (pos == *pdata_size);
   3031     mhd_assert (0 != tf->value_idx);
   3032     mhd_assert (pos != tf->value_idx);
   3033     name_start = tf->name_idx;
   3034     name_len = tf->name_len;
   3035     value_start = tf->value_idx;
   3036     value_len = tf->value_len;
   3037     mhd_assert (value_start + value_len + 1 == pos);
   3038     mhd_assert (0 == buf[value_start + value_len]);
   3039     break;
   3040   case mhd_POST_TEXT_ST_FULL_LINE_FOUND:
   3041     /* Full value was found, but the stream has been suspended by
   3042      * the application */
   3043     mhd_assert (pos + 1 == *pdata_size);
   3044     mhd_assert (0 != tf->value_idx);
   3045     name_start = tf->name_idx;
   3046     name_len = tf->name_len;
   3047     value_start = tf->value_idx;
   3048     value_len = tf->value_len;
   3049     mhd_assert ((value_start + value_len + 1 == pos) || \
   3050                 (value_start + value_len + 2 == pos));
   3051     mhd_assert (0 == buf[value_start + value_len]);
   3052     ++pos;
   3053     mhd_assert (pos == *pdata_size);
   3054     break;
   3055   case mhd_POST_TEXT_ST_AT_EQ:
   3056   case mhd_POST_TEXT_ST_AT_LF_BARE:
   3057   case mhd_POST_TEXT_ST_AT_CR:
   3058   default:
   3059     mhd_UNREACHABLE ();
   3060     p_data->parse_result = MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
   3061     return false;
   3062   }
   3063 
   3064   if (tf->st != mhd_POST_TEXT_ST_FULL_LINE_FOUND)
   3065     report_invalid_termination (c); /* The line must be terminated by CRLF, but it is not */
   3066 
   3067   if (0 != value_start)
   3068   {
   3069     if (c->rq.cntn.lbuf.size > (value_start + value_len))
   3070       buf [value_start + value_len] = 0; /* Zero-terminate the value */
   3071     else if (NULL != c->rq.app_act.head_act.data.post_parse.stream_reader)
   3072       p_data->force_streamed = true;
   3073     else
   3074       return report_low_lbuf_mem (c);
   3075   }
   3076 
   3077   if (process_complete_field (c,
   3078                               buf,
   3079                               &pos,
   3080                               pdata_size,
   3081                               p_data->field_start,
   3082                               name_start,
   3083                               name_len,
   3084                               value_start,
   3085                               value_len))
   3086     return true;
   3087 
   3088   reset_parse_field_data_text (p_data);
   3089 
   3090   return false; /* Continue normal processing */
   3091 }
   3092 
   3093 
   3094 /**
   3095  * Check in leftover POST data in the buffers
   3096  * @param c the stream to use
   3097  * @return 'true' if stream state is changed,
   3098  *         'false' to continue
   3099  */
   3100 static MHD_FN_PAR_NONNULL_ALL_ bool
   3101 check_post_leftovers (struct MHD_Connection *restrict c)
   3102 {
   3103   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   3104   // TODO: implement processing in the connection buffer
   3105   if (p_data->lbuf_used == c->rq.cntn.lbuf.size)
   3106     (void) extend_lbuf_up_to (c,
   3107                               1,
   3108                               &(c->rq.cntn.lbuf));
   3109 
   3110   switch (p_data->enc)
   3111   {
   3112   case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
   3113     return check_post_leftovers_urlenc (c,
   3114                                         &(p_data->lbuf_used),
   3115                                         c->rq.cntn.lbuf.data);
   3116   case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
   3117     return check_post_leftovers_mpart (c,
   3118                                        &(p_data->lbuf_used),
   3119                                        c->rq.cntn.lbuf.data);
   3120   case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
   3121     return check_post_leftovers_text (c,
   3122                                       &(p_data->lbuf_used),
   3123                                       c->rq.cntn.lbuf.data);
   3124   case MHD_HTTP_POST_ENCODING_OTHER:
   3125   default:
   3126     mhd_UNREACHABLE ();
   3127     p_data->parse_result =
   3128       MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
   3129     c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   3130     break;
   3131   }
   3132   return true;
   3133 }
   3134 
   3135 
   3136 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   3137 mhd_stream_process_post_finish (struct MHD_Connection *restrict c)
   3138 {
   3139   struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
   3140   const struct MHD_UploadAction *act;
   3141   bool state_changed;
   3142 
   3143   if ((MHD_POST_PARSE_RES_OK == p_data->parse_result) &&
   3144       ! c->discard_request)
   3145   {
   3146     // TODO: implement processing in the connection buffer
   3147     if (check_post_leftovers (c))
   3148       return true;
   3149   }
   3150 
   3151   act = c->rq.app_act.head_act.data.post_parse.done_cb (
   3152     &(c->rq),
   3153     c->rq.app_act.head_act.data.post_parse.done_cb_cls,
   3154     p_data->parse_result);
   3155 
   3156   state_changed = mhd_stream_process_upload_action (c, act, true);
   3157   if (! c->suspended)
   3158     mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
   3159   return state_changed;
   3160 }