"Fossies" - the Fresh Open Source Software Archive

Member "selenium-selenium-4.8.1/third_party/cpp/civetweb/handle_form.inl" (17 Feb 2023, 29689 Bytes) of package /linux/www/selenium-selenium-4.8.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* Copyright (c) 2016-2018 the Civetweb developers
    2  *
    3  * Permission is hereby granted, free of charge, to any person obtaining a copy
    4  * of this software and associated documentation files (the "Software"), to deal
    5  * in the Software without restriction, including without limitation the rights
    6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    7  * copies of the Software, and to permit persons to whom the Software is
    8  * furnished to do so, subject to the following conditions:
    9  *
   10  * The above copyright notice and this permission notice shall be included in
   11  * all copies or substantial portions of the Software.
   12  *
   13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   19  * THE SOFTWARE.
   20  */
   21 
   22 
   23 static int
   24 url_encoded_field_found(const struct mg_connection *conn,
   25                         const char *key,
   26                         size_t key_len,
   27                         const char *filename,
   28                         size_t filename_len,
   29                         char *path,
   30                         size_t path_len,
   31                         struct mg_form_data_handler *fdh)
   32 {
   33     char key_dec[1024];
   34     char filename_dec[1024];
   35     int key_dec_len;
   36     int filename_dec_len;
   37     int ret;
   38 
   39     key_dec_len =
   40         mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
   41 
   42     if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
   43         return MG_FORM_FIELD_STORAGE_SKIP;
   44     }
   45 
   46     if (filename) {
   47         filename_dec_len = mg_url_decode(filename,
   48                                          (int)filename_len,
   49                                          filename_dec,
   50                                          (int)sizeof(filename_dec),
   51                                          1);
   52 
   53         if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
   54             || (filename_dec_len < 0)) {
   55             /* Log error message and skip this field. */
   56             mg_cry_internal(conn, "%s: Cannot decode filename", __func__);
   57             return MG_FORM_FIELD_STORAGE_SKIP;
   58         }
   59     } else {
   60         filename_dec[0] = 0;
   61     }
   62 
   63     ret =
   64         fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
   65 
   66     if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_GET) {
   67         if (fdh->field_get == NULL) {
   68             mg_cry_internal(conn,
   69                             "%s: Function \"Get\" not available",
   70                             __func__);
   71             return MG_FORM_FIELD_STORAGE_SKIP;
   72         }
   73     }
   74     if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_STORE) {
   75         if (fdh->field_store == NULL) {
   76             mg_cry_internal(conn,
   77                             "%s: Function \"Store\" not available",
   78                             __func__);
   79             return MG_FORM_FIELD_STORAGE_SKIP;
   80         }
   81     }
   82 
   83     return ret;
   84 }
   85 
   86 
   87 static int
   88 url_encoded_field_get(const struct mg_connection *conn,
   89                       const char *key,
   90                       size_t key_len,
   91                       const char *value,
   92                       size_t value_len,
   93                       struct mg_form_data_handler *fdh)
   94 {
   95     char key_dec[1024];
   96 
   97     char *value_dec = (char *)mg_malloc_ctx(value_len + 1, conn->phys_ctx);
   98     int value_dec_len, ret;
   99 
  100     if (!value_dec) {
  101         /* Log error message and stop parsing the form data. */
  102         mg_cry_internal(conn,
  103                         "%s: Not enough memory (required: %lu)",
  104                         __func__,
  105                         (unsigned long)(value_len + 1));
  106         return MG_FORM_FIELD_STORAGE_ABORT;
  107     }
  108 
  109     mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
  110 
  111     value_dec_len =
  112         mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
  113 
  114     ret = fdh->field_get(key_dec,
  115                          value_dec,
  116                          (size_t)value_dec_len,
  117                          fdh->user_data);
  118 
  119     mg_free(value_dec);
  120 
  121     return ret;
  122 }
  123 
  124 
  125 static int
  126 unencoded_field_get(const struct mg_connection *conn,
  127                     const char *key,
  128                     size_t key_len,
  129                     const char *value,
  130                     size_t value_len,
  131                     struct mg_form_data_handler *fdh)
  132 {
  133     char key_dec[1024];
  134     (void)conn;
  135 
  136     mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
  137 
  138     return fdh->field_get(key_dec, value, value_len, fdh->user_data);
  139 }
  140 
  141 
  142 static int
  143 field_stored(const struct mg_connection *conn,
  144              const char *path,
  145              long long file_size,
  146              struct mg_form_data_handler *fdh)
  147 {
  148     /* Equivalent to "upload" callback of "mg_upload". */
  149 
  150     (void)conn; /* we do not need mg_cry here, so conn is currently unused */
  151 
  152     return fdh->field_store(path, file_size, fdh->user_data);
  153 }
  154 
  155 
  156 static const char *
  157 search_boundary(const char *buf,
  158                 size_t buf_len,
  159                 const char *boundary,
  160                 size_t boundary_len)
  161 {
  162     /* We must do a binary search here, not a string search, since the buffer
  163      * may contain '\x00' bytes, if binary data is transferred. */
  164     int clen = (int)buf_len - (int)boundary_len - 4;
  165     int i;
  166 
  167     for (i = 0; i <= clen; i++) {
  168         if (!memcmp(buf + i, "\r\n--", 4)) {
  169             if (!memcmp(buf + i + 4, boundary, boundary_len)) {
  170                 return buf + i;
  171             }
  172         }
  173     }
  174     return NULL;
  175 }
  176 
  177 
  178 int
  179 mg_handle_form_request(struct mg_connection *conn,
  180                        struct mg_form_data_handler *fdh)
  181 {
  182     const char *content_type;
  183     char path[512];
  184     char buf[MG_BUF_LEN]; /* Must not be smaller than ~900 */
  185     int field_storage;
  186     int buf_fill = 0;
  187     int r;
  188     int field_count = 0;
  189     struct mg_file fstore = STRUCT_FILE_INITIALIZER;
  190     int64_t file_size = 0; /* init here, to a avoid a false positive
  191                              "uninitialized variable used" warning */
  192 
  193     int has_body_data =
  194         (conn->request_info.content_length > 0) || (conn->is_chunked);
  195 
  196     /* There are three ways to encode data from a HTML form:
  197      * 1) method: GET (default)
  198      *    The form data is in the HTTP query string.
  199      * 2) method: POST, enctype: "application/x-www-form-urlencoded"
  200      *    The form data is in the request body.
  201      *    The body is url encoded (the default encoding for POST).
  202      * 3) method: POST, enctype: "multipart/form-data".
  203      *    The form data is in the request body of a multipart message.
  204      *    This is the typical way to handle file upload from a form.
  205      */
  206 
  207     if (!has_body_data) {
  208         const char *data;
  209 
  210         if (0 != strcmp(conn->request_info.request_method, "GET")) {
  211             /* No body data, but not a GET request.
  212              * This is not a valid form request. */
  213             return -1;
  214         }
  215 
  216         /* GET request: form data is in the query string. */
  217         /* The entire data has already been loaded, so there is no nead to
  218          * call mg_read. We just need to split the query string into key-value
  219          * pairs. */
  220         data = conn->request_info.query_string;
  221         if (!data) {
  222             /* No query string. */
  223             return -1;
  224         }
  225 
  226         /* Split data in a=1&b=xy&c=3&c=4 ... */
  227         while (*data) {
  228             const char *val = strchr(data, '=');
  229             const char *next;
  230             ptrdiff_t keylen, vallen;
  231 
  232             if (!val) {
  233                 break;
  234             }
  235             keylen = val - data;
  236 
  237             /* In every "field_found" callback we ask what to do with the
  238              * data ("field_storage"). This could be:
  239              * MG_FORM_FIELD_STORAGE_SKIP (0):
  240              *   ignore the value of this field
  241              * MG_FORM_FIELD_STORAGE_GET (1):
  242              *   read the data and call the get callback function
  243              * MG_FORM_FIELD_STORAGE_STORE (2):
  244              *   store the data in a file
  245              * MG_FORM_FIELD_STORAGE_READ (3):
  246              *   let the user read the data (for parsing long data on the fly)
  247              * MG_FORM_FIELD_STORAGE_ABORT (flag):
  248              *   stop parsing
  249              */
  250             memset(path, 0, sizeof(path));
  251             field_count++;
  252             field_storage = url_encoded_field_found(conn,
  253                                                     data,
  254                                                     (size_t)keylen,
  255                                                     NULL,
  256                                                     0,
  257                                                     path,
  258                                                     sizeof(path) - 1,
  259                                                     fdh);
  260 
  261             val++;
  262             next = strchr(val, '&');
  263             if (next) {
  264                 vallen = next - val;
  265                 next++;
  266             } else {
  267                 vallen = (ptrdiff_t)strlen(val);
  268                 next = val + vallen;
  269             }
  270 
  271             if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
  272                 /* Call callback */
  273                 r = url_encoded_field_get(
  274                     conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
  275                 if (r == MG_FORM_FIELD_HANDLE_ABORT) {
  276                     /* Stop request handling */
  277                     break;
  278                 }
  279                 if (r == MG_FORM_FIELD_HANDLE_NEXT) {
  280                     /* Skip to next field */
  281                     field_storage = MG_FORM_FIELD_STORAGE_SKIP;
  282                 }
  283             }
  284             if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
  285                 /* Store the content to a file */
  286                 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
  287                     fstore.access.fp = NULL;
  288                 }
  289                 file_size = 0;
  290                 if (fstore.access.fp != NULL) {
  291                     size_t n = (size_t)
  292                         fwrite(val, 1, (size_t)vallen, fstore.access.fp);
  293                     if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
  294                         mg_cry_internal(conn,
  295                                         "%s: Cannot write file %s",
  296                                         __func__,
  297                                         path);
  298                         (void)mg_fclose(&fstore.access);
  299                         remove_bad_file(conn, path);
  300                     }
  301                     file_size += (int64_t)n;
  302 
  303                     if (fstore.access.fp) {
  304                         r = mg_fclose(&fstore.access);
  305                         if (r == 0) {
  306                             /* stored successfully */
  307                             r = field_stored(conn, path, file_size, fdh);
  308                             if (r == MG_FORM_FIELD_HANDLE_ABORT) {
  309                                 /* Stop request handling */
  310                                 break;
  311                             }
  312 
  313                         } else {
  314                             mg_cry_internal(conn,
  315                                             "%s: Error saving file %s",
  316                                             __func__,
  317                                             path);
  318                             remove_bad_file(conn, path);
  319                         }
  320                         fstore.access.fp = NULL;
  321                     }
  322 
  323                 } else {
  324                     mg_cry_internal(conn,
  325                                     "%s: Cannot create file %s",
  326                                     __func__,
  327                                     path);
  328                 }
  329             }
  330 
  331             /* if (field_storage == MG_FORM_FIELD_STORAGE_READ) { */
  332             /* The idea of "field_storage=read" is to let the API user read
  333              * data chunk by chunk and to some data processing on the fly.
  334              * This should avoid the need to store data in the server:
  335              * It should neither be stored in memory, like
  336              * "field_storage=get" does, nor in a file like
  337              * "field_storage=store".
  338              * However, for a "GET" request this does not make any much
  339              * sense, since the data is already stored in memory, as it is
  340              * part of the query string.
  341              */
  342             /* } */
  343 
  344             if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
  345                 == MG_FORM_FIELD_STORAGE_ABORT) {
  346                 /* Stop parsing the request */
  347                 break;
  348             }
  349 
  350             /* Proceed to next entry */
  351             data = next;
  352         }
  353 
  354         return field_count;
  355     }
  356 
  357     content_type = mg_get_header(conn, "Content-Type");
  358 
  359     if (!content_type
  360         || !mg_strncasecmp(content_type,
  361                            "APPLICATION/X-WWW-FORM-URLENCODED",
  362                            33)
  363         || !mg_strncasecmp(content_type,
  364                            "APPLICATION/WWW-FORM-URLENCODED",
  365                            31)) {
  366         /* The form data is in the request body data, encoded in key/value
  367          * pairs. */
  368         int all_data_read = 0;
  369 
  370         /* Read body data and split it in keys and values.
  371          * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
  372          * Here we use "POST", and read the data from the request body.
  373          * The data read on the fly, so it is not required to buffer the
  374          * entire request in memory before processing it. */
  375         for (;;) {
  376             const char *val;
  377             const char *next;
  378             ptrdiff_t keylen, vallen;
  379             ptrdiff_t used;
  380             int end_of_key_value_pair_found = 0;
  381             int get_block;
  382 
  383             if ((size_t)buf_fill < (sizeof(buf) - 1)) {
  384 
  385                 size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
  386                 r = mg_read(conn, buf + (size_t)buf_fill, to_read);
  387                 if (r < 0) {
  388                     /* read error */
  389                     return -1;
  390                 }
  391                 if (r != (int)to_read) {
  392                     /* TODO: Create a function to get "all_data_read" from
  393                      * the conn object. All data is read if the Content-Length
  394                      * has been reached, or if chunked encoding is used and
  395                      * the end marker has been read, or if the connection has
  396                      * been closed. */
  397                     all_data_read = 1;
  398                 }
  399                 buf_fill += r;
  400                 buf[buf_fill] = 0;
  401                 if (buf_fill < 1) {
  402                     break;
  403                 }
  404             }
  405 
  406             val = strchr(buf, '=');
  407 
  408             if (!val) {
  409                 break;
  410             }
  411             keylen = val - buf;
  412             val++;
  413 
  414             /* Call callback */
  415             memset(path, 0, sizeof(path));
  416             field_count++;
  417             field_storage = url_encoded_field_found(conn,
  418                                                     buf,
  419                                                     (size_t)keylen,
  420                                                     NULL,
  421                                                     0,
  422                                                     path,
  423                                                     sizeof(path) - 1,
  424                                                     fdh);
  425 
  426             if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
  427                 == MG_FORM_FIELD_STORAGE_ABORT) {
  428                 /* Stop parsing the request */
  429                 break;
  430             }
  431 
  432             if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
  433                 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
  434                     fstore.access.fp = NULL;
  435                 }
  436                 file_size = 0;
  437                 if (!fstore.access.fp) {
  438                     mg_cry_internal(conn,
  439                                     "%s: Cannot create file %s",
  440                                     __func__,
  441                                     path);
  442                 }
  443             }
  444 
  445             get_block = 0;
  446             /* Loop to read values larger than sizeof(buf)-keylen-2 */
  447             do {
  448                 next = strchr(val, '&');
  449                 if (next) {
  450                     vallen = next - val;
  451                     next++;
  452                     end_of_key_value_pair_found = 1;
  453                 } else {
  454                     vallen = (ptrdiff_t)strlen(val);
  455                     next = val + vallen;
  456                     end_of_key_value_pair_found = all_data_read;
  457                 }
  458 
  459                 if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
  460 #if 0
  461                     if (!end_of_key_value_pair_found && !all_data_read) {
  462                         /* This callback will deliver partial contents */
  463                     }
  464 #endif
  465 
  466                     /* Call callback */
  467                     r = url_encoded_field_get(conn,
  468                                               ((get_block > 0) ? NULL : buf),
  469                                               ((get_block > 0)
  470                                                    ? 0
  471                                                    : (size_t)keylen),
  472                                               val,
  473                                               (size_t)vallen,
  474                                               fdh);
  475                     get_block++;
  476                     if (r == MG_FORM_FIELD_HANDLE_ABORT) {
  477                         /* Stop request handling */
  478                         break;
  479                     }
  480                     if (r == MG_FORM_FIELD_HANDLE_NEXT) {
  481                         /* Skip to next field */
  482                         field_storage = MG_FORM_FIELD_STORAGE_SKIP;
  483                     }
  484                 }
  485                 if (fstore.access.fp) {
  486                     size_t n = (size_t)
  487                         fwrite(val, 1, (size_t)vallen, fstore.access.fp);
  488                     if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
  489                         mg_cry_internal(conn,
  490                                         "%s: Cannot write file %s",
  491                                         __func__,
  492                                         path);
  493                         mg_fclose(&fstore.access);
  494                         remove_bad_file(conn, path);
  495                     }
  496                     file_size += (int64_t)n;
  497                 }
  498 
  499                 if (!end_of_key_value_pair_found) {
  500                     used = next - buf;
  501                     memmove(buf,
  502                             buf + (size_t)used,
  503                             sizeof(buf) - (size_t)used);
  504                     next = buf;
  505                     buf_fill -= (int)used;
  506                     if ((size_t)buf_fill < (sizeof(buf) - 1)) {
  507 
  508                         size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
  509                         r = mg_read(conn, buf + (size_t)buf_fill, to_read);
  510                         if (r < 0) {
  511                             /* read error */
  512                             if (fstore.access.fp) {
  513                                 mg_fclose(&fstore.access);
  514                                 remove_bad_file(conn, path);
  515                             }
  516                             return -1;
  517                         }
  518                         if (r != (int)to_read) {
  519                             /* TODO: Create a function to get "all_data_read"
  520                              * from the conn object. All data is read if the
  521                              * Content-Length has been reached, or if chunked
  522                              * encoding is used and the end marker has been
  523                              * read, or if the connection has been closed. */
  524                             all_data_read = 1;
  525                         }
  526                         buf_fill += r;
  527                         buf[buf_fill] = 0;
  528                         if (buf_fill < 1) {
  529                             break;
  530                         }
  531                         val = buf;
  532                     }
  533                 }
  534 
  535             } while (!end_of_key_value_pair_found);
  536 
  537             if (fstore.access.fp) {
  538                 r = mg_fclose(&fstore.access);
  539                 if (r == 0) {
  540                     /* stored successfully */
  541                     r = field_stored(conn, path, file_size, fdh);
  542                     if (r == MG_FORM_FIELD_HANDLE_ABORT) {
  543                         /* Stop request handling */
  544                         break;
  545                     }
  546                 } else {
  547                     mg_cry_internal(conn,
  548                                     "%s: Error saving file %s",
  549                                     __func__,
  550                                     path);
  551                     remove_bad_file(conn, path);
  552                 }
  553                 fstore.access.fp = NULL;
  554             }
  555 
  556             if (all_data_read && (buf_fill == 0)) {
  557                 /* nothing more to process */
  558                 break;
  559             }
  560 
  561             /* Proceed to next entry */
  562             used = next - buf;
  563             memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
  564             buf_fill -= (int)used;
  565         }
  566 
  567         return field_count;
  568     }
  569 
  570     if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
  571         /* The form data is in the request body data, encoded as multipart
  572          * content (see https://www.ietf.org/rfc/rfc1867.txt,
  573          * https://www.ietf.org/rfc/rfc2388.txt). */
  574         char *boundary;
  575         size_t bl;
  576         ptrdiff_t used;
  577         struct mg_request_info part_header;
  578         char *hbuf;
  579         const char *content_disp, *hend, *fbeg, *fend, *nbeg, *nend;
  580         const char *next;
  581         unsigned part_no;
  582 
  583         memset(&part_header, 0, sizeof(part_header));
  584 
  585         /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
  586         bl = 20;
  587         while (content_type[bl] == ' ') {
  588             bl++;
  589         }
  590 
  591         /* There has to be a BOUNDARY definition in the Content-Type header */
  592         if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
  593             /* Malformed request */
  594             return -1;
  595         }
  596 
  597         /* Copy boundary string to variable "boundary" */
  598         fbeg = content_type + bl + 9;
  599         bl = strlen(fbeg);
  600         boundary = (char *)mg_malloc(bl + 1);
  601         if (!boundary) {
  602             /* Out of memory */
  603             mg_cry_internal(conn,
  604                             "%s: Cannot allocate memory for boundary [%lu]",
  605                             __func__,
  606                             (unsigned long)bl);
  607             return -1;
  608         }
  609         memcpy(boundary, fbeg, bl);
  610         boundary[bl] = 0;
  611 
  612         /* RFC 2046 permits the boundary string to be quoted. */
  613         /* If the boundary is quoted, trim the quotes */
  614         if (boundary[0] == '"') {
  615             hbuf = strchr(boundary + 1, '"');
  616             if ((!hbuf) || (*hbuf != '"')) {
  617                 /* Malformed request */
  618                 mg_free(boundary);
  619                 return -1;
  620             }
  621             *hbuf = 0;
  622             memmove(boundary, boundary + 1, bl);
  623             bl = strlen(boundary);
  624         }
  625 
  626         /* Do some sanity checks for boundary lengths */
  627         if (bl > 70) {
  628             /* From RFC 2046:
  629              * Boundary delimiters must not appear within the
  630              * encapsulated material, and must be no longer
  631              * than 70 characters, not counting the two
  632              * leading hyphens.
  633              */
  634 
  635             /* The algorithm can not work if bl >= sizeof(buf), or if buf
  636              * can not hold the multipart header plus the boundary.
  637              * Requests with long boundaries are not RFC compliant, maybe they
  638              * are intended attacks to interfere with this algorithm. */
  639             mg_free(boundary);
  640             return -1;
  641         }
  642         if (bl < 4) {
  643             /* Sanity check:  A boundary string of less than 4 bytes makes
  644              * no sense either. */
  645             mg_free(boundary);
  646             return -1;
  647         }
  648 
  649         for (part_no = 0;; part_no++) {
  650             size_t towrite, fnlen, n;
  651             int get_block;
  652 
  653             r = mg_read(conn,
  654                         buf + (size_t)buf_fill,
  655                         sizeof(buf) - 1 - (size_t)buf_fill);
  656             if (r < 0) {
  657                 /* read error */
  658                 mg_free(boundary);
  659                 return -1;
  660             }
  661             buf_fill += r;
  662             buf[buf_fill] = 0;
  663             if (buf_fill < 1) {
  664                 /* No data */
  665                 mg_free(boundary);
  666                 return -1;
  667             }
  668 
  669             if (part_no == 0) {
  670                 int d = 0;
  671                 while ((buf[d] != '-') && (d < buf_fill)) {
  672                     d++;
  673                 }
  674                 if ((d > 0) && (buf[d] == '-')) {
  675                     memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d);
  676                     buf_fill -= d;
  677                     buf[buf_fill] = 0;
  678                 }
  679             }
  680 
  681             if (buf[0] != '-' || buf[1] != '-') {
  682                 /* Malformed request */
  683                 mg_free(boundary);
  684                 return -1;
  685             }
  686             if (0 != strncmp(buf + 2, boundary, bl)) {
  687                 /* Malformed request */
  688                 mg_free(boundary);
  689                 return -1;
  690             }
  691             if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
  692                 /* Every part must end with \r\n, if there is another part.
  693                  * The end of the request has an extra -- */
  694                 if (((size_t)buf_fill != (size_t)(bl + 6))
  695                     || (strncmp(buf + bl + 2, "--\r\n", 4))) {
  696                     /* Malformed request */
  697                     mg_free(boundary);
  698                     return -1;
  699                 }
  700                 /* End of the request */
  701                 break;
  702             }
  703 
  704             /* Next, we need to get the part header: Read until \r\n\r\n */
  705             hbuf = buf + bl + 4;
  706             hend = strstr(hbuf, "\r\n\r\n");
  707             if (!hend) {
  708                 /* Malformed request */
  709                 mg_free(boundary);
  710                 return -1;
  711             }
  712 
  713             part_header.num_headers =
  714                 parse_http_headers(&hbuf, part_header.http_headers);
  715             if ((hend + 2) != hbuf) {
  716                 /* Malformed request */
  717                 mg_free(boundary);
  718                 return -1;
  719             }
  720 
  721             /* Skip \r\n\r\n */
  722             hend += 4;
  723 
  724             /* According to the RFC, every part has to have a header field like:
  725              * Content-Disposition: form-data; name="..." */
  726             content_disp = get_header(part_header.http_headers,
  727                                       part_header.num_headers,
  728                                       "Content-Disposition");
  729             if (!content_disp) {
  730                 /* Malformed request */
  731                 mg_free(boundary);
  732                 return -1;
  733             }
  734 
  735             /* Get the mandatory name="..." part of the Content-Disposition
  736              * header. */
  737             nbeg = strstr(content_disp, "name=\"");
  738             while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
  739                 /* It could be somethingname= instead of name= */
  740                 nbeg = strstr(nbeg + 1, "name=\"");
  741             }
  742 
  743             /* This line is not required, but otherwise some compilers
  744              * generate spurious warnings. */
  745             nend = nbeg;
  746             /* And others complain, the result is unused. */
  747             (void)nend;
  748 
  749             /* If name=" is found, search for the closing " */
  750             if (nbeg) {
  751                 nbeg += 6;
  752                 nend = strchr(nbeg, '\"');
  753                 if (!nend) {
  754                     /* Malformed request */
  755                     mg_free(boundary);
  756                     return -1;
  757                 }
  758             } else {
  759                 /* name= without quotes is also allowed */
  760                 nbeg = strstr(content_disp, "name=");
  761                 while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
  762                     /* It could be somethingname= instead of name= */
  763                     nbeg = strstr(nbeg + 1, "name=");
  764                 }
  765                 if (!nbeg) {
  766                     /* Malformed request */
  767                     mg_free(boundary);
  768                     return -1;
  769                 }
  770                 nbeg += 5;
  771 
  772                 /* RFC 2616 Sec. 2.2 defines a list of allowed
  773                  * separators, but many of them make no sense
  774                  * here, e.g. various brackets or slashes.
  775                  * If they are used, probably someone is
  776                  * trying to attack with curious hand made
  777                  * requests. Only ; , space and tab seem to be
  778                  * reasonable here. Ignore everything else. */
  779                 nend = nbeg + strcspn(nbeg, ",; \t");
  780             }
  781 
  782             /* Get the optional filename="..." part of the Content-Disposition
  783              * header. */
  784             fbeg = strstr(content_disp, "filename=\"");
  785             while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
  786                 /* It could be somethingfilename= instead of filename= */
  787                 fbeg = strstr(fbeg + 1, "filename=\"");
  788             }
  789 
  790             /* This line is not required, but otherwise some compilers
  791              * generate spurious warnings. */
  792             fend = fbeg;
  793 
  794             /* If filename=" is found, search for the closing " */
  795             if (fbeg) {
  796                 fbeg += 10;
  797                 fend = strchr(fbeg, '\"');
  798 
  799                 if (!fend) {
  800                     /* Malformed request (the filename field is optional, but if
  801                      * it exists, it needs to be terminated correctly). */
  802                     mg_free(boundary);
  803                     return -1;
  804                 }
  805 
  806                 /* TODO: check Content-Type */
  807                 /* Content-Type: application/octet-stream */
  808             }
  809             if (!fbeg) {
  810                 /* Try the same without quotes */
  811                 fbeg = strstr(content_disp, "filename=");
  812                 while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
  813                     /* It could be somethingfilename= instead of filename= */
  814                     fbeg = strstr(fbeg + 1, "filename=");
  815                 }
  816                 if (fbeg) {
  817                     fbeg += 9;
  818                     fend = fbeg + strcspn(fbeg, ",; \t");
  819                 }
  820             }
  821 
  822             if (!fbeg || !fend) {
  823                 fbeg = NULL;
  824                 fend = NULL;
  825                 fnlen = 0;
  826             } else {
  827                 fnlen = (size_t)(fend - fbeg);
  828             }
  829 
  830             /* In theory, it could be possible that someone crafts
  831              * a request like name=filename=xyz. Check if name and
  832              * filename do not overlap. */
  833             if (!(((ptrdiff_t)fbeg > (ptrdiff_t)nend)
  834                   || ((ptrdiff_t)nbeg > (ptrdiff_t)fend))) {
  835                 mg_free(boundary);
  836                 return -1;
  837             }
  838 
  839             /* Call callback for new field */
  840             memset(path, 0, sizeof(path));
  841             field_count++;
  842             field_storage = url_encoded_field_found(conn,
  843                                                     nbeg,
  844                                                     (size_t)(nend - nbeg),
  845                                                     ((fnlen > 0) ? fbeg : NULL),
  846                                                     fnlen,
  847                                                     path,
  848                                                     sizeof(path) - 1,
  849                                                     fdh);
  850 
  851             /* If the boundary is already in the buffer, get the address,
  852              * otherwise next will be NULL. */
  853             next = search_boundary(hbuf,
  854                                    (size_t)((buf - hbuf) + buf_fill),
  855                                    boundary,
  856                                    bl);
  857 
  858             if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
  859                 /* Store the content to a file */
  860                 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
  861                     fstore.access.fp = NULL;
  862                 }
  863                 file_size = 0;
  864 
  865                 if (!fstore.access.fp) {
  866                     mg_cry_internal(conn,
  867                                     "%s: Cannot create file %s",
  868                                     __func__,
  869                                     path);
  870                 }
  871             }
  872 
  873             get_block = 0;
  874             while (!next) {
  875                 /* Set "towrite" to the number of bytes available
  876                  * in the buffer */
  877                 towrite = (size_t)(buf - hend + buf_fill);
  878                 /* Subtract the boundary length, to deal with
  879                  * cases the boundary is only partially stored
  880                  * in the buffer. */
  881                 towrite -= bl + 4;
  882 
  883                 if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
  884                     r = unencoded_field_get(conn,
  885                                             ((get_block > 0) ? NULL : nbeg),
  886                                             ((get_block > 0)
  887                                                  ? 0
  888                                                  : (size_t)(nend - nbeg)),
  889                                             hend,
  890                                             towrite,
  891                                             fdh);
  892                     get_block++;
  893                     if (r == MG_FORM_FIELD_HANDLE_ABORT) {
  894                         /* Stop request handling */
  895                         break;
  896                     }
  897                     if (r == MG_FORM_FIELD_HANDLE_NEXT) {
  898                         /* Skip to next field */
  899                         field_storage = MG_FORM_FIELD_STORAGE_SKIP;
  900                     }
  901                 }
  902 
  903                 if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
  904                     if (fstore.access.fp) {
  905 
  906                         /* Store the content of the buffer. */
  907                         n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
  908                         if ((n != towrite) || (ferror(fstore.access.fp))) {
  909                             mg_cry_internal(conn,
  910                                             "%s: Cannot write file %s",
  911                                             __func__,
  912                                             path);
  913                             mg_fclose(&fstore.access);
  914                             remove_bad_file(conn, path);
  915                         }
  916                         file_size += (int64_t)n;
  917                     }
  918                 }
  919 
  920                 memmove(buf, hend + towrite, bl + 4);
  921                 buf_fill = (int)(bl + 4);
  922                 hend = buf;
  923 
  924                 /* Read new data */
  925                 r = mg_read(conn,
  926                             buf + (size_t)buf_fill,
  927                             sizeof(buf) - 1 - (size_t)buf_fill);
  928                 if (r < 0) {
  929                     /* read error */
  930                     if (fstore.access.fp) {
  931                         mg_fclose(&fstore.access);
  932                         remove_bad_file(conn, path);
  933                     }
  934                     mg_free(boundary);
  935                     return -1;
  936                 }
  937                 buf_fill += r;
  938                 buf[buf_fill] = 0;
  939                 /* buf_fill is at least 8 here */
  940 
  941                 /* Find boundary */
  942                 next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
  943             }
  944 
  945             towrite = (size_t)(next - hend);
  946 
  947             if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
  948                 /* Call callback */
  949                 r = unencoded_field_get(conn,
  950                                         ((get_block > 0) ? NULL : nbeg),
  951                                         ((get_block > 0)
  952                                              ? 0
  953                                              : (size_t)(nend - nbeg)),
  954                                         hend,
  955                                         towrite,
  956                                         fdh);
  957                 if (r == MG_FORM_FIELD_HANDLE_ABORT) {
  958                     /* Stop request handling */
  959                     break;
  960                 }
  961                 if (r == MG_FORM_FIELD_HANDLE_NEXT) {
  962                     /* Skip to next field */
  963                     field_storage = MG_FORM_FIELD_STORAGE_SKIP;
  964                 }
  965             }
  966 
  967             if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
  968 
  969                 if (fstore.access.fp) {
  970                     n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
  971                     if ((n != towrite) || (ferror(fstore.access.fp))) {
  972                         mg_cry_internal(conn,
  973                                         "%s: Cannot write file %s",
  974                                         __func__,
  975                                         path);
  976                         mg_fclose(&fstore.access);
  977                         remove_bad_file(conn, path);
  978                     } else {
  979                         file_size += (int64_t)n;
  980                         r = mg_fclose(&fstore.access);
  981                         if (r == 0) {
  982                             /* stored successfully */
  983                             r = field_stored(conn, path, file_size, fdh);
  984                             if (r == MG_FORM_FIELD_HANDLE_ABORT) {
  985                                 /* Stop request handling */
  986                                 break;
  987                             }
  988                         } else {
  989                             mg_cry_internal(conn,
  990                                             "%s: Error saving file %s",
  991                                             __func__,
  992                                             path);
  993                             remove_bad_file(conn, path);
  994                         }
  995                     }
  996                     fstore.access.fp = NULL;
  997                 }
  998             }
  999 
 1000             if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
 1001                 == MG_FORM_FIELD_STORAGE_ABORT) {
 1002                 /* Stop parsing the request */
 1003                 break;
 1004             }
 1005 
 1006             /* Remove from the buffer */
 1007             used = next - buf + 2;
 1008             memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
 1009             buf_fill -= (int)used;
 1010         }
 1011 
 1012         /* All parts handled */
 1013         mg_free(boundary);
 1014         return field_count;
 1015     }
 1016 
 1017     /* Unknown Content-Type */
 1018     return -1;
 1019 }
 1020 
 1021 
 1022 /* End of handle_form.inl */