1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: Rasmus Lerdorf <rasmus@php.net> | 16 | Jani Taskinen <jani@php.net> | 17 +----------------------------------------------------------------------+ 18 */ 19 20/* $Id$ */ 21 22/* 23 * This product includes software developed by the Apache Group 24 * for use in the Apache HTTP server project (http://www.apache.org/). 25 * 26 */ 27 28#include <stdio.h> 29#include "php.h" 30#include "php_open_temporary_file.h" 31#include "zend_globals.h" 32#include "php_globals.h" 33#include "php_variables.h" 34#include "rfc1867.h" 35#include "ext/standard/php_string.h" 36 37#define DEBUG_FILE_UPLOAD ZEND_DEBUG 38 39static int dummy_encoding_translation(TSRMLS_D) 40{ 41 return 0; 42} 43 44static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop TSRMLS_DC); 45static char *php_ap_getword_conf(const zend_encoding *encoding, char *str TSRMLS_DC); 46 47static php_rfc1867_encoding_translation_t php_rfc1867_encoding_translation = dummy_encoding_translation; 48static php_rfc1867_get_detect_order_t php_rfc1867_get_detect_order = NULL; 49static php_rfc1867_set_input_encoding_t php_rfc1867_set_input_encoding = NULL; 50static php_rfc1867_getword_t php_rfc1867_getword = php_ap_getword; 51static php_rfc1867_getword_conf_t php_rfc1867_getword_conf = php_ap_getword_conf; 52static php_rfc1867_basename_t php_rfc1867_basename = NULL; 53 54PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra TSRMLS_DC) = NULL; 55 56static void safe_php_register_variable(char *var, char *strval, int val_len, zval *track_vars_array, zend_bool override_protection TSRMLS_DC); 57 58/* The longest property name we use in an uploaded file array */ 59#define MAX_SIZE_OF_INDEX sizeof("[tmp_name]") 60 61/* The longest anonymous name */ 62#define MAX_SIZE_ANONNAME 33 63 64/* Errors */ 65#define UPLOAD_ERROR_OK 0 /* File upload successful */ 66#define UPLOAD_ERROR_A 1 /* Uploaded file exceeded upload_max_filesize */ 67#define UPLOAD_ERROR_B 2 /* Uploaded file exceeded MAX_FILE_SIZE */ 68#define UPLOAD_ERROR_C 3 /* Partially uploaded */ 69#define UPLOAD_ERROR_D 4 /* No file uploaded */ 70#define UPLOAD_ERROR_E 6 /* Missing /tmp or similar directory */ 71#define UPLOAD_ERROR_F 7 /* Failed to write file to disk */ 72#define UPLOAD_ERROR_X 8 /* File upload stopped by extension */ 73 74void php_rfc1867_register_constants(TSRMLS_D) /* {{{ */ 75{ 76 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_OK", UPLOAD_ERROR_OK, CONST_CS | CONST_PERSISTENT); 77 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_INI_SIZE", UPLOAD_ERROR_A, CONST_CS | CONST_PERSISTENT); 78 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_FORM_SIZE", UPLOAD_ERROR_B, CONST_CS | CONST_PERSISTENT); 79 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_PARTIAL", UPLOAD_ERROR_C, CONST_CS | CONST_PERSISTENT); 80 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_FILE", UPLOAD_ERROR_D, CONST_CS | CONST_PERSISTENT); 81 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_TMP_DIR", UPLOAD_ERROR_E, CONST_CS | CONST_PERSISTENT); 82 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_CANT_WRITE", UPLOAD_ERROR_F, CONST_CS | CONST_PERSISTENT); 83 REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_EXTENSION", UPLOAD_ERROR_X, CONST_CS | CONST_PERSISTENT); 84} 85/* }}} */ 86 87static void normalize_protected_variable(char *varname TSRMLS_DC) /* {{{ */ 88{ 89 char *s = varname, *index = NULL, *indexend = NULL, *p; 90 91 /* overjump leading space */ 92 while (*s == ' ') { 93 s++; 94 } 95 96 /* and remove it */ 97 if (s != varname) { 98 memmove(varname, s, strlen(s)+1); 99 } 100 101 for (p = varname; *p && *p != '['; p++) { 102 switch(*p) { 103 case ' ': 104 case '.': 105 *p = '_'; 106 break; 107 } 108 } 109 110 /* find index */ 111 index = strchr(varname, '['); 112 if (index) { 113 index++; 114 s = index; 115 } else { 116 return; 117 } 118 119 /* done? */ 120 while (index) { 121 while (*index == ' ' || *index == '\r' || *index == '\n' || *index=='\t') { 122 index++; 123 } 124 indexend = strchr(index, ']'); 125 indexend = indexend ? indexend + 1 : index + strlen(index); 126 127 if (s != index) { 128 memmove(s, index, strlen(index)+1); 129 s += indexend-index; 130 } else { 131 s = indexend; 132 } 133 134 if (*s == '[') { 135 s++; 136 index = s; 137 } else { 138 index = NULL; 139 } 140 } 141 *s = '\0'; 142} 143/* }}} */ 144 145static void add_protected_variable(char *varname TSRMLS_DC) /* {{{ */ 146{ 147 int dummy = 1; 148 149 normalize_protected_variable(varname TSRMLS_CC); 150 zend_hash_add(&PG(rfc1867_protected_variables), varname, strlen(varname)+1, &dummy, sizeof(int), NULL); 151} 152/* }}} */ 153 154static zend_bool is_protected_variable(char *varname TSRMLS_DC) /* {{{ */ 155{ 156 normalize_protected_variable(varname TSRMLS_CC); 157 return zend_hash_exists(&PG(rfc1867_protected_variables), varname, strlen(varname)+1); 158} 159/* }}} */ 160 161static void safe_php_register_variable(char *var, char *strval, int val_len, zval *track_vars_array, zend_bool override_protection TSRMLS_DC) /* {{{ */ 162{ 163 if (override_protection || !is_protected_variable(var TSRMLS_CC)) { 164 php_register_variable_safe(var, strval, val_len, track_vars_array TSRMLS_CC); 165 } 166} 167/* }}} */ 168 169static void safe_php_register_variable_ex(char *var, zval *val, zval *track_vars_array, zend_bool override_protection TSRMLS_DC) /* {{{ */ 170{ 171 if (override_protection || !is_protected_variable(var TSRMLS_CC)) { 172 php_register_variable_ex(var, val, track_vars_array TSRMLS_CC); 173 } 174} 175/* }}} */ 176 177static void register_http_post_files_variable(char *strvar, char *val, zval *http_post_files, zend_bool override_protection TSRMLS_DC) /* {{{ */ 178{ 179 safe_php_register_variable(strvar, val, strlen(val), http_post_files, override_protection TSRMLS_CC); 180} 181/* }}} */ 182 183static void register_http_post_files_variable_ex(char *var, zval *val, zval *http_post_files, zend_bool override_protection TSRMLS_DC) /* {{{ */ 184{ 185 safe_php_register_variable_ex(var, val, http_post_files, override_protection TSRMLS_CC); 186} 187/* }}} */ 188 189static int unlink_filename(char **filename TSRMLS_DC) /* {{{ */ 190{ 191 VCWD_UNLINK(*filename); 192 return 0; 193} 194/* }}} */ 195 196void destroy_uploaded_files_hash(TSRMLS_D) /* {{{ */ 197{ 198 zend_hash_apply(SG(rfc1867_uploaded_files), (apply_func_t) unlink_filename TSRMLS_CC); 199 zend_hash_destroy(SG(rfc1867_uploaded_files)); 200 FREE_HASHTABLE(SG(rfc1867_uploaded_files)); 201} 202/* }}} */ 203 204/* {{{ Following code is based on apache_multipart_buffer.c from libapreq-0.33 package. */ 205 206#define FILLUNIT (1024 * 5) 207 208typedef struct { 209 210 /* read buffer */ 211 char *buffer; 212 char *buf_begin; 213 int bufsize; 214 int bytes_in_buffer; 215 216 /* boundary info */ 217 char *boundary; 218 char *boundary_next; 219 int boundary_next_len; 220 221 const zend_encoding *input_encoding; 222 const zend_encoding **detect_order; 223 size_t detect_order_size; 224} multipart_buffer; 225 226typedef struct { 227 char *key; 228 char *value; 229} mime_header_entry; 230 231/* 232 * Fill up the buffer with client data. 233 * Returns number of bytes added to buffer. 234 */ 235static int fill_buffer(multipart_buffer *self TSRMLS_DC) 236{ 237 int bytes_to_read, total_read = 0, actual_read = 0; 238 239 /* shift the existing data if necessary */ 240 if (self->bytes_in_buffer > 0 && self->buf_begin != self->buffer) { 241 memmove(self->buffer, self->buf_begin, self->bytes_in_buffer); 242 } 243 244 self->buf_begin = self->buffer; 245 246 /* calculate the free space in the buffer */ 247 bytes_to_read = self->bufsize - self->bytes_in_buffer; 248 249 /* read the required number of bytes */ 250 while (bytes_to_read > 0) { 251 252 char *buf = self->buffer + self->bytes_in_buffer; 253 254 actual_read = sapi_module.read_post(buf, bytes_to_read TSRMLS_CC); 255 256 /* update the buffer length */ 257 if (actual_read > 0) { 258 self->bytes_in_buffer += actual_read; 259 SG(read_post_bytes) += actual_read; 260 total_read += actual_read; 261 bytes_to_read -= actual_read; 262 } else { 263 break; 264 } 265 } 266 267 return total_read; 268} 269 270/* eof if we are out of bytes, or if we hit the final boundary */ 271static int multipart_buffer_eof(multipart_buffer *self TSRMLS_DC) 272{ 273 if ( (self->bytes_in_buffer == 0 && fill_buffer(self TSRMLS_CC) < 1) ) { 274 return 1; 275 } else { 276 return 0; 277 } 278} 279 280/* create new multipart_buffer structure */ 281static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len TSRMLS_DC) 282{ 283 multipart_buffer *self = (multipart_buffer *) ecalloc(1, sizeof(multipart_buffer)); 284 285 int minsize = boundary_len + 6; 286 if (minsize < FILLUNIT) minsize = FILLUNIT; 287 288 self->buffer = (char *) ecalloc(1, minsize + 1); 289 self->bufsize = minsize; 290 291 spprintf(&self->boundary, 0, "--%s", boundary); 292 293 self->boundary_next_len = spprintf(&self->boundary_next, 0, "\n--%s", boundary); 294 295 self->buf_begin = self->buffer; 296 self->bytes_in_buffer = 0; 297 298 if (php_rfc1867_encoding_translation(TSRMLS_C)) { 299 php_rfc1867_get_detect_order(&self->detect_order, &self->detect_order_size TSRMLS_CC); 300 } else { 301 self->detect_order = NULL; 302 self->detect_order_size = 0; 303 } 304 305 self->input_encoding = NULL; 306 307 return self; 308} 309 310/* 311 * Gets the next CRLF terminated line from the input buffer. 312 * If it doesn't find a CRLF, and the buffer isn't completely full, returns 313 * NULL; otherwise, returns the beginning of the null-terminated line, 314 * minus the CRLF. 315 * 316 * Note that we really just look for LF terminated lines. This works 317 * around a bug in internet explorer for the macintosh which sends mime 318 * boundaries that are only LF terminated when you use an image submit 319 * button in a multipart/form-data form. 320 */ 321static char *next_line(multipart_buffer *self) 322{ 323 /* look for LF in the data */ 324 char* line = self->buf_begin; 325 char* ptr = memchr(self->buf_begin, '\n', self->bytes_in_buffer); 326 327 if (ptr) { /* LF found */ 328 329 /* terminate the string, remove CRLF */ 330 if ((ptr - line) > 0 && *(ptr-1) == '\r') { 331 *(ptr-1) = 0; 332 } else { 333 *ptr = 0; 334 } 335 336 /* bump the pointer */ 337 self->buf_begin = ptr + 1; 338 self->bytes_in_buffer -= (self->buf_begin - line); 339 340 } else { /* no LF found */ 341 342 /* buffer isn't completely full, fail */ 343 if (self->bytes_in_buffer < self->bufsize) { 344 return NULL; 345 } 346 /* return entire buffer as a partial line */ 347 line[self->bufsize] = 0; 348 self->buf_begin = ptr; 349 self->bytes_in_buffer = 0; 350 } 351 352 return line; 353} 354 355/* Returns the next CRLF terminated line from the client */ 356static char *get_line(multipart_buffer *self TSRMLS_DC) 357{ 358 char* ptr = next_line(self); 359 360 if (!ptr) { 361 fill_buffer(self TSRMLS_CC); 362 ptr = next_line(self); 363 } 364 365 return ptr; 366} 367 368/* Free header entry */ 369static void php_free_hdr_entry(mime_header_entry *h) 370{ 371 if (h->key) { 372 efree(h->key); 373 } 374 if (h->value) { 375 efree(h->value); 376 } 377} 378 379/* finds a boundary */ 380static int find_boundary(multipart_buffer *self, char *boundary TSRMLS_DC) 381{ 382 char *line; 383 384 /* loop thru lines */ 385 while( (line = get_line(self TSRMLS_CC)) ) 386 { 387 /* finished if we found the boundary */ 388 if (!strcmp(line, boundary)) { 389 return 1; 390 } 391 } 392 393 /* didn't find the boundary */ 394 return 0; 395} 396 397/* parse headers */ 398static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header TSRMLS_DC) 399{ 400 char *line; 401 mime_header_entry prev_entry, entry; 402 int prev_len, cur_len; 403 404 /* didn't find boundary, abort */ 405 if (!find_boundary(self, self->boundary TSRMLS_CC)) { 406 return 0; 407 } 408 409 /* get lines of text, or CRLF_CRLF */ 410 411 while( (line = get_line(self TSRMLS_CC)) && line[0] != '\0' ) 412 { 413 /* add header to table */ 414 char *key = line; 415 char *value = NULL; 416 417 if (php_rfc1867_encoding_translation(TSRMLS_C)) { 418 self->input_encoding = zend_multibyte_encoding_detector(line, strlen(line), self->detect_order, self->detect_order_size TSRMLS_CC); 419 } 420 421 /* space in the beginning means same header */ 422 if (!isspace(line[0])) { 423 value = strchr(line, ':'); 424 } 425 426 if (value) { 427 *value = 0; 428 do { value++; } while(isspace(*value)); 429 430 entry.value = estrdup(value); 431 entry.key = estrdup(key); 432 433 } else if (zend_llist_count(header)) { /* If no ':' on the line, add to previous line */ 434 435 prev_len = strlen(prev_entry.value); 436 cur_len = strlen(line); 437 438 entry.value = emalloc(prev_len + cur_len + 1); 439 memcpy(entry.value, prev_entry.value, prev_len); 440 memcpy(entry.value + prev_len, line, cur_len); 441 entry.value[cur_len + prev_len] = '\0'; 442 443 entry.key = estrdup(prev_entry.key); 444 445 zend_llist_remove_tail(header); 446 } else { 447 continue; 448 } 449 450 zend_llist_add_element(header, &entry); 451 prev_entry = entry; 452 } 453 454 return 1; 455} 456 457static char *php_mime_get_hdr_value(zend_llist header, char *key) 458{ 459 mime_header_entry *entry; 460 461 if (key == NULL) { 462 return NULL; 463 } 464 465 entry = zend_llist_get_first(&header); 466 while (entry) { 467 if (!strcasecmp(entry->key, key)) { 468 return entry->value; 469 } 470 entry = zend_llist_get_next(&header); 471 } 472 473 return NULL; 474} 475 476static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop TSRMLS_DC) 477{ 478 char *pos = *line, quote; 479 char *res; 480 481 while (*pos && *pos != stop) { 482 if ((quote = *pos) == '"' || quote == '\'') { 483 ++pos; 484 while (*pos && *pos != quote) { 485 if (*pos == '\\' && pos[1] && pos[1] == quote) { 486 pos += 2; 487 } else { 488 ++pos; 489 } 490 } 491 if (*pos) { 492 ++pos; 493 } 494 } else ++pos; 495 } 496 if (*pos == '\0') { 497 res = estrdup(*line); 498 *line += strlen(*line); 499 return res; 500 } 501 502 res = estrndup(*line, pos - *line); 503 504 while (*pos == stop) { 505 ++pos; 506 } 507 508 *line = pos; 509 return res; 510} 511 512static char *substring_conf(char *start, int len, char quote) 513{ 514 char *result = emalloc(len + 1); 515 char *resp = result; 516 int i; 517 518 for (i = 0; i < len && start[i] != quote; ++i) { 519 if (start[i] == '\\' && (start[i + 1] == '\\' || (quote && start[i + 1] == quote))) { 520 *resp++ = start[++i]; 521 } else { 522 *resp++ = start[i]; 523 } 524 } 525 526 *resp = '\0'; 527 return result; 528} 529 530static char *php_ap_getword_conf(const zend_encoding *encoding, char *str TSRMLS_DC) 531{ 532 while (*str && isspace(*str)) { 533 ++str; 534 } 535 536 if (!*str) { 537 return estrdup(""); 538 } 539 540 if (*str == '"' || *str == '\'') { 541 char quote = *str; 542 543 str++; 544 return substring_conf(str, strlen(str), quote); 545 } else { 546 char *strend = str; 547 548 while (*strend && !isspace(*strend)) { 549 ++strend; 550 } 551 return substring_conf(str, strend - str, 0); 552 } 553} 554 555static char *php_ap_basename(const zend_encoding *encoding, char *path TSRMLS_DC) 556{ 557 char *s = strrchr(path, '\\'); 558 char *s2 = strrchr(path, '/'); 559 560 if (s && s2) { 561 if (s > s2) { 562 ++s; 563 } else { 564 s = ++s2; 565 } 566 return s; 567 } else if (s) { 568 return ++s; 569 } else if (s2) { 570 return ++s2; 571 } 572 return path; 573} 574 575/* 576 * Search for a string in a fixed-length byte string. 577 * If partial is true, partial matches are allowed at the end of the buffer. 578 * Returns NULL if not found, or a pointer to the start of the first match. 579 */ 580static void *php_ap_memstr(char *haystack, int haystacklen, char *needle, int needlen, int partial) 581{ 582 int len = haystacklen; 583 char *ptr = haystack; 584 585 /* iterate through first character matches */ 586 while( (ptr = memchr(ptr, needle[0], len)) ) { 587 588 /* calculate length after match */ 589 len = haystacklen - (ptr - (char *)haystack); 590 591 /* done if matches up to capacity of buffer */ 592 if (memcmp(needle, ptr, needlen < len ? needlen : len) == 0 && (partial || len >= needlen)) { 593 break; 594 } 595 596 /* next character */ 597 ptr++; len--; 598 } 599 600 return ptr; 601} 602 603/* read until a boundary condition */ 604static int multipart_buffer_read(multipart_buffer *self, char *buf, int bytes, int *end TSRMLS_DC) 605{ 606 int len, max; 607 char *bound; 608 609 /* fill buffer if needed */ 610 if (bytes > self->bytes_in_buffer) { 611 fill_buffer(self TSRMLS_CC); 612 } 613 614 /* look for a potential boundary match, only read data up to that point */ 615 if ((bound = php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 1))) { 616 max = bound - self->buf_begin; 617 if (end && php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 0)) { 618 *end = 1; 619 } 620 } else { 621 max = self->bytes_in_buffer; 622 } 623 624 /* maximum number of bytes we are reading */ 625 len = max < bytes-1 ? max : bytes-1; 626 627 /* if we read any data... */ 628 if (len > 0) { 629 630 /* copy the data */ 631 memcpy(buf, self->buf_begin, len); 632 buf[len] = 0; 633 634 if (bound && len > 0 && buf[len-1] == '\r') { 635 buf[--len] = 0; 636 } 637 638 /* update the buffer */ 639 self->bytes_in_buffer -= len; 640 self->buf_begin += len; 641 } 642 643 return len; 644} 645 646/* 647 XXX: this is horrible memory-usage-wise, but we only expect 648 to do this on small pieces of form data. 649*/ 650static char *multipart_buffer_read_body(multipart_buffer *self, unsigned int *len TSRMLS_DC) 651{ 652 char buf[FILLUNIT], *out=NULL; 653 int total_bytes=0, read_bytes=0; 654 655 while((read_bytes = multipart_buffer_read(self, buf, sizeof(buf), NULL TSRMLS_CC))) { 656 out = erealloc(out, total_bytes + read_bytes + 1); 657 memcpy(out + total_bytes, buf, read_bytes); 658 total_bytes += read_bytes; 659 } 660 661 if (out) { 662 out[total_bytes] = '\0'; 663 } 664 *len = total_bytes; 665 666 return out; 667} 668/* }}} */ 669 670/* 671 * The combined READER/HANDLER 672 * 673 */ 674 675SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ 676{ 677 char *boundary, *s = NULL, *boundary_end = NULL, *start_arr = NULL, *array_index = NULL; 678 char *temp_filename = NULL, *lbuf = NULL, *abuf = NULL; 679 int boundary_len = 0, total_bytes = 0, cancel_upload = 0, is_arr_upload = 0, array_len = 0; 680 int max_file_size = 0, skip_upload = 0, anonindex = 0, is_anonymous; 681 zval *http_post_files = NULL; 682 HashTable *uploaded_files = NULL; 683 multipart_buffer *mbuff; 684 zval *array_ptr = (zval *) arg; 685 int fd = -1; 686 zend_llist header; 687 void *event_extra_data = NULL; 688 unsigned int llen = 0; 689 int upload_cnt = INI_INT("max_file_uploads"); 690 const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding(TSRMLS_C); 691 php_rfc1867_getword_t getword; 692 php_rfc1867_getword_conf_t getword_conf; 693 php_rfc1867_basename_t _basename; 694 long count = 0; 695 696 if (php_rfc1867_encoding_translation(TSRMLS_C) && internal_encoding) { 697 getword = php_rfc1867_getword; 698 getword_conf = php_rfc1867_getword_conf; 699 _basename = php_rfc1867_basename; 700 } else { 701 getword = php_ap_getword; 702 getword_conf = php_ap_getword_conf; 703 _basename = php_ap_basename; 704 } 705 706 if (SG(post_max_size) > 0 && SG(request_info).content_length > SG(post_max_size)) { 707 sapi_module.sapi_error(E_WARNING, "POST Content-Length of %ld bytes exceeds the limit of %ld bytes", SG(request_info).content_length, SG(post_max_size)); 708 return; 709 } 710 711 /* Get the boundary */ 712 boundary = strstr(content_type_dup, "boundary"); 713 if (!boundary) { 714 int content_type_len = strlen(content_type_dup); 715 char *content_type_lcase = estrndup(content_type_dup, content_type_len); 716 717 php_strtolower(content_type_lcase, content_type_len); 718 boundary = strstr(content_type_lcase, "boundary"); 719 if (boundary) { 720 boundary = content_type_dup + (boundary - content_type_lcase); 721 } 722 efree(content_type_lcase); 723 } 724 725 if (!boundary || !(boundary = strchr(boundary, '='))) { 726 sapi_module.sapi_error(E_WARNING, "Missing boundary in multipart/form-data POST data"); 727 return; 728 } 729 730 boundary++; 731 boundary_len = strlen(boundary); 732 733 if (boundary[0] == '"') { 734 boundary++; 735 boundary_end = strchr(boundary, '"'); 736 if (!boundary_end) { 737 sapi_module.sapi_error(E_WARNING, "Invalid boundary in multipart/form-data POST data"); 738 return; 739 } 740 } else { 741 /* search for the end of the boundary */ 742 boundary_end = strpbrk(boundary, ",;"); 743 } 744 if (boundary_end) { 745 boundary_end[0] = '\0'; 746 boundary_len = boundary_end-boundary; 747 } 748 749 /* Initialize the buffer */ 750 if (!(mbuff = multipart_buffer_new(boundary, boundary_len TSRMLS_CC))) { 751 sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer"); 752 return; 753 } 754 755 /* Initialize $_FILES[] */ 756 zend_hash_init(&PG(rfc1867_protected_variables), 5, NULL, NULL, 0); 757 758 ALLOC_HASHTABLE(uploaded_files); 759 zend_hash_init(uploaded_files, 5, NULL, (dtor_func_t) free_estring, 0); 760 SG(rfc1867_uploaded_files) = uploaded_files; 761 762 ALLOC_ZVAL(http_post_files); 763 array_init(http_post_files); 764 INIT_PZVAL(http_post_files); 765 PG(http_globals)[TRACK_VARS_FILES] = http_post_files; 766 767 zend_llist_init(&header, sizeof(mime_header_entry), (llist_dtor_func_t) php_free_hdr_entry, 0); 768 769 if (php_rfc1867_callback != NULL) { 770 multipart_event_start event_start; 771 772 event_start.content_length = SG(request_info).content_length; 773 if (php_rfc1867_callback(MULTIPART_EVENT_START, &event_start, &event_extra_data TSRMLS_CC) == FAILURE) { 774 goto fileupload_done; 775 } 776 } 777 778 while (!multipart_buffer_eof(mbuff TSRMLS_CC)) 779 { 780 char buff[FILLUNIT]; 781 char *cd = NULL, *param = NULL, *filename = NULL, *tmp = NULL; 782 size_t blen = 0, wlen = 0; 783 off_t offset; 784 785 zend_llist_clean(&header); 786 787 if (!multipart_buffer_headers(mbuff, &header TSRMLS_CC)) { 788 goto fileupload_done; 789 } 790 791 if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) { 792 char *pair = NULL; 793 int end = 0; 794 795 while (isspace(*cd)) { 796 ++cd; 797 } 798 799 while (*cd && (pair = getword(mbuff->input_encoding, &cd, ';' TSRMLS_CC))) 800 { 801 char *key = NULL, *word = pair; 802 803 while (isspace(*cd)) { 804 ++cd; 805 } 806 807 if (strchr(pair, '=')) { 808 key = getword(mbuff->input_encoding, &pair, '=' TSRMLS_CC); 809 810 if (!strcasecmp(key, "name")) { 811 if (param) { 812 efree(param); 813 } 814 param = getword_conf(mbuff->input_encoding, pair TSRMLS_CC); 815 if (mbuff->input_encoding && internal_encoding) { 816 unsigned char *new_param; 817 size_t new_param_len; 818 if ((size_t)-1 != zend_multibyte_encoding_converter(&new_param, &new_param_len, (unsigned char *)param, strlen(param), internal_encoding, mbuff->input_encoding TSRMLS_CC)) { 819 efree(param); 820 param = (char *)new_param; 821 } 822 } 823 } else if (!strcasecmp(key, "filename")) { 824 if (filename) { 825 efree(filename); 826 } 827 filename = getword_conf(mbuff->input_encoding, pair TSRMLS_CC); 828 if (mbuff->input_encoding && internal_encoding) { 829 unsigned char *new_filename; 830 size_t new_filename_len; 831 if ((size_t)-1 != zend_multibyte_encoding_converter(&new_filename, &new_filename_len, (unsigned char *)filename, strlen(filename), internal_encoding, mbuff->input_encoding TSRMLS_CC)) { 832 efree(filename); 833 filename = (char *)new_filename; 834 } 835 } 836 } 837 } 838 if (key) { 839 efree(key); 840 } 841 efree(word); 842 } 843 844 /* Normal form variable, safe to read all data into memory */ 845 if (!filename && param) { 846 unsigned int value_len; 847 char *value = multipart_buffer_read_body(mbuff, &value_len TSRMLS_CC); 848 unsigned int new_val_len; /* Dummy variable */ 849 850 if (!value) { 851 value = estrdup(""); 852 value_len = 0; 853 } 854 855 if (mbuff->input_encoding && internal_encoding) { 856 unsigned char *new_value; 857 size_t new_value_len; 858 if ((size_t)-1 != zend_multibyte_encoding_converter(&new_value, &new_value_len, (unsigned char *)value, value_len, internal_encoding, mbuff->input_encoding TSRMLS_CC)) { 859 efree(value); 860 value = (char *)new_value; 861 value_len = new_value_len; 862 } 863 } 864 865 if (++count <= PG(max_input_vars) && sapi_module.input_filter(PARSE_POST, param, &value, value_len, &new_val_len TSRMLS_CC)) { 866 if (php_rfc1867_callback != NULL) { 867 multipart_event_formdata event_formdata; 868 size_t newlength = new_val_len; 869 870 event_formdata.post_bytes_processed = SG(read_post_bytes); 871 event_formdata.name = param; 872 event_formdata.value = &value; 873 event_formdata.length = new_val_len; 874 event_formdata.newlength = &newlength; 875 if (php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data TSRMLS_CC) == FAILURE) { 876 efree(param); 877 efree(value); 878 continue; 879 } 880 new_val_len = newlength; 881 } 882 safe_php_register_variable(param, value, new_val_len, array_ptr, 0 TSRMLS_CC); 883 } else { 884 if (count == PG(max_input_vars) + 1) { 885 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); 886 } 887 888 if (php_rfc1867_callback != NULL) { 889 multipart_event_formdata event_formdata; 890 891 event_formdata.post_bytes_processed = SG(read_post_bytes); 892 event_formdata.name = param; 893 event_formdata.value = &value; 894 event_formdata.length = value_len; 895 event_formdata.newlength = NULL; 896 php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data TSRMLS_CC); 897 } 898 } 899 900 if (!strcasecmp(param, "MAX_FILE_SIZE")) { 901 max_file_size = atol(value); 902 } 903 904 efree(param); 905 efree(value); 906 continue; 907 } 908 909 /* If file_uploads=off, skip the file part */ 910 if (!PG(file_uploads)) { 911 skip_upload = 1; 912 } else if (upload_cnt <= 0) { 913 skip_upload = 1; 914 sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded"); 915 } 916 917 /* Return with an error if the posted data is garbled */ 918 if (!param && !filename) { 919 sapi_module.sapi_error(E_WARNING, "File Upload Mime headers garbled"); 920 goto fileupload_done; 921 } 922 923 if (!param) { 924 is_anonymous = 1; 925 param = emalloc(MAX_SIZE_ANONNAME); 926 snprintf(param, MAX_SIZE_ANONNAME, "%u", anonindex++); 927 } else { 928 is_anonymous = 0; 929 } 930 931 /* New Rule: never repair potential malicious user input */ 932 if (!skip_upload) { 933 long c = 0; 934 tmp = param; 935 936 while (*tmp) { 937 if (*tmp == '[') { 938 c++; 939 } else if (*tmp == ']') { 940 c--; 941 if (tmp[1] && tmp[1] != '[') { 942 skip_upload = 1; 943 break; 944 } 945 } 946 if (c < 0) { 947 skip_upload = 1; 948 break; 949 } 950 tmp++; 951 } 952 /* Brackets should always be closed */ 953 if(c != 0) { 954 skip_upload = 1; 955 } 956 } 957 958 total_bytes = cancel_upload = 0; 959 temp_filename = NULL; 960 fd = -1; 961 962 if (!skip_upload && php_rfc1867_callback != NULL) { 963 multipart_event_file_start event_file_start; 964 965 event_file_start.post_bytes_processed = SG(read_post_bytes); 966 event_file_start.name = param; 967 event_file_start.filename = &filename; 968 if (php_rfc1867_callback(MULTIPART_EVENT_FILE_START, &event_file_start, &event_extra_data TSRMLS_CC) == FAILURE) { 969 temp_filename = ""; 970 efree(param); 971 efree(filename); 972 continue; 973 } 974 } 975 976 if (skip_upload) { 977 efree(param); 978 efree(filename); 979 continue; 980 } 981 982 if (filename[0] == '\0') { 983#if DEBUG_FILE_UPLOAD 984 sapi_module.sapi_error(E_NOTICE, "No file uploaded"); 985#endif 986 cancel_upload = UPLOAD_ERROR_D; 987 } 988 989 offset = 0; 990 end = 0; 991 992 if (!cancel_upload) { 993 /* only bother to open temp file if we have data */ 994 blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end TSRMLS_CC); 995#if DEBUG_FILE_UPLOAD 996 if (blen > 0) { 997#else 998 /* in non-debug mode we have no problem with 0-length files */ 999 { 1000#endif 1001 fd = php_open_temporary_fd_ex(PG(upload_tmp_dir), "php", &temp_filename, 1 TSRMLS_CC); 1002 upload_cnt--; 1003 if (fd == -1) { 1004 sapi_module.sapi_error(E_WARNING, "File upload error - unable to create a temporary file"); 1005 cancel_upload = UPLOAD_ERROR_E; 1006 } 1007 } 1008 } 1009 1010 while (!cancel_upload && (blen > 0)) 1011 { 1012 if (php_rfc1867_callback != NULL) { 1013 multipart_event_file_data event_file_data; 1014 1015 event_file_data.post_bytes_processed = SG(read_post_bytes); 1016 event_file_data.offset = offset; 1017 event_file_data.data = buff; 1018 event_file_data.length = blen; 1019 event_file_data.newlength = &blen; 1020 if (php_rfc1867_callback(MULTIPART_EVENT_FILE_DATA, &event_file_data, &event_extra_data TSRMLS_CC) == FAILURE) { 1021 cancel_upload = UPLOAD_ERROR_X; 1022 continue; 1023 } 1024 } 1025 1026 if (PG(upload_max_filesize) > 0 && (long)(total_bytes+blen) > PG(upload_max_filesize)) { 1027#if DEBUG_FILE_UPLOAD 1028 sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of %ld bytes exceeded - file [%s=%s] not saved", PG(upload_max_filesize), param, filename); 1029#endif 1030 cancel_upload = UPLOAD_ERROR_A; 1031 } else if (max_file_size && ((long)(total_bytes+blen) > max_file_size)) { 1032#if DEBUG_FILE_UPLOAD 1033 sapi_module.sapi_error(E_NOTICE, "MAX_FILE_SIZE of %ld bytes exceeded - file [%s=%s] not saved", max_file_size, param, filename); 1034#endif 1035 cancel_upload = UPLOAD_ERROR_B; 1036 } else if (blen > 0) { 1037 wlen = write(fd, buff, blen); 1038 1039 if (wlen == -1) { 1040 /* write failed */ 1041#if DEBUG_FILE_UPLOAD 1042 sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno)); 1043#endif 1044 cancel_upload = UPLOAD_ERROR_F; 1045 } else if (wlen < blen) { 1046#if DEBUG_FILE_UPLOAD 1047 sapi_module.sapi_error(E_NOTICE, "Only %d bytes were written, expected to write %d", wlen, blen); 1048#endif 1049 cancel_upload = UPLOAD_ERROR_F; 1050 } else { 1051 total_bytes += wlen; 1052 } 1053 offset += wlen; 1054 } 1055 1056 /* read data for next iteration */ 1057 blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end TSRMLS_CC); 1058 } 1059 1060 if (fd != -1) { /* may not be initialized if file could not be created */ 1061 close(fd); 1062 } 1063 1064 if (!cancel_upload && !end) { 1065#if DEBUG_FILE_UPLOAD 1066 sapi_module.sapi_error(E_NOTICE, "Missing mime boundary at the end of the data for file %s", filename[0] != '\0' ? filename : ""); 1067#endif 1068 cancel_upload = UPLOAD_ERROR_C; 1069 } 1070#if DEBUG_FILE_UPLOAD 1071 if (filename[0] != '\0' && total_bytes == 0 && !cancel_upload) { 1072 sapi_module.sapi_error(E_WARNING, "Uploaded file size 0 - file [%s=%s] not saved", param, filename); 1073 cancel_upload = 5; 1074 } 1075#endif 1076 if (php_rfc1867_callback != NULL) { 1077 multipart_event_file_end event_file_end; 1078 1079 event_file_end.post_bytes_processed = SG(read_post_bytes); 1080 event_file_end.temp_filename = temp_filename; 1081 event_file_end.cancel_upload = cancel_upload; 1082 if (php_rfc1867_callback(MULTIPART_EVENT_FILE_END, &event_file_end, &event_extra_data TSRMLS_CC) == FAILURE) { 1083 cancel_upload = UPLOAD_ERROR_X; 1084 } 1085 } 1086 1087 if (cancel_upload) { 1088 if (temp_filename) { 1089 if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */ 1090 unlink(temp_filename); 1091 } 1092 efree(temp_filename); 1093 } 1094 temp_filename = ""; 1095 } else { 1096 zend_hash_add(SG(rfc1867_uploaded_files), temp_filename, strlen(temp_filename) + 1, &temp_filename, sizeof(char *), NULL); 1097 } 1098 1099 /* is_arr_upload is true when name of file upload field 1100 * ends in [.*] 1101 * start_arr is set to point to 1st [ */ 1102 is_arr_upload = (start_arr = strchr(param,'[')) && (param[strlen(param)-1] == ']'); 1103 1104 if (is_arr_upload) { 1105 array_len = strlen(start_arr); 1106 if (array_index) { 1107 efree(array_index); 1108 } 1109 array_index = estrndup(start_arr + 1, array_len - 2); 1110 } 1111 1112 /* Add $foo_name */ 1113 if (llen < strlen(param) + MAX_SIZE_OF_INDEX + 1) { 1114 llen = strlen(param); 1115 lbuf = (char *) safe_erealloc(lbuf, llen, 1, MAX_SIZE_OF_INDEX + 1); 1116 llen += MAX_SIZE_OF_INDEX + 1; 1117 } 1118 1119 if (is_arr_upload) { 1120 if (abuf) efree(abuf); 1121 abuf = estrndup(param, strlen(param)-array_len); 1122 snprintf(lbuf, llen, "%s_name[%s]", abuf, array_index); 1123 } else { 1124 snprintf(lbuf, llen, "%s_name", param); 1125 } 1126 1127 /* The \ check should technically be needed for win32 systems only where 1128 * it is a valid path separator. However, IE in all it's wisdom always sends 1129 * the full path of the file on the user's filesystem, which means that unless 1130 * the user does basename() they get a bogus file name. Until IE's user base drops 1131 * to nill or problem is fixed this code must remain enabled for all systems. */ 1132 s = _basename(internal_encoding, filename TSRMLS_CC); 1133 if (!s) { 1134 s = filename; 1135 } 1136 1137 if (!is_anonymous) { 1138 safe_php_register_variable(lbuf, s, strlen(s), NULL, 0 TSRMLS_CC); 1139 } 1140 1141 /* Add $foo[name] */ 1142 if (is_arr_upload) { 1143 snprintf(lbuf, llen, "%s[name][%s]", abuf, array_index); 1144 } else { 1145 snprintf(lbuf, llen, "%s[name]", param); 1146 } 1147 register_http_post_files_variable(lbuf, s, http_post_files, 0 TSRMLS_CC); 1148 efree(filename); 1149 s = NULL; 1150 1151 /* Possible Content-Type: */ 1152 if (cancel_upload || !(cd = php_mime_get_hdr_value(header, "Content-Type"))) { 1153 cd = ""; 1154 } else { 1155 /* fix for Opera 6.01 */ 1156 s = strchr(cd, ';'); 1157 if (s != NULL) { 1158 *s = '\0'; 1159 } 1160 } 1161 1162 /* Add $foo_type */ 1163 if (is_arr_upload) { 1164 snprintf(lbuf, llen, "%s_type[%s]", abuf, array_index); 1165 } else { 1166 snprintf(lbuf, llen, "%s_type", param); 1167 } 1168 if (!is_anonymous) { 1169 safe_php_register_variable(lbuf, cd, strlen(cd), NULL, 0 TSRMLS_CC); 1170 } 1171 1172 /* Add $foo[type] */ 1173 if (is_arr_upload) { 1174 snprintf(lbuf, llen, "%s[type][%s]", abuf, array_index); 1175 } else { 1176 snprintf(lbuf, llen, "%s[type]", param); 1177 } 1178 register_http_post_files_variable(lbuf, cd, http_post_files, 0 TSRMLS_CC); 1179 1180 /* Restore Content-Type Header */ 1181 if (s != NULL) { 1182 *s = ';'; 1183 } 1184 s = ""; 1185 1186 { 1187 /* store temp_filename as-is (in case upload_tmp_dir 1188 * contains escapeable characters. escape only the variable name.) */ 1189 zval zfilename; 1190 1191 /* Initialize variables */ 1192 add_protected_variable(param TSRMLS_CC); 1193 1194 /* if param is of form xxx[.*] this will cut it to xxx */ 1195 if (!is_anonymous) { 1196 ZVAL_STRING(&zfilename, temp_filename, 1); 1197 safe_php_register_variable_ex(param, &zfilename, NULL, 1 TSRMLS_CC); 1198 } 1199 1200 /* Add $foo[tmp_name] */ 1201 if (is_arr_upload) { 1202 snprintf(lbuf, llen, "%s[tmp_name][%s]", abuf, array_index); 1203 } else { 1204 snprintf(lbuf, llen, "%s[tmp_name]", param); 1205 } 1206 add_protected_variable(lbuf TSRMLS_CC); 1207 ZVAL_STRING(&zfilename, temp_filename, 1); 1208 register_http_post_files_variable_ex(lbuf, &zfilename, http_post_files, 1 TSRMLS_CC); 1209 } 1210 1211 { 1212 zval file_size, error_type; 1213 1214 error_type.value.lval = cancel_upload; 1215 error_type.type = IS_LONG; 1216 1217 /* Add $foo[error] */ 1218 if (cancel_upload) { 1219 file_size.value.lval = 0; 1220 file_size.type = IS_LONG; 1221 } else { 1222 file_size.value.lval = total_bytes; 1223 file_size.type = IS_LONG; 1224 } 1225 1226 if (is_arr_upload) { 1227 snprintf(lbuf, llen, "%s[error][%s]", abuf, array_index); 1228 } else { 1229 snprintf(lbuf, llen, "%s[error]", param); 1230 } 1231 register_http_post_files_variable_ex(lbuf, &error_type, http_post_files, 0 TSRMLS_CC); 1232 1233 /* Add $foo_size */ 1234 if (is_arr_upload) { 1235 snprintf(lbuf, llen, "%s_size[%s]", abuf, array_index); 1236 } else { 1237 snprintf(lbuf, llen, "%s_size", param); 1238 } 1239 if (!is_anonymous) { 1240 safe_php_register_variable_ex(lbuf, &file_size, NULL, 0 TSRMLS_CC); 1241 } 1242 1243 /* Add $foo[size] */ 1244 if (is_arr_upload) { 1245 snprintf(lbuf, llen, "%s[size][%s]", abuf, array_index); 1246 } else { 1247 snprintf(lbuf, llen, "%s[size]", param); 1248 } 1249 register_http_post_files_variable_ex(lbuf, &file_size, http_post_files, 0 TSRMLS_CC); 1250 } 1251 efree(param); 1252 } 1253 } 1254 1255fileupload_done: 1256 if (php_rfc1867_callback != NULL) { 1257 multipart_event_end event_end; 1258 1259 event_end.post_bytes_processed = SG(read_post_bytes); 1260 php_rfc1867_callback(MULTIPART_EVENT_END, &event_end, &event_extra_data TSRMLS_CC); 1261 } 1262 1263 if (lbuf) efree(lbuf); 1264 if (abuf) efree(abuf); 1265 if (array_index) efree(array_index); 1266 zend_hash_destroy(&PG(rfc1867_protected_variables)); 1267 zend_llist_destroy(&header); 1268 if (mbuff->boundary_next) efree(mbuff->boundary_next); 1269 if (mbuff->boundary) efree(mbuff->boundary); 1270 if (mbuff->buffer) efree(mbuff->buffer); 1271 if (mbuff) efree(mbuff); 1272} 1273/* }}} */ 1274 1275SAPI_API void php_rfc1867_set_multibyte_callbacks( 1276 php_rfc1867_encoding_translation_t encoding_translation, 1277 php_rfc1867_get_detect_order_t get_detect_order, 1278 php_rfc1867_set_input_encoding_t set_input_encoding, 1279 php_rfc1867_getword_t getword, 1280 php_rfc1867_getword_conf_t getword_conf, 1281 php_rfc1867_basename_t basename) /* {{{ */ 1282{ 1283 php_rfc1867_encoding_translation = encoding_translation; 1284 php_rfc1867_get_detect_order = get_detect_order; 1285 php_rfc1867_set_input_encoding = set_input_encoding; 1286 php_rfc1867_getword = getword; 1287 php_rfc1867_getword_conf = getword_conf; 1288 php_rfc1867_basename = basename; 1289} 1290/* }}} */ 1291 1292/* 1293 * Local variables: 1294 * tab-width: 4 1295 * c-basic-offset: 4 1296 * End: 1297 * vim600: sw=4 ts=4 fdm=marker 1298 * vim<600: sw=4 ts=4 1299 */ 1300