Coverage Report

Created: 2025-12-31 06:52

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