libmicrohttpd2

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

post_parser_funcs.c (113995B)


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