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