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

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

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