1/* Copyright (c) 2016 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 23static int 24url_encoded_field_found(const struct mg_connection *conn, 25 const char *key, 26 size_t key_len, 27 const char *filename, 28 size_t filename_len, 29 char *path, 30 size_t path_len, 31 struct mg_form_data_handler *fdh) 32{ 33 char key_dec[1024]; 34 char filename_dec[1024]; 35 int key_dec_len; 36 int filename_dec_len; 37 int ret; 38 39 key_dec_len = 40 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); 41 42 if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) { 43 return FORM_FIELD_STORAGE_SKIP; 44 } 45 46 if (filename) { 47 filename_dec_len = mg_url_decode(filename, 48 (int)filename_len, 49 filename_dec, 50 (int)sizeof(filename_dec), 51 1); 52 53 if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec)) 54 || (filename_dec_len < 0)) { 55 /* Log error message and skip this field. */ 56 mg_cry(conn, "%s: Cannot decode filename", __func__); 57 return FORM_FIELD_STORAGE_SKIP; 58 } 59 } else { 60 filename_dec[0] = 0; 61 } 62 63 ret = 64 fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data); 65 66 if ((ret & 0xF) == FORM_FIELD_STORAGE_GET) { 67 if (fdh->field_get == NULL) { 68 mg_cry(conn, "%s: Function \"Get\" not available", __func__); 69 return FORM_FIELD_STORAGE_SKIP; 70 } 71 } 72 if ((ret & 0xF) == FORM_FIELD_STORAGE_STORE) { 73 if (fdh->field_store == NULL) { 74 mg_cry(conn, "%s: Function \"Store\" not available", __func__); 75 return FORM_FIELD_STORAGE_SKIP; 76 } 77 } 78 79 return ret; 80} 81 82 83static int 84url_encoded_field_get(const struct mg_connection *conn, 85 const char *key, 86 size_t key_len, 87 const char *value, 88 size_t value_len, 89 struct mg_form_data_handler *fdh) 90{ 91 char key_dec[1024]; 92 93 char *value_dec = mg_malloc(value_len + 1); 94 int value_dec_len, ret; 95 96 if (!value_dec) { 97 /* Log error message and stop parsing the form data. */ 98 mg_cry(conn, 99 "%s: Not enough memory (required: %lu)", 100 __func__, 101 (unsigned long)(value_len + 1)); 102 return FORM_FIELD_STORAGE_ABORT; 103 } 104 105 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); 106 107 value_dec_len = 108 mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1); 109 110 ret = fdh->field_get(key_dec, 111 value_dec, 112 (size_t)value_dec_len, 113 fdh->user_data); 114 115 mg_free(value_dec); 116 117 return ret; 118} 119 120 121static int 122unencoded_field_get(const struct mg_connection *conn, 123 const char *key, 124 size_t key_len, 125 const char *value, 126 size_t value_len, 127 struct mg_form_data_handler *fdh) 128{ 129 char key_dec[1024]; 130 (void)conn; 131 132 mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1); 133 134 return fdh->field_get(key_dec, value, value_len, fdh->user_data); 135} 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 151 152static const char * 153search_boundary(const char *buf, 154 size_t buf_len, 155 const char *boundary, 156 size_t boundary_len) 157{ 158 /* We must do a binary search here, not a string search, since the buffer 159 * may contain '\x00' bytes, if binary data is transferred. */ 160 int clen = (int)buf_len - (int)boundary_len - 4; 161 int i; 162 163 for (i = 0; i <= clen; i++) { 164 if (!memcmp(buf + i, "\r\n--", 4)) { 165 if (!memcmp(buf + i + 4, boundary, boundary_len)) { 166 return buf + i; 167 } 168 } 169 } 170 return NULL; 171} 172 173 174int 175mg_handle_form_request(struct mg_connection *conn, 176 struct mg_form_data_handler *fdh) 177{ 178 const char *content_type; 179 char path[512]; 180 char buf[1024]; 181 int field_storage; 182 int buf_fill = 0; 183 int r; 184 int field_count = 0; 185 struct mg_file fstore = STRUCT_FILE_INITIALIZER; 186 int64_t file_size = 0; /* init here, to a avoid a false positive 187 "uninitialized variable used" warning */ 188 189 int has_body_data = 190 (conn->request_info.content_length > 0) || (conn->is_chunked); 191 192 /* There are three ways to encode data from a HTML form: 193 * 1) method: GET (default) 194 * The form data is in the HTTP query string. 195 * 2) method: POST, enctype: "application/x-www-form-urlencoded" 196 * The form data is in the request body. 197 * The body is url encoded (the default encoding for POST). 198 * 3) method: POST, enctype: "multipart/form-data". 199 * The form data is in the request body of a multipart message. 200 * This is the typical way to handle file upload from a form. 201 */ 202 203 if (!has_body_data) { 204 const char *data; 205 206 if (strcmp(conn->request_info.request_method, "GET")) { 207 /* No body data, but not a GET request. 208 * This is not a valid form request. */ 209 return -1; 210 } 211 212 /* GET request: form data is in the query string. */ 213 /* The entire data has already been loaded, so there is no nead to 214 * call mg_read. We just need to split the query string into key-value 215 * pairs. */ 216 data = conn->request_info.query_string; 217 if (!data) { 218 /* No query string. */ 219 return -1; 220 } 221 222 /* Split data in a=1&b=xy&c=3&c=4 ... */ 223 while (*data) { 224 const char *val = strchr(data, '='); 225 const char *next; 226 ptrdiff_t keylen, vallen; 227 228 if (!val) { 229 break; 230 } 231 keylen = val - data; 232 233 /* In every "field_found" callback we ask what to do with the 234 * data ("field_storage"). This could be: 235 * FORM_FIELD_STORAGE_SKIP (0) ... ignore the value of this field 236 * FORM_FIELD_STORAGE_GET (1) ... read the data and call the get 237 * callback function 238 * FORM_FIELD_STORAGE_STORE (2) ... store the data in a file 239 * FORM_FIELD_STORAGE_READ (3) ... let the user read the data 240 * (for parsing long data on the fly) 241 * (currently not implemented) 242 * FORM_FIELD_STORAGE_ABORT (flag) ... stop parsing 243 */ 244 memset(path, 0, sizeof(path)); 245 field_count++; 246 field_storage = url_encoded_field_found(conn, 247 data, 248 (size_t)keylen, 249 NULL, 250 0, 251 path, 252 sizeof(path) - 1, 253 fdh); 254 255 val++; 256 next = strchr(val, '&'); 257 if (next) { 258 vallen = next - val; 259 next++; 260 } else { 261 vallen = (ptrdiff_t)strlen(val); 262 next = val + vallen; 263 } 264 265 if (field_storage == FORM_FIELD_STORAGE_GET) { 266 /* Call callback */ 267 url_encoded_field_get( 268 conn, data, (size_t)keylen, val, (size_t)vallen, fdh); 269 } 270 if (field_storage == FORM_FIELD_STORAGE_STORE) { 271 /* Store the content to a file */ 272 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) { 273 fstore.access.fp = NULL; 274 } 275 file_size = 0; 276 if (fstore.access.fp != NULL) { 277 size_t n = (size_t) 278 fwrite(val, 1, (size_t)vallen, fstore.access.fp); 279 if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) { 280 mg_cry(conn, 281 "%s: Cannot write file %s", 282 __func__, 283 path); 284 (void)mg_fclose(&fstore.access); 285 remove_bad_file(conn, path); 286 } 287 file_size += (int64_t)n; 288 289 if (fstore.access.fp) { 290 r = mg_fclose(&fstore.access); 291 if (r == 0) { 292 /* stored successfully */ 293 field_stored(conn, path, file_size, fdh); 294 } else { 295 mg_cry(conn, 296 "%s: Error saving file %s", 297 __func__, 298 path); 299 remove_bad_file(conn, path); 300 } 301 fstore.access.fp = NULL; 302 } 303 304 } else { 305 mg_cry(conn, "%s: Cannot create file %s", __func__, path); 306 } 307 } 308 309 /* if (field_storage == FORM_FIELD_STORAGE_READ) { */ 310 /* The idea of "field_storage=read" is to let the API user read 311 * data chunk by chunk and to some data processing on the fly. 312 * This should avoid the need to store data in the server: 313 * It should neither be stored in memory, like 314 * "field_storage=get" does, nor in a file like 315 * "field_storage=store". 316 * However, for a "GET" request this does not make any much 317 * sense, since the data is already stored in memory, as it is 318 * part of the query string. 319 */ 320 /* } */ 321 322 if ((field_storage & FORM_FIELD_STORAGE_ABORT) 323 == FORM_FIELD_STORAGE_ABORT) { 324 /* Stop parsing the request */ 325 break; 326 } 327 328 /* Proceed to next entry */ 329 data = next; 330 } 331 332 return field_count; 333 } 334 335 content_type = mg_get_header(conn, "Content-Type"); 336 337 if (!content_type 338 || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED") 339 || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) { 340 /* The form data is in the request body data, encoded in key/value 341 * pairs. */ 342 int all_data_read = 0; 343 344 /* Read body data and split it in keys and values. 345 * The encoding is like in the "GET" case above: a=1&b&c=3&c=4. 346 * Here we use "POST", and read the data from the request body. 347 * The data read on the fly, so it is not required to buffer the 348 * entire request in memory before processing it. */ 349 for (;;) { 350 const char *val; 351 const char *next; 352 ptrdiff_t keylen, vallen; 353 ptrdiff_t used; 354 int end_of_key_value_pair_found = 0; 355 int get_block; 356 357 if ((size_t)buf_fill < (sizeof(buf) - 1)) { 358 359 size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; 360 r = mg_read(conn, buf + (size_t)buf_fill, to_read); 361 if (r < 0) { 362 /* read error */ 363 return -1; 364 } 365 if (r != (int)to_read) { 366 /* TODO: Create a function to get "all_data_read" from 367 * the conn object. All data is read if the Content-Length 368 * has been reached, or if chunked encoding is used and 369 * the end marker has been read, or if the connection has 370 * been closed. */ 371 all_data_read = 1; 372 } 373 buf_fill += r; 374 buf[buf_fill] = 0; 375 if (buf_fill < 1) { 376 break; 377 } 378 } 379 380 val = strchr(buf, '='); 381 382 if (!val) { 383 break; 384 } 385 keylen = val - buf; 386 val++; 387 388 /* Call callback */ 389 memset(path, 0, sizeof(path)); 390 field_count++; 391 field_storage = url_encoded_field_found(conn, 392 buf, 393 (size_t)keylen, 394 NULL, 395 0, 396 path, 397 sizeof(path) - 1, 398 fdh); 399 400 if ((field_storage & FORM_FIELD_STORAGE_ABORT) 401 == FORM_FIELD_STORAGE_ABORT) { 402 /* Stop parsing the request */ 403 break; 404 } 405 406 if (field_storage == FORM_FIELD_STORAGE_STORE) { 407 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) { 408 fstore.access.fp = NULL; 409 } 410 file_size = 0; 411 if (!fstore.access.fp) { 412 mg_cry(conn, "%s: Cannot create file %s", __func__, path); 413 } 414 } 415 416 get_block = 0; 417 /* Loop to read values larger than sizeof(buf)-keylen-2 */ 418 do { 419 next = strchr(val, '&'); 420 if (next) { 421 vallen = next - val; 422 next++; 423 end_of_key_value_pair_found = 1; 424 } else { 425 vallen = (ptrdiff_t)strlen(val); 426 next = val + vallen; 427 } 428 429 if (field_storage == FORM_FIELD_STORAGE_GET) { 430#if 0 431 if (!end_of_key_value_pair_found && !all_data_read) { 432 /* This callback will deliver partial contents */ 433 } 434#else 435 (void)all_data_read; /* avoid warning */ 436#endif 437 438 /* Call callback */ 439 url_encoded_field_get(conn, 440 ((get_block > 0) ? NULL : buf), 441 ((get_block > 0) ? 0 442 : (size_t)keylen), 443 val, 444 (size_t)vallen, 445 fdh); 446 get_block++; 447 } 448 if (fstore.access.fp) { 449 size_t n = (size_t) 450 fwrite(val, 1, (size_t)vallen, fstore.access.fp); 451 if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) { 452 mg_cry(conn, 453 "%s: Cannot write file %s", 454 __func__, 455 path); 456 mg_fclose(&fstore.access); 457 remove_bad_file(conn, path); 458 } 459 file_size += (int64_t)n; 460 } 461 462 if (!end_of_key_value_pair_found) { 463 used = next - buf; 464 memmove(buf, 465 buf + (size_t)used, 466 sizeof(buf) - (size_t)used); 467 buf_fill -= (int)used; 468 if ((size_t)buf_fill < (sizeof(buf) - 1)) { 469 470 size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill; 471 r = mg_read(conn, buf + (size_t)buf_fill, to_read); 472 if (r < 0) { 473 /* read error */ 474 return -1; 475 } 476 if (r != (int)to_read) { 477 /* TODO: Create a function to get "all_data_read" 478 * from the conn object. All data is read if the 479 * Content-Length has been reached, or if chunked 480 * encoding is used and the end marker has been 481 * read, or if the connection has been closed. */ 482 all_data_read = 1; 483 } 484 buf_fill += r; 485 buf[buf_fill] = 0; 486 if (buf_fill < 1) { 487 break; 488 } 489 val = buf; 490 } 491 } 492 493 } while (!end_of_key_value_pair_found); 494 495 if (fstore.access.fp) { 496 r = mg_fclose(&fstore.access); 497 if (r == 0) { 498 /* stored successfully */ 499 field_stored(conn, path, file_size, fdh); 500 } else { 501 mg_cry(conn, "%s: Error saving file %s", __func__, path); 502 remove_bad_file(conn, path); 503 } 504 fstore.access.fp = NULL; 505 } 506 507 /* Proceed to next entry */ 508 used = next - buf; 509 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); 510 buf_fill -= (int)used; 511 } 512 513 return field_count; 514 } 515 516 if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) { 517 /* The form data is in the request body data, encoded as multipart 518 * content (see https://www.ietf.org/rfc/rfc1867.txt, 519 * https://www.ietf.org/rfc/rfc2388.txt). */ 520 const char *boundary; 521 size_t bl; 522 ptrdiff_t used; 523 struct mg_request_info part_header; 524 char *hbuf, *hend, *fbeg, *fend, *nbeg, *nend; 525 const char *content_disp; 526 const char *next; 527 528 memset(&part_header, 0, sizeof(part_header)); 529 530 /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */ 531 bl = 20; 532 while (content_type[bl] == ' ') { 533 bl++; 534 } 535 536 /* There has to be a BOUNDARY definition in the Content-Type header */ 537 if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) { 538 /* Malformed request */ 539 return -1; 540 } 541 542 boundary = content_type + bl + 9; 543 bl = strlen(boundary); 544 545 if (bl + 800 > sizeof(buf)) { 546 /* Sanity check: The algorithm can not work if bl >= sizeof(buf), 547 * and it will not work effectively, if the buf is only a few byte 548 * larger than bl, or it buf can not hold the multipart header 549 * plus the boundary. 550 * Check some reasonable number here, that should be fulfilled by 551 * any reasonable request from every browser. If it is not 552 * fulfilled, it might be a hand-made request, intended to 553 * interfere with the algorithm. */ 554 return -1; 555 } 556 557 for (;;) { 558 size_t towrite, n; 559 int get_block; 560 561 r = mg_read(conn, 562 buf + (size_t)buf_fill, 563 sizeof(buf) - 1 - (size_t)buf_fill); 564 if (r < 0) { 565 /* read error */ 566 return -1; 567 } 568 buf_fill += r; 569 buf[buf_fill] = 0; 570 if (buf_fill < 1) { 571 /* No data */ 572 return -1; 573 } 574 575 if (buf[0] != '-' || buf[1] != '-') { 576 /* Malformed request */ 577 return -1; 578 } 579 if (strncmp(buf + 2, boundary, bl)) { 580 /* Malformed request */ 581 return -1; 582 } 583 if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') { 584 /* Every part must end with \r\n, if there is another part. 585 * The end of the request has an extra -- */ 586 if (((size_t)buf_fill != (size_t)(bl + 6)) 587 || (strncmp(buf + bl + 2, "--\r\n", 4))) { 588 /* Malformed request */ 589 return -1; 590 } 591 /* End of the request */ 592 break; 593 } 594 595 /* Next, we need to get the part header: Read until \r\n\r\n */ 596 hbuf = buf + bl + 4; 597 hend = strstr(hbuf, "\r\n\r\n"); 598 if (!hend) { 599 /* Malformed request */ 600 return -1; 601 } 602 603 parse_http_headers(&hbuf, &part_header); 604 if ((hend + 2) != hbuf) { 605 /* Malformed request */ 606 return -1; 607 } 608 609 /* Skip \r\n\r\n */ 610 hend += 4; 611 612 /* According to the RFC, every part has to have a header field like: 613 * Content-Disposition: form-data; name="..." */ 614 content_disp = get_header(&part_header, "Content-Disposition"); 615 if (!content_disp) { 616 /* Malformed request */ 617 return -1; 618 } 619 620 /* Get the mandatory name="..." part of the Content-Disposition 621 * header. */ 622 nbeg = strstr(content_disp, "name=\""); 623 if (!nbeg) { 624 /* Malformed request */ 625 return -1; 626 } 627 nbeg += 6; 628 nend = strchr(nbeg, '\"'); 629 if (!nend) { 630 /* Malformed request */ 631 return -1; 632 } 633 634 /* Get the optional filename="..." part of the Content-Disposition 635 * header. */ 636 fbeg = strstr(content_disp, "filename=\""); 637 if (fbeg) { 638 fbeg += 10; 639 fend = strchr(fbeg, '\"'); 640 if (!fend) { 641 /* Malformed request (the filename field is optional, but if 642 * it exists, it needs to be terminated correctly). */ 643 return -1; 644 } 645 646 /* TODO: check Content-Type */ 647 /* Content-Type: application/octet-stream */ 648 649 } else { 650 fend = fbeg; 651 } 652 653 memset(path, 0, sizeof(path)); 654 field_count++; 655 field_storage = url_encoded_field_found(conn, 656 nbeg, 657 (size_t)(nend - nbeg), 658 fbeg, 659 (size_t)(fend - fbeg), 660 path, 661 sizeof(path) - 1, 662 fdh); 663 664 /* If the boundary is already in the buffer, get the address, 665 * otherwise next will be NULL. */ 666 next = search_boundary(hbuf, 667 (size_t)((buf - hbuf) + buf_fill), 668 boundary, 669 bl); 670 671 if (field_storage == FORM_FIELD_STORAGE_STORE) { 672 /* Store the content to a file */ 673 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) { 674 fstore.access.fp = NULL; 675 } 676 file_size = 0; 677 678 if (!fstore.access.fp) { 679 mg_cry(conn, "%s: Cannot create file %s", __func__, path); 680 } 681 } 682 683 get_block = 0; 684 while (!next) { 685 /* Set "towrite" to the number of bytes available 686 * in the buffer */ 687 towrite = (size_t)(buf - hend + buf_fill); 688 /* Subtract the boundary length, to deal with 689 * cases the boundary is only partially stored 690 * in the buffer. */ 691 towrite -= bl + 4; 692 693 if (field_storage == FORM_FIELD_STORAGE_GET) { 694 unencoded_field_get(conn, 695 ((get_block > 0) ? NULL : nbeg), 696 ((get_block > 0) 697 ? 0 698 : (size_t)(nend - nbeg)), 699 hend, 700 towrite, 701 fdh); 702 get_block++; 703 } 704 705 if (field_storage == FORM_FIELD_STORAGE_STORE) { 706 if (fstore.access.fp) { 707 708 /* Store the content of the buffer. */ 709 n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp); 710 if ((n != towrite) || (ferror(fstore.access.fp))) { 711 mg_cry(conn, 712 "%s: Cannot write file %s", 713 __func__, 714 path); 715 mg_fclose(&fstore.access); 716 remove_bad_file(conn, path); 717 } 718 file_size += (int64_t)n; 719 } 720 } 721 722 memmove(buf, hend + towrite, bl + 4); 723 buf_fill = (int)(bl + 4); 724 hend = buf; 725 726 /* Read new data */ 727 r = mg_read(conn, 728 buf + (size_t)buf_fill, 729 sizeof(buf) - 1 - (size_t)buf_fill); 730 if (r < 0) { 731 /* read error */ 732 return -1; 733 } 734 buf_fill += r; 735 buf[buf_fill] = 0; 736 if (buf_fill < 1) { 737 /* No data */ 738 return -1; 739 } 740 741 /* Find boundary */ 742 next = search_boundary(buf, (size_t)buf_fill, boundary, bl); 743 } 744 745 towrite = (size_t)(next - hend); 746 747 if (field_storage == FORM_FIELD_STORAGE_GET) { 748 /* Call callback */ 749 unencoded_field_get(conn, 750 ((get_block > 0) ? NULL : nbeg), 751 ((get_block > 0) ? 0 752 : (size_t)(nend - nbeg)), 753 hend, 754 towrite, 755 fdh); 756 } 757 758 if (field_storage == FORM_FIELD_STORAGE_STORE) { 759 760 if (fstore.access.fp) { 761 n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp); 762 if ((n != towrite) || (ferror(fstore.access.fp))) { 763 mg_cry(conn, 764 "%s: Cannot write file %s", 765 __func__, 766 path); 767 mg_fclose(&fstore.access); 768 remove_bad_file(conn, path); 769 } 770 file_size += (int64_t)n; 771 } 772 } 773 774 if (field_storage == FORM_FIELD_STORAGE_STORE) { 775 776 if (fstore.access.fp) { 777 r = mg_fclose(&fstore.access); 778 if (r == 0) { 779 /* stored successfully */ 780 field_stored(conn, path, file_size, fdh); 781 } else { 782 mg_cry(conn, 783 "%s: Error saving file %s", 784 __func__, 785 path); 786 remove_bad_file(conn, path); 787 } 788 fstore.access.fp = NULL; 789 } 790 } 791 792 if ((field_storage & FORM_FIELD_STORAGE_ABORT) 793 == FORM_FIELD_STORAGE_ABORT) { 794 /* Stop parsing the request */ 795 break; 796 } 797 798 /* Remove from the buffer */ 799 used = next - buf + 2; 800 memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used); 801 buf_fill -= (int)used; 802 } 803 804 /* All parts handled */ 805 return field_count; 806 } 807 808 /* Unknown Content-Type */ 809 return -1; 810} 811