Return to cast.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / main / streams |
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: */