Coverage Report

Created: 2025-07-09 06:52

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