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