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