Annotation of embedaddon/php/main/streams/memory.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
                      5:    | Copyright (c) 1997-2012 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:    | Author: Marcus Boerger <helly@php.net>                               |
                     16:    +----------------------------------------------------------------------+
                     17:  */
                     18: 
                     19: /* $Id: memory.c 321634 2012-01-01 13:15:04Z felipe $ */
                     20: 
                     21: #define _GNU_SOURCE
                     22: #include "php.h"
                     23: 
                     24: PHPAPI int php_url_decode(char *str, int len);
                     25: PHPAPI unsigned char *php_base64_decode(const unsigned char *str, int length, int *ret_length);
                     26: 
                     27: /* Memory streams use a dynamic memory buffer to emulate a stream.
                     28:  * You can use php_stream_memory_open to create a readonly stream
                     29:  * from an existing memory buffer.
                     30:  */
                     31: 
                     32: /* Temp streams are streams that uses memory streams as long their
                     33:  * size is less than a given memory amount. When a write operation
                     34:  * exceeds that limit the content is written to a temporary file.
                     35:  */
                     36: 
                     37: /* {{{ ------- MEMORY stream implementation -------*/
                     38: 
                     39: typedef struct {
                     40:        char        *data;
                     41:        size_t      fpos;
                     42:        size_t      fsize;
                     43:        size_t      smax;
                     44:        int                     mode;
                     45:        php_stream  **owner_ptr;
                     46: } php_stream_memory_data;
                     47: 
                     48: 
                     49: /* {{{ */
                     50: static size_t php_stream_memory_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
                     51: {
                     52:        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
                     53:        assert(ms != NULL);
                     54: 
                     55:        if (ms->mode & TEMP_STREAM_READONLY) {
                     56:                return 0;
                     57:        }
                     58:        if (ms->fpos + count > ms->fsize) {
                     59:                char *tmp;
                     60: 
                     61:                if (!ms->data) {
                     62:                        tmp = emalloc(ms->fpos + count);
                     63:                } else {
                     64:                        tmp = erealloc(ms->data, ms->fpos + count);
                     65:                }
                     66:                if (!tmp) {
                     67:                        count = ms->fsize - ms->fpos + 1;
                     68:                } else {
                     69:                        ms->data = tmp;
                     70:                        ms->fsize = ms->fpos + count;
                     71:                }
                     72:        }
                     73:        if (!ms->data)
                     74:                count = 0;
                     75:        if (count) {
                     76:                assert(buf!= NULL);
                     77:                memcpy(ms->data+ms->fpos, (char*)buf, count);
                     78:                ms->fpos += count;
                     79:        }
                     80:        return count;
                     81: }
                     82: /* }}} */
                     83: 
                     84: 
                     85: /* {{{ */
                     86: static size_t php_stream_memory_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
                     87: {
                     88:        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
                     89:        assert(ms != NULL);
                     90: 
                     91:        if (ms->fpos + count >= ms->fsize) {
                     92:                count = ms->fsize - ms->fpos;
                     93:                stream->eof = 1;
                     94:        }
                     95:        if (count) {
                     96:                assert(ms->data!= NULL);
                     97:                assert(buf!= NULL);
                     98:                memcpy(buf, ms->data+ms->fpos, count);
                     99:                ms->fpos += count;
                    100:        }
                    101:        return count;
                    102: }
                    103: /* }}} */
                    104: 
                    105: 
                    106: /* {{{ */
                    107: static int php_stream_memory_close(php_stream *stream, int close_handle TSRMLS_DC)
                    108: {
                    109:        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
                    110:        assert(ms != NULL);
                    111: 
                    112:        if (ms->data && close_handle && ms->mode != TEMP_STREAM_READONLY) {
                    113:                efree(ms->data);
                    114:        }
                    115:        if (ms->owner_ptr) {
                    116:                *ms->owner_ptr = NULL;
                    117:        }
                    118:        efree(ms);
                    119:        return 0;
                    120: }
                    121: /* }}} */
                    122: 
                    123: 
                    124: /* {{{ */
                    125: static int php_stream_memory_flush(php_stream *stream TSRMLS_DC)
                    126: {
                    127:        /* nothing to do here */
                    128:        return 0;
                    129: }
                    130: /* }}} */
                    131: 
                    132: 
                    133: /* {{{ */
                    134: static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
                    135: {
                    136:        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
                    137:        assert(ms != NULL);
                    138: 
                    139:        switch(whence) {
                    140:                case SEEK_CUR:
                    141:                        if (offset < 0) {
                    142:                                if (ms->fpos < (size_t)(-offset)) {
                    143:                                        ms->fpos = 0;
                    144:                                        *newoffs = -1;
                    145:                                        return -1;
                    146:                                } else {
                    147:                                        ms->fpos = ms->fpos + offset;
                    148:                                        *newoffs = ms->fpos;
                    149:                                        stream->eof = 0;
                    150:                                        return 0;
                    151:                                }
                    152:                        } else {
                    153:                                if (ms->fpos + (size_t)(offset) > ms->fsize) {
                    154:                                        ms->fpos = ms->fsize;
                    155:                                        *newoffs = -1;
                    156:                                        return -1;
                    157:                                } else {
                    158:                                        ms->fpos = ms->fpos + offset;
                    159:                                        *newoffs = ms->fpos;
                    160:                                        stream->eof = 0;
                    161:                                        return 0;
                    162:                                }
                    163:                        }
                    164:                case SEEK_SET:
                    165:                        if (ms->fsize < (size_t)(offset)) {
                    166:                                ms->fpos = ms->fsize;
                    167:                                *newoffs = -1;
                    168:                                return -1;
                    169:                        } else {
                    170:                                ms->fpos = offset;
                    171:                                *newoffs = ms->fpos;
                    172:                                stream->eof = 0;
                    173:                                return 0;
                    174:                        }
                    175:                case SEEK_END:
                    176:                        if (offset > 0) {
                    177:                                ms->fpos = ms->fsize;
                    178:                                *newoffs = -1;
                    179:                                return -1;
                    180:                        } else if (ms->fsize < (size_t)(-offset)) {
                    181:                                ms->fpos = 0;
                    182:                                *newoffs = -1;
                    183:                                return -1;
                    184:                        } else {
                    185:                                ms->fpos = ms->fsize + offset;
                    186:                                *newoffs = ms->fpos;
                    187:                                stream->eof = 0;
                    188:                                return 0;
                    189:                        }
                    190:                default:
                    191:                        *newoffs = ms->fpos;
                    192:                        return -1;
                    193:        }
                    194: }
                    195: /* }}} */
                    196: 
                    197: /* {{{ */
                    198: static int php_stream_memory_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
                    199: {
                    200:        return FAILURE;
                    201: }
                    202: /* }}} */
                    203: 
                    204: static int php_stream_memory_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
                    205: {
                    206:        time_t timestamp = 0;
                    207:        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
                    208:        assert(ms != NULL);
                    209: 
                    210:        memset(ssb, 0, sizeof(php_stream_statbuf));
                    211:        /* read-only across the board */
                    212:        
                    213:        ssb->sb.st_mode = ms->mode & TEMP_STREAM_READONLY ? 0444 : 0666;
                    214: 
                    215:        ssb->sb.st_size = ms->fsize;
                    216:        ssb->sb.st_mode |= S_IFREG; /* regular file */
                    217: 
                    218: #ifdef NETWARE
                    219:        ssb->sb.st_mtime.tv_sec = timestamp;
                    220:        ssb->sb.st_atime.tv_sec = timestamp;
                    221:        ssb->sb.st_ctime.tv_sec = timestamp;
                    222: #else
                    223:        ssb->sb.st_mtime = timestamp;
                    224:        ssb->sb.st_atime = timestamp;
                    225:        ssb->sb.st_ctime = timestamp;
                    226: #endif
                    227: 
                    228:        ssb->sb.st_nlink = 1;
                    229:        ssb->sb.st_rdev = -1;
                    230:        /* this is only for APC, so use /dev/null device - no chance of conflict there! */
                    231:        ssb->sb.st_dev = 0xC;
                    232:        /* generate unique inode number for alias/filename, so no phars will conflict */
                    233:        ssb->sb.st_ino = 0;
                    234: 
                    235: #ifndef PHP_WIN32
                    236:        ssb->sb.st_blksize = -1;
                    237: #endif
                    238: 
                    239: #if !defined(PHP_WIN32) && !defined(__BEOS__)
                    240:        ssb->sb.st_blocks = -1;
                    241: #endif
                    242: 
                    243:        return 0;
                    244: }
                    245: /* }}} */
                    246: 
                    247: static int php_stream_memory_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */
                    248: {
                    249:        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
                    250:        size_t newsize;
                    251:        
                    252:        switch(option) {
                    253:                case PHP_STREAM_OPTION_TRUNCATE_API:
                    254:                        switch (value) {
                    255:                                case PHP_STREAM_TRUNCATE_SUPPORTED:
                    256:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    257: 
                    258:                                case PHP_STREAM_TRUNCATE_SET_SIZE:
                    259:                                        if (ms->mode & TEMP_STREAM_READONLY) {
                    260:                                                return PHP_STREAM_OPTION_RETURN_ERR;
                    261:                                        }
                    262:                                        newsize = *(size_t*)ptrparam;
                    263:                                        if (newsize <= ms->fsize) {
                    264:                                                if (newsize < ms->fpos) {
                    265:                                                        ms->fpos = newsize;
                    266:                                                }
                    267:                                        } else {
                    268:                                                ms->data = erealloc(ms->data, newsize);
                    269:                                                memset(ms->data+ms->fsize, 0, newsize - ms->fsize);
                    270:                                                ms->fsize = newsize;
                    271:                                        }
                    272:                                        ms->fsize = newsize;
                    273:                                        return PHP_STREAM_OPTION_RETURN_OK;
                    274:                        }
                    275:                default:
                    276:                        return PHP_STREAM_OPTION_RETURN_NOTIMPL;
                    277:        }
                    278: }
                    279: /* }}} */
                    280:        
                    281: PHPAPI php_stream_ops  php_stream_memory_ops = {
                    282:        php_stream_memory_write, php_stream_memory_read,
                    283:        php_stream_memory_close, php_stream_memory_flush,
                    284:        "MEMORY",
                    285:        php_stream_memory_seek,
                    286:        php_stream_memory_cast,
                    287:        php_stream_memory_stat,
                    288:        php_stream_memory_set_option
                    289: };
                    290: 
                    291: 
                    292: /* {{{ */
                    293: PHPAPI php_stream *_php_stream_memory_create(int mode STREAMS_DC TSRMLS_DC)
                    294: {
                    295:        php_stream_memory_data *self;
                    296:        php_stream *stream;
                    297: 
                    298:        self = emalloc(sizeof(*self));
                    299:        self->data = NULL;
                    300:        self->fpos = 0;
                    301:        self->fsize = 0;
                    302:        self->smax = ~0u;
                    303:        self->mode = mode;
                    304:        self->owner_ptr = NULL;
                    305:        
                    306:        stream = php_stream_alloc_rel(&php_stream_memory_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b");
                    307:        stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
                    308:        return stream;
                    309: }
                    310: /* }}} */
                    311: 
                    312: 
                    313: /* {{{ */
                    314: PHPAPI php_stream *_php_stream_memory_open(int mode, char *buf, size_t length STREAMS_DC TSRMLS_DC)
                    315: {
                    316:        php_stream *stream;
                    317:        php_stream_memory_data *ms;
                    318: 
                    319:        if ((stream = php_stream_memory_create_rel(mode)) != NULL) {
                    320:                ms = (php_stream_memory_data*)stream->abstract;
                    321:                
                    322:                if (mode == TEMP_STREAM_READONLY || mode == TEMP_STREAM_TAKE_BUFFER) {
                    323:                        /* use the buffer directly */
                    324:                        ms->data = buf;
                    325:                        ms->fsize = length;
                    326:                } else {
                    327:                        if (length) {
                    328:                                assert(buf != NULL);
                    329:                                php_stream_write(stream, buf, length);
                    330:                        }
                    331:                }
                    332:        }
                    333:        return stream;
                    334: }
                    335: /* }}} */
                    336: 
                    337: 
                    338: /* {{{ */
                    339: PHPAPI char *_php_stream_memory_get_buffer(php_stream *stream, size_t *length STREAMS_DC TSRMLS_DC)
                    340: {
                    341:        php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract;
                    342: 
                    343:        assert(ms != NULL);
                    344:        assert(length != 0);
                    345: 
                    346:        *length = ms->fsize;
                    347:        return ms->data;
                    348: }
                    349: /* }}} */
                    350: 
                    351: /* }}} */
                    352: 
                    353: /* {{{ ------- TEMP stream implementation -------*/
                    354: 
                    355: typedef struct {
                    356:        php_stream  *innerstream;
                    357:        size_t      smax;
                    358:        int                     mode;
                    359:        zval*       meta;
                    360: } php_stream_temp_data;
                    361: 
                    362: 
                    363: /* {{{ */
                    364: static size_t php_stream_temp_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
                    365: {
                    366:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    367:        assert(ts != NULL);
                    368: 
                    369:        if (!ts->innerstream) {
                    370:                return -1;
                    371:        }
                    372:        if (php_stream_is(ts->innerstream, PHP_STREAM_IS_MEMORY)) {
                    373:                size_t memsize;
                    374:                char *membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize);
                    375: 
                    376:                if (memsize + count >= ts->smax) {
                    377:                        php_stream *file = php_stream_fopen_tmpfile();
                    378:                        php_stream_write(file, membuf, memsize);
                    379:                        php_stream_close(ts->innerstream);
                    380:                        ts->innerstream = file;
                    381:                }
                    382:        }
                    383:        return php_stream_write(ts->innerstream, buf, count);
                    384: }
                    385: /* }}} */
                    386: 
                    387: 
                    388: /* {{{ */
                    389: static size_t php_stream_temp_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
                    390: {
                    391:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    392:        size_t got;
                    393: 
                    394:        assert(ts != NULL);
                    395: 
                    396:        if (!ts->innerstream) {
                    397:                return -1;
                    398:        }
                    399:        
                    400:        got = php_stream_read(ts->innerstream, buf, count);
                    401:        
                    402:        stream->eof = ts->innerstream->eof;
                    403:        
                    404:        return got;
                    405: }
                    406: /* }}} */
                    407: 
                    408: 
                    409: /* {{{ */
                    410: static int php_stream_temp_close(php_stream *stream, int close_handle TSRMLS_DC)
                    411: {
                    412:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    413:        int ret;
                    414: 
                    415:        assert(ts != NULL);
                    416: 
                    417:        if (ts->innerstream) {
                    418:                ret = php_stream_free(ts->innerstream, PHP_STREAM_FREE_CLOSE | (close_handle ? 0 : PHP_STREAM_FREE_PRESERVE_HANDLE));
                    419:        } else {
                    420:                ret = 0;
                    421:        }
                    422:        
                    423:        if (ts->meta) {
                    424:                zval_ptr_dtor(&ts->meta);
                    425:        }
                    426: 
                    427:        efree(ts);
                    428: 
                    429:        return ret;
                    430: }
                    431: /* }}} */
                    432: 
                    433: 
                    434: /* {{{ */
                    435: static int php_stream_temp_flush(php_stream *stream TSRMLS_DC)
                    436: {
                    437:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    438:        assert(ts != NULL);
                    439: 
                    440:        return ts->innerstream ? php_stream_flush(ts->innerstream) : -1;
                    441: }
                    442: /* }}} */
                    443: 
                    444: 
                    445: /* {{{ */
                    446: static int php_stream_temp_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
                    447: {
                    448:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    449:        int ret;
                    450: 
                    451:        assert(ts != NULL);
                    452: 
                    453:        if (!ts->innerstream) {
                    454:                *newoffs = -1;
                    455:                return -1;
                    456:        }
                    457:        ret = php_stream_seek(ts->innerstream, offset, whence);
                    458:        *newoffs = php_stream_tell(ts->innerstream);
                    459:        stream->eof = ts->innerstream->eof;
                    460:        
                    461:        return ret;
                    462: }
                    463: /* }}} */
                    464: 
                    465: /* {{{ */
                    466: static int php_stream_temp_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
                    467: {
                    468:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    469:        php_stream *file;
                    470:        size_t memsize;
                    471:        char *membuf;
                    472:        off_t pos;
                    473: 
                    474:        assert(ts != NULL);
                    475: 
                    476:        if (!ts->innerstream) {
                    477:                return FAILURE;
                    478:        }
                    479:        if (php_stream_is(ts->innerstream, PHP_STREAM_IS_STDIO)) {
                    480:                return php_stream_cast(ts->innerstream, castas, ret, 0);
                    481:        }
                    482: 
                    483:        /* we are still using a memory based backing. If they are if we can be
                    484:         * a FILE*, say yes because we can perform the conversion.
                    485:         * If they actually want to perform the conversion, we need to switch
                    486:         * the memory stream to a tmpfile stream */
                    487: 
                    488:        if (ret == NULL && castas == PHP_STREAM_AS_STDIO) {
                    489:                return SUCCESS;
                    490:        }
                    491: 
                    492:        /* say "no" to other stream forms */
                    493:        if (ret == NULL) {
                    494:                return FAILURE;
                    495:        }
                    496: 
                    497:        /* perform the conversion and then pass the request on to the innerstream */
                    498:        membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize);
                    499:        file = php_stream_fopen_tmpfile();
                    500:        php_stream_write(file, membuf, memsize);
                    501:        pos = php_stream_tell(ts->innerstream);
                    502: 
                    503:        php_stream_close(ts->innerstream);
                    504:        ts->innerstream = file;
                    505:        php_stream_seek(ts->innerstream, pos, SEEK_SET);
                    506: 
                    507:        return php_stream_cast(ts->innerstream, castas, ret, 1);
                    508: }
                    509: /* }}} */
                    510: 
                    511: static int php_stream_temp_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
                    512: {
                    513:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    514: 
                    515:        if (!ts || !ts->innerstream) {
                    516:                return -1;
                    517:        }
                    518:        return php_stream_stat(ts->innerstream, ssb);
                    519: }
                    520: /* }}} */
                    521: 
                    522: static int php_stream_temp_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */
                    523: {
                    524:        php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract;
                    525:        
                    526:        switch(option) {
                    527:                case PHP_STREAM_OPTION_META_DATA_API:
                    528:                        if (ts->meta) {
                    529:                                zend_hash_copy(Z_ARRVAL_P((zval*)ptrparam), Z_ARRVAL_P(ts->meta), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
                    530:                        }
                    531:                        return PHP_STREAM_OPTION_RETURN_OK;
                    532:                default:
                    533:                        if (ts->innerstream) {
                    534:                                return php_stream_set_option(ts->innerstream, option, value, ptrparam);
                    535:                        }
                    536:                        return PHP_STREAM_OPTION_RETURN_NOTIMPL;
                    537:        }
                    538: }
                    539: /* }}} */
                    540: 
                    541: PHPAPI php_stream_ops  php_stream_temp_ops = {
                    542:        php_stream_temp_write, php_stream_temp_read,
                    543:        php_stream_temp_close, php_stream_temp_flush,
                    544:        "TEMP",
                    545:        php_stream_temp_seek,
                    546:        php_stream_temp_cast,
                    547:        php_stream_temp_stat,
                    548:        php_stream_temp_set_option
                    549: };
                    550: 
                    551: /* }}} */
                    552: 
                    553: /* {{{ _php_stream_temp_create */
                    554: PHPAPI php_stream *_php_stream_temp_create(int mode, size_t max_memory_usage STREAMS_DC TSRMLS_DC)
                    555: {
                    556:        php_stream_temp_data *self;
                    557:        php_stream *stream;
                    558: 
                    559:        self = ecalloc(1, sizeof(*self));
                    560:        self->smax = max_memory_usage;
                    561:        self->mode = mode;
                    562:        self->meta = NULL;
                    563:        stream = php_stream_alloc_rel(&php_stream_temp_ops, self, 0, mode & TEMP_STREAM_READONLY ? "rb" : "w+b");
                    564:        stream->flags |= PHP_STREAM_FLAG_NO_BUFFER;
                    565:        self->innerstream = php_stream_memory_create_rel(mode);
                    566:        php_stream_auto_cleanup(self->innerstream); /* do not warn if innerstream is GC'ed before stream */
                    567:        ((php_stream_memory_data*)self->innerstream->abstract)->owner_ptr = &self->innerstream;
                    568: 
                    569:        return stream;
                    570: }
                    571: /* }}} */
                    572: 
                    573: 
                    574: /* {{{ _php_stream_temp_open */
                    575: PHPAPI php_stream *_php_stream_temp_open(int mode, size_t max_memory_usage, char *buf, size_t length STREAMS_DC TSRMLS_DC)
                    576: {
                    577:        php_stream *stream;
                    578:        php_stream_temp_data *ts;
                    579:        off_t newoffs;
                    580: 
                    581:        if ((stream = php_stream_temp_create_rel(mode, max_memory_usage)) != NULL) {
                    582:                if (length) {
                    583:                        assert(buf != NULL);
                    584:                        php_stream_temp_write(stream, buf, length TSRMLS_CC);
                    585:                        php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC);
                    586:                }
                    587:                ts = (php_stream_temp_data*)stream->abstract;
                    588:                assert(ts != NULL);
                    589:                ts->mode = mode;
                    590:        }
                    591:        return stream;
                    592: }
                    593: /* }}} */
                    594: 
                    595: PHPAPI php_stream_ops php_stream_rfc2397_ops = {
                    596:        php_stream_temp_write, php_stream_temp_read,
                    597:        php_stream_temp_close, php_stream_temp_flush,
                    598:        "RFC2397",
                    599:        php_stream_temp_seek,
                    600:        php_stream_temp_cast,
                    601:        php_stream_temp_stat,
                    602:        php_stream_temp_set_option
                    603: };
                    604: 
                    605: static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
                    606: {
                    607:        php_stream *stream;
                    608:        php_stream_temp_data *ts;
                    609:        char *comma, *semi, *sep, *key;
                    610:        size_t mlen, dlen, plen, vlen;
                    611:        off_t newoffs;
                    612:        zval *meta = NULL;
                    613:        int base64 = 0, ilen;
                    614: 
                    615:        if (memcmp(path, "data:", 5)) {
                    616:                return NULL;
                    617:        }
                    618: 
                    619:        path += 5;
                    620:        dlen = strlen(path);
                    621: 
                    622:        if (dlen >= 2 && path[0] == '/' && path[1] == '/') {
                    623:                dlen -= 2;
                    624:                path += 2;
                    625:        }
                    626: 
                    627:        if ((comma = memchr(path, ',', dlen)) == NULL) {
                    628:                php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: no comma in URL");
                    629:                return NULL;
                    630:        }
                    631: 
                    632:        if (comma != path) {
                    633:                /* meta info */
                    634:                mlen = comma - path;
                    635:                dlen -= mlen;
                    636:                semi = memchr(path, ';', mlen);
                    637:                sep = memchr(path, '/', mlen);
                    638:                
                    639:                if (!semi && !sep) {
                    640:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type");
                    641:                        return NULL;
                    642:                }
                    643: 
                    644:                MAKE_STD_ZVAL(meta);
                    645:                array_init(meta);
                    646:                if (!semi) { /* there is only a mime type */
                    647:                        add_assoc_stringl(meta, "mediatype", path, mlen, 1);
                    648:                        mlen = 0;
                    649:                } else if (sep && sep < semi) { /* there is a mime type */
                    650:                        plen = semi - path;
                    651:                        add_assoc_stringl(meta, "mediatype", path, plen, 1);
                    652:                        mlen -= plen;
                    653:                        path += plen;
                    654:                } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */
                    655:                        zval_ptr_dtor(&meta);
                    656:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type");
                    657:                        return NULL;
                    658:                }
                    659:                /* get parameters and potentially ';base64' */
                    660:                while(semi && (semi == path)) {
                    661:                        path++;
                    662:                        mlen--;
                    663:                        sep = memchr(path, '=', mlen);
                    664:                        semi = memchr(path, ';', mlen);
                    665:                        if (!sep || (semi && semi < sep)) { /* must be ';base64' or failure */
                    666:                                if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) {
                    667:                                        /* must be error since parameters are only allowed after mediatype and we have no '=' sign */
                    668:                                        zval_ptr_dtor(&meta);
                    669:                                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal parameter");
                    670:                                        return NULL;
                    671:                                }
                    672:                                base64 = 1;
                    673:                                mlen -= sizeof("base64") - 1;
                    674:                                path += sizeof("base64") - 1;
                    675:                                break;
                    676:                        }
                    677:                        /* found parameter ... the heart of cs ppl lies in +1/-1 or was it +2 this time? */
                    678:                        plen = sep - path;
                    679:                        vlen = (semi ? semi - sep : mlen - plen) - 1 /* '=' */;
                    680:                        key = estrndup(path, plen);
                    681:                        add_assoc_stringl_ex(meta, key, plen + 1, sep + 1, vlen, 1);
                    682:                        efree(key);
                    683:                        plen += vlen + 1;
                    684:                        mlen -= plen;
                    685:                        path += plen;
                    686:                }
                    687:                if (mlen) {
                    688:                        zval_ptr_dtor(&meta);
                    689:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal URL");
                    690:                        return NULL;
                    691:                }
                    692:        } else {
                    693:                MAKE_STD_ZVAL(meta);
                    694:                array_init(meta);
                    695:        }
                    696:        add_assoc_bool(meta, "base64", base64);
                    697: 
                    698:        /* skip ',' */
                    699:        comma++;
                    700:        dlen--;
                    701: 
                    702:        if (base64) {
                    703:                comma = (char*)php_base64_decode((const unsigned char *)comma, dlen, &ilen);
                    704:                if (!comma) {
                    705:                        zval_ptr_dtor(&meta);
                    706:                        php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: unable to decode");
                    707:                        return NULL;
                    708:                }
                    709:        } else {
                    710:                comma = estrndup(comma, dlen);
                    711:                ilen = dlen = php_url_decode(comma, dlen);
                    712:        }
                    713: 
                    714:        if ((stream = php_stream_temp_create_rel(0, ~0u)) != NULL) {
                    715:                /* store data */
                    716:                php_stream_temp_write(stream, comma, ilen TSRMLS_CC);
                    717:                php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC);
                    718:                /* set special stream stuff (enforce exact mode) */
                    719:                vlen = strlen(mode);
                    720:                if (vlen >= sizeof(stream->mode)) {
                    721:                        vlen = sizeof(stream->mode) - 1;
                    722:                }
                    723:                memcpy(stream->mode, mode, vlen);
                    724:                stream->mode[vlen] = '\0';
                    725:                stream->ops = &php_stream_rfc2397_ops;
                    726:                ts = (php_stream_temp_data*)stream->abstract;
                    727:                assert(ts != NULL);
                    728:                ts->mode = mode && mode[0] == 'r' && mode[1] != '+' ? TEMP_STREAM_READONLY : 0;
                    729:                ts->meta = meta;
                    730:        }
                    731:        efree(comma);
                    732: 
                    733:        return stream;
                    734: }
                    735: 
                    736: PHPAPI php_stream_wrapper_ops php_stream_rfc2397_wops = {
                    737:        php_stream_url_wrap_rfc2397,
                    738:        NULL, /* close */
                    739:        NULL, /* fstat */
                    740:        NULL, /* stat */
                    741:        NULL, /* opendir */
                    742:        "RFC2397",
                    743:        NULL, /* unlink */
                    744:        NULL, /* rename */
                    745:        NULL, /* mkdir */
                    746:        NULL  /* rmdir */
                    747: };
                    748: 
                    749: PHPAPI php_stream_wrapper php_stream_rfc2397_wrapper = {
                    750:        &php_stream_rfc2397_wops,
                    751:        NULL,
                    752:        1, /* is_url */
                    753: };
                    754: 
                    755: /*
                    756:  * Local variables:
                    757:  * tab-width: 4
                    758:  * c-basic-offset: 4
                    759:  * End:
                    760:  * vim600: noet sw=4 ts=4 fdm=marker
                    761:  * vim<600: noet sw=4 ts=4
                    762:  */

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