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