Annotation of embedaddon/php/main/streams/cast.c, revision 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:    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
        !            16:    +----------------------------------------------------------------------+
        !            17:  */
        !            18: 
        !            19: /* $Id: cast.c 321634 2012-01-01 13:15:04Z felipe $ */
        !            20: 
        !            21: #define _GNU_SOURCE
        !            22: #include "php.h"
        !            23: #include "php_globals.h"
        !            24: #include "php_network.h"
        !            25: #include "php_open_temporary_file.h"
        !            26: #include "ext/standard/file.h"
        !            27: #include <stddef.h>
        !            28: #include <fcntl.h>
        !            29: 
        !            30: #include "php_streams_int.h"
        !            31: 
        !            32: /* Under BSD, emulate fopencookie using funopen */
        !            33: #if defined(HAVE_FUNOPEN) && !defined(HAVE_FOPENCOOKIE)
        !            34: typedef struct {
        !            35:        int (*reader)(void *, char *, int);
        !            36:        int (*writer)(void *, const char *, int);
        !            37:        fpos_t (*seeker)(void *, fpos_t, int);
        !            38:        int (*closer)(void *);
        !            39: } COOKIE_IO_FUNCTIONS_T;
        !            40: 
        !            41: FILE *fopencookie(void *cookie, const char *mode, COOKIE_IO_FUNCTIONS_T *funcs)
        !            42: {
        !            43:        return funopen(cookie, funcs->reader, funcs->writer, funcs->seeker, funcs->closer);
        !            44: }
        !            45: # define HAVE_FOPENCOOKIE 1
        !            46: # define PHP_EMULATE_FOPENCOOKIE 1
        !            47: # define PHP_STREAM_COOKIE_FUNCTIONS   &stream_cookie_functions
        !            48: #elif defined(HAVE_FOPENCOOKIE)
        !            49: # define PHP_STREAM_COOKIE_FUNCTIONS   stream_cookie_functions
        !            50: #endif
        !            51: 
        !            52: /* {{{ STDIO with fopencookie */
        !            53: #if defined(PHP_EMULATE_FOPENCOOKIE)
        !            54: /* use our fopencookie emulation */
        !            55: static int stream_cookie_reader(void *cookie, char *buffer, int size)
        !            56: {
        !            57:        int ret;
        !            58:        TSRMLS_FETCH();
        !            59: 
        !            60:        ret = php_stream_read((php_stream*)cookie, buffer, size);
        !            61:        return ret;
        !            62: }
        !            63: 
        !            64: static int stream_cookie_writer(void *cookie, const char *buffer, int size)
        !            65: {
        !            66:        TSRMLS_FETCH();
        !            67: 
        !            68:        return php_stream_write((php_stream *)cookie, (char *)buffer, size);
        !            69: }
        !            70: 
        !            71: static fpos_t stream_cookie_seeker(void *cookie, off_t position, int whence)
        !            72: {
        !            73:        TSRMLS_FETCH();
        !            74: 
        !            75:        return (fpos_t)php_stream_seek((php_stream *)cookie, position, whence);
        !            76: }
        !            77: 
        !            78: static int stream_cookie_closer(void *cookie)
        !            79: {
        !            80:        php_stream *stream = (php_stream*)cookie;
        !            81:        TSRMLS_FETCH();
        !            82: 
        !            83:        /* prevent recursion */
        !            84:        stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
        !            85:        return php_stream_close(stream);
        !            86: }
        !            87: #elif defined(HAVE_FOPENCOOKIE)
        !            88: static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size)
        !            89: {
        !            90:        ssize_t ret;
        !            91:        TSRMLS_FETCH();
        !            92: 
        !            93:        ret = php_stream_read(((php_stream *)cookie), buffer, size);
        !            94:        return ret;
        !            95: }
        !            96: 
        !            97: static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t size)
        !            98: {
        !            99:        TSRMLS_FETCH();
        !           100: 
        !           101:        return php_stream_write(((php_stream *)cookie), (char *)buffer, size);
        !           102: }
        !           103: 
        !           104: # ifdef COOKIE_SEEKER_USES_OFF64_T
        !           105: static int stream_cookie_seeker(void *cookie, __off64_t *position, int whence)
        !           106: {
        !           107:        TSRMLS_FETCH();
        !           108: 
        !           109:        *position = php_stream_seek((php_stream *)cookie, (off_t)*position, whence);
        !           110: 
        !           111:        if (*position == -1) {
        !           112:                return -1;
        !           113:        }
        !           114:        return 0;
        !           115: }
        !           116: # else
        !           117: static int stream_cookie_seeker(void *cookie, off_t position, int whence)
        !           118: {
        !           119:        TSRMLS_FETCH();
        !           120: 
        !           121:        return php_stream_seek((php_stream *)cookie, position, whence);
        !           122: }
        !           123: # endif
        !           124: 
        !           125: static int stream_cookie_closer(void *cookie)
        !           126: {
        !           127:        php_stream *stream = (php_stream*)cookie;
        !           128:        TSRMLS_FETCH();
        !           129: 
        !           130:        /* prevent recursion */
        !           131:        stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE;
        !           132:        return php_stream_close(stream);
        !           133: }
        !           134: #endif /* elif defined(HAVE_FOPENCOOKIE) */
        !           135: 
        !           136: #if HAVE_FOPENCOOKIE
        !           137: static COOKIE_IO_FUNCTIONS_T stream_cookie_functions =
        !           138: {
        !           139:        stream_cookie_reader, stream_cookie_writer,
        !           140:        stream_cookie_seeker, stream_cookie_closer
        !           141: };
        !           142: #else
        !           143: /* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */
        !           144: #endif
        !           145: /* }}} */
        !           146: 
        !           147: /* {{{ php_stream_mode_sanitize_fdopen_fopencookie
        !           148:  * Result should have at least size 5, e.g. to write wbx+\0 */
        !           149: void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result)
        !           150: {
        !           151:        /* replace modes not supported by fdopen and fopencookie, but supported 
        !           152:         * by PHP's fread(), so that their calls won't fail */
        !           153:        const char *cur_mode = stream->mode;
        !           154:        int         has_plus = 0,
        !           155:                        has_bin  = 0,
        !           156:                                i,
        !           157:                                res_curs = 0;
        !           158: 
        !           159:        if (cur_mode[0] == 'r' || cur_mode[0] == 'w' || cur_mode[0] == 'a') {
        !           160:                result[res_curs++] = cur_mode[0];
        !           161:        } else {
        !           162:                /* assume cur_mode[0] is 'c' or 'x'; substitute by 'w', which should not
        !           163:                 * truncate anything in fdopen/fopencookie */
        !           164:                result[res_curs++] = 'w';
        !           165: 
        !           166:                /* x is allowed (at least by glibc & compat), but not as the 1st mode
        !           167:                 * as in PHP and in any case is (at best) ignored by fdopen and fopencookie */
        !           168:        }
        !           169:        
        !           170:        /* assume current mode has at most length 4 (e.g. wbn+) */
        !           171:        for (i = 1; i < 4 && cur_mode[i] != '\0'; i++) {
        !           172:                if (cur_mode[i] == 'b') {
        !           173:                        has_bin = 1;
        !           174:                } else if (cur_mode[i] == '+') {
        !           175:                        has_plus = 1;
        !           176:                }
        !           177:                /* ignore 'n', 't' or other stuff */
        !           178:        }
        !           179: 
        !           180:        if (has_bin) {
        !           181:                result[res_curs++] = 'b';
        !           182:        }
        !           183:        if (has_plus) {
        !           184:                result[res_curs++] = '+';
        !           185:        }
        !           186: 
        !           187:        result[res_curs] = '\0';
        !           188: }
        !           189: /* }}} */
        !           190: 
        !           191: /* {{{ php_stream_cast */
        !           192: PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC)
        !           193: {
        !           194:        int flags = castas & PHP_STREAM_CAST_MASK;
        !           195:        castas &= ~PHP_STREAM_CAST_MASK;
        !           196: 
        !           197:        /* synchronize our buffer (if possible) */
        !           198:        if (ret && castas != PHP_STREAM_AS_FD_FOR_SELECT) {
        !           199:                php_stream_flush(stream);
        !           200:                if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) {
        !           201:                        off_t dummy;
        !           202: 
        !           203:                        stream->ops->seek(stream, stream->position, SEEK_SET, &dummy TSRMLS_CC);
        !           204:                        stream->readpos = stream->writepos = 0;
        !           205:                }
        !           206:        }
        !           207: 
        !           208:        /* filtered streams can only be cast as stdio, and only when fopencookie is present */
        !           209: 
        !           210:        if (castas == PHP_STREAM_AS_STDIO) {
        !           211:                if (stream->stdiocast) {
        !           212:                        if (ret) {
        !           213:                                *(FILE**)ret = stream->stdiocast;
        !           214:                        }
        !           215:                        goto exit_success;
        !           216:                }
        !           217: 
        !           218:                /* if the stream is a stdio stream let's give it a chance to respond
        !           219:                 * first, to avoid doubling up the layers of stdio with an fopencookie */
        !           220:                if (php_stream_is(stream, PHP_STREAM_IS_STDIO) &&
        !           221:                        stream->ops->cast &&
        !           222:                        !php_stream_is_filtered(stream) &&
        !           223:                        stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS
        !           224:                ) {
        !           225:                        goto exit_success;
        !           226:                }
        !           227: 
        !           228: #if HAVE_FOPENCOOKIE
        !           229:                /* if just checking, say yes we can be a FILE*, but don't actually create it yet */
        !           230:                if (ret == NULL) {
        !           231:                        goto exit_success;
        !           232:                }
        !           233: 
        !           234:                {
        !           235:                        char fixed_mode[5];
        !           236:                        php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode);
        !           237:                        *(FILE**)ret = fopencookie(stream, fixed_mode, PHP_STREAM_COOKIE_FUNCTIONS);
        !           238:                }
        !           239: 
        !           240:                if (*ret != NULL) {
        !           241:                        off_t pos;
        !           242: 
        !           243:                        stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE;
        !           244: 
        !           245:                        /* If the stream position is not at the start, we need to force
        !           246:                         * the stdio layer to believe it's real location. */
        !           247:                        pos = php_stream_tell(stream);
        !           248:                        if (pos > 0) {
        !           249:                                fseek(*ret, pos, SEEK_SET);
        !           250:                        }
        !           251: 
        !           252:                        goto exit_success;
        !           253:                }
        !           254: 
        !           255:                /* must be either:
        !           256:                        a) programmer error
        !           257:                        b) no memory
        !           258:                        -> lets bail
        !           259:                */
        !           260:                php_error_docref(NULL TSRMLS_CC, E_ERROR, "fopencookie failed");
        !           261:                return FAILURE;
        !           262: #endif
        !           263: 
        !           264:                if (!php_stream_is_filtered(stream) && stream->ops->cast && stream->ops->cast(stream, castas, NULL TSRMLS_CC) == SUCCESS) {
        !           265:                        if (FAILURE == stream->ops->cast(stream, castas, ret TSRMLS_CC)) {
        !           266:                                return FAILURE;
        !           267:                        }
        !           268:                        goto exit_success;
        !           269:                } else if (flags & PHP_STREAM_CAST_TRY_HARD) {
        !           270:                        php_stream *newstream;
        !           271: 
        !           272:                        newstream = php_stream_fopen_tmpfile();
        !           273:                        if (newstream) {
        !           274:                                size_t retcopy = php_stream_copy_to_stream_ex(stream, newstream, PHP_STREAM_COPY_ALL, NULL);
        !           275: 
        !           276:                                if (retcopy != SUCCESS) {
        !           277:                                        php_stream_close(newstream);
        !           278:                                } else {
        !           279:                                        int retcast = php_stream_cast(newstream, castas | flags, (void **)ret, show_err);
        !           280: 
        !           281:                                        if (retcast == SUCCESS) {
        !           282:                                                rewind(*(FILE**)ret);
        !           283:                                        }
        !           284: 
        !           285:                                        /* do some specialized cleanup */
        !           286:                                        if ((flags & PHP_STREAM_CAST_RELEASE)) {
        !           287:                                                php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED);
        !           288:                                        }
        !           289: 
        !           290:                                        /* TODO: we probably should be setting .stdiocast and .fclose_stdiocast or
        !           291:                                         * we may be leaking the FILE*. Needs investigation, though. */
        !           292:                                        return retcast;
        !           293:                                }
        !           294:                        }
        !           295:                }
        !           296:        }
        !           297: 
        !           298:        if (php_stream_is_filtered(stream)) {
        !           299:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system");
        !           300:                return FAILURE;
        !           301:        } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) {
        !           302:                goto exit_success;
        !           303:        }
        !           304: 
        !           305:        if (show_err) {
        !           306:                /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */
        !           307:                static const char *cast_names[4] = {
        !           308:                        "STDIO FILE*",
        !           309:                        "File Descriptor",
        !           310:                        "Socket Descriptor",
        !           311:                        "select()able descriptor"
        !           312:                };
        !           313: 
        !           314:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]);
        !           315:        }
        !           316: 
        !           317:        return FAILURE;
        !           318: 
        !           319: exit_success:
        !           320: 
        !           321:        if ((stream->writepos - stream->readpos) > 0 &&
        !           322:                stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE &&
        !           323:                (flags & PHP_STREAM_CAST_INTERNAL) == 0
        !           324:        ) {
        !           325:                /* the data we have buffered will be lost to the third party library that
        !           326:                 * will be accessing the stream.  Emit a warning so that the end-user will
        !           327:                 * know that they should try something else */
        !           328: 
        !           329:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "%ld bytes of buffered data lost during stream conversion!", (long)(stream->writepos - stream->readpos));
        !           330:        }
        !           331: 
        !           332:        if (castas == PHP_STREAM_AS_STDIO && ret) {
        !           333:                stream->stdiocast = *(FILE**)ret;
        !           334:        }
        !           335: 
        !           336:        if (flags & PHP_STREAM_CAST_RELEASE) {
        !           337:                php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED);
        !           338:        }
        !           339: 
        !           340:        return SUCCESS;
        !           341: 
        !           342: }
        !           343: /* }}} */
        !           344: 
        !           345: /* {{{ php_stream_open_wrapper_as_file */
        !           346: PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC)
        !           347: {
        !           348:        FILE *fp = NULL;
        !           349:        php_stream *stream = NULL;
        !           350: 
        !           351:        stream = php_stream_open_wrapper_rel(path, mode, options|STREAM_WILL_CAST, opened_path);
        !           352: 
        !           353:        if (stream == NULL) {
        !           354:                return NULL;
        !           355:        }
        !           356: 
        !           357:        if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE, (void**)&fp, REPORT_ERRORS) == FAILURE) {
        !           358:                php_stream_close(stream);
        !           359:                if (opened_path && *opened_path) {
        !           360:                        efree(*opened_path);
        !           361:                }
        !           362:                return NULL;
        !           363:        }
        !           364:        return fp;
        !           365: }
        !           366: /* }}} */
        !           367: 
        !           368: /* {{{ php_stream_make_seekable */
        !           369: PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC)
        !           370: {
        !           371:        if (newstream == NULL) {
        !           372:                return PHP_STREAM_FAILED;
        !           373:        }
        !           374:        *newstream = NULL;
        !           375: 
        !           376:        if (((flags & PHP_STREAM_FORCE_CONVERSION) == 0) && origstream->ops->seek != NULL) {
        !           377:                *newstream = origstream;
        !           378:                return PHP_STREAM_UNCHANGED;
        !           379:        }
        !           380: 
        !           381:        /* Use a tmpfile and copy the old streams contents into it */
        !           382: 
        !           383:        if (flags & PHP_STREAM_PREFER_STDIO) {
        !           384:                *newstream = php_stream_fopen_tmpfile();
        !           385:        } else {
        !           386:                *newstream = php_stream_temp_new();
        !           387:        }
        !           388: 
        !           389:        if (*newstream == NULL) {
        !           390:                return PHP_STREAM_FAILED;
        !           391:        }
        !           392: 
        !           393: #if ZEND_DEBUG
        !           394:        (*newstream)->open_filename = origstream->open_filename;
        !           395:        (*newstream)->open_lineno = origstream->open_lineno;
        !           396: #endif
        !           397: 
        !           398:        if (php_stream_copy_to_stream_ex(origstream, *newstream, PHP_STREAM_COPY_ALL, NULL) != SUCCESS) {
        !           399:                php_stream_close(*newstream);
        !           400:                *newstream = NULL;
        !           401:                return PHP_STREAM_CRITICAL;
        !           402:        }
        !           403: 
        !           404:        php_stream_close(origstream);
        !           405:        php_stream_seek(*newstream, 0, SEEK_SET);
        !           406: 
        !           407:        return PHP_STREAM_RELEASED;
        !           408: }
        !           409: /* }}} */
        !           410: 
        !           411: /*
        !           412:  * Local variables:
        !           413:  * tab-width: 4
        !           414:  * c-basic-offset: 4
        !           415:  * End:
        !           416:  * vim600: noet sw=4 ts=4 fdm=marker
        !           417:  * vim<600: noet sw=4 ts=4
        !           418:  */

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