Annotation of embedaddon/php/main/rfc1867.c, revision 1.1.1.5

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>