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