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