? wez.streams.php ? index.html ? config.nice.cgi ? Session.vim ? wez.streams.2.diff ? wez.streams.diff.gz Index: ext/bz2/bz2.c =================================================================== RCS file: /repository/php4/ext/bz2/bz2.c,v retrieving revision 1.34 diff -u -w -B -b -r1.34 bz2.c --- ext/bz2/bz2.c 27 Jan 2002 17:24:31 -0000 1.34 +++ ext/bz2/bz2.c 18 Feb 2002 12:00:21 -0000 @@ -128,8 +128,21 @@ } /* If it is a resource, than its a 'FILE *' resource */ else { - ZEND_FETCH_RESOURCE(fp, FILE *, file, -1, "File Handle", php_file_le_fopen()); - bz = BZ2_bzdopen(fileno(fp), Z_STRVAL_PP(mode)); + int type; + int fd; + + fp = (FILE*) zend_fetch_resource(file TSRMLS_CC, -1, "File-Handle", &type, 2, php_file_le_fopen(), php_file_le_stream()); + ZEND_VERIFY_RESOURCE(fp); +#if HAVE_PHP_STREAM + if (type == php_file_le_stream()) { + if (!php_stream_cast((php_stream*)fp, PHP_STREAM_AS_FD, (void*)&fd, REPORT_ERRORS)) + RETURN_FALSE; + } + else +#endif + fd = fileno(fp); + + bz = BZ2_bzdopen(fd, Z_STRVAL_PP(mode)); } if (bz == NULL) { Index: ext/curl/curl.c =================================================================== RCS file: /repository/php4/ext/curl/curl.c,v retrieving revision 1.105 diff -u -w -B -b -r1.105 curl.c --- ext/curl/curl.c 27 Jan 2002 06:11:50 -0000 1.105 +++ ext/curl/curl.c 18 Feb 2002 12:00:26 -0000 @@ -699,7 +699,18 @@ case CURLOPT_WRITEHEADER: case CURLOPT_STDERR: { FILE *fp = NULL; - ZEND_FETCH_RESOURCE(fp, FILE *, zvalue, -1, "File-Handle", php_file_le_fopen()); + int type; + void * what; + + what = zend_fetch_resource(zvalue TSRMLS_CC, -1, "File-Handle", &type, 2, php_file_le_fopen(), php_file_le_stream()); + ZEND_VERIFY_RESOURCE(what); + +#if HAVE_PHP_STREAM + if (type == php_file_le_stream()) { + if (!php_stream_cast((php_stream*)what, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS)) + RETURN_FALSE; + } +#endif if (!fp) { RETURN_FALSE; } Index: ext/mailparse/mailparse.c =================================================================== RCS file: /repository/php4/ext/mailparse/mailparse.c,v retrieving revision 1.17 diff -u -w -B -b -r1.17 mailparse.c --- ext/mailparse/mailparse.c 4 Feb 2002 14:27:37 -0000 1.17 +++ ext/mailparse/mailparse.c 18 Feb 2002 12:00:38 -0000 @@ -183,6 +183,7 @@ zval * file, * item; FILE *infp, *outfp=NULL, *partfp=NULL; int type; + void * what; char * buffer = NULL; char * outpath = NULL; int nparts = 0; @@ -190,9 +191,20 @@ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &file)) return; - infp = (FILE*)zend_fetch_resource(&file TSRMLS_CC, -1, "File-Handle", &type, 1, php_file_le_fopen()); + what = zend_fetch_resource(&file TSRMLS_CC, -1, "File-Handle", &type, 2, php_file_le_fopen(), php_file_le_stream()); ZEND_VERIFY_RESOURCE(infp); +#if HAVE_PHP_STREAM + if (type == php_file_le_stream()) { + if (php_stream_cast((php_stream *) what, PHP_STREAM_AS_STDIO, (void *) &infp, 1) == FAILURE) { + RETURN_FALSE; + } + } + else +#endif + infp = (FILE*)what; + + outfp = php_open_temporary_file(NULL, "mailparse", &outpath TSRMLS_CC); if (outfp == NULL) { zend_error(E_WARNING, "%s(): unable to open temp file", get_active_function_name(TSRMLS_C)); @@ -401,6 +413,18 @@ /* {{{ proto boolean mailparse_stream_encode(resource sourcefp, resource destfp, string encoding) Streams data from source file pointer, apply encoding and write to destfp */ +#if HAVE_PHP_STREAM +static int mailparse_stream_output(int c, void * stream) +{ + char buf = c; + return php_stream_write((php_stream*)stream, &buf, 1); +} +static int mailparse_stream_flush(void * stream) +{ + return php_stream_flush((php_stream*)stream); +} +#endif + static int mailparse_fp_output(int c, void * fp) { return fputc(c, (FILE*)fp); @@ -414,6 +438,11 @@ { zval ** srcfile, ** destfile, ** encod; FILE * srcfp, * destfp; + void * what; + int type; +#if HAVE_PHP_STREAM + php_stream * srcstream, * deststream; +#endif char * buf; size_t len; size_t bufsize = 2048; @@ -427,12 +456,32 @@ if (Z_TYPE_PP(srcfile) == IS_RESOURCE && Z_LVAL_PP(srcfile) == 0) { RETURN_FALSE; } - ZEND_FETCH_RESOURCE(srcfp, FILE *, srcfile, -1, "File-Handle", php_file_le_fopen()); - if (Z_TYPE_PP(destfile) == IS_RESOURCE && Z_LVAL_PP(destfile) == 0) { RETURN_FALSE; } - ZEND_FETCH_RESOURCE(destfp, FILE *, destfile, -1, "File-Handle", php_file_le_fopen()); + + what = zend_fetch_resource(srcfile TSRMLS_CC, -1, "File-Handle", &type, 2, php_file_le_fopen(), php_file_le_stream()); + ZEND_VERIFY_RESOURCE(what); + +#if HAVE_PHP_STREAM + if (type == php_file_le_stream()) { + srcstream = (php_stream*)what; + } + else +#endif + srcfp = (FILE*)what; + + + what = zend_fetch_resource(destfile TSRMLS_CC, -1, "File-Handle", &type, 2, php_file_le_fopen(), php_file_le_stream()); + ZEND_VERIFY_RESOURCE(what); +#if HAVE_PHP_STREAM + if (type == php_file_le_stream()) { + deststream = (php_stream*)what; + } + else +#endif + destfp = (FILE*)what; + convert_to_string_ex(encod); enc = mbfl_name2no_encoding(Z_STRVAL_PP(encod)); @@ -447,6 +496,28 @@ buf = emalloc(bufsize); RETVAL_TRUE; +#if HAVE_PHP_STREAM + if (type == php_file_le_stream()) { + conv = mbfl_convert_filter_new(mbfl_no_encoding_8bit, + enc, + mailparse_stream_output, + mailparse_stream_flush, + deststream + ); + + while(!php_stream_eof(srcstream)) { + len = php_stream_read(srcstream, buf, bufsize); + if (len > 0) + { + int i; + for (i=0; i here! */ } Index: ext/recode/recode.c =================================================================== RCS file: /repository/php4/ext/recode/recode.c,v retrieving revision 1.20 diff -u -w -B -b -r1.20 recode.c --- ext/recode/recode.c 11 Dec 2001 15:30:18 -0000 1.20 +++ ext/recode/recode.c 18 Feb 2002 12:00:53 -0000 @@ -26,6 +26,7 @@ #include "php.h" #include "php_recode.h" +#include "php_streams.h" #if HAVE_LIBRECODE #include "ext/standard/info.h" @@ -171,18 +173,34 @@ } in_fp = zend_fetch_resource(input TSRMLS_CC,-1, "File-Handle", &in_type, - 2, php_file_le_fopen(), php_file_le_popen()); + 3, php_file_le_fopen(), php_file_le_popen(), php_file_le_stream()); + if (!in_fp) { php_error(E_WARNING,"Unable to find input file identifier"); RETURN_FALSE; } +#if HAVE_PHP_STREAM + if (in_type == php_file_le_stream()) { + if (!php_stream_cast((php_stream*)in_fp, PHP_STREAM_AS_STDIO, &in_fp, REPORT_ERRORS)) { + RETURN_FALSE; + } + } +#endif + out_fp = zend_fetch_resource(output TSRMLS_CC,-1, "File-Handle", &out_type, - 2, php_file_le_fopen(), php_file_le_popen()); + 3, php_file_le_fopen(), php_file_le_popen(), php_file_le_stream()); if (!out_fp) { php_error(E_WARNING,"Unable to find output file identifier"); RETURN_FALSE; } +#if HAVE_PHP_STREAM + if (out_type == php_file_le_stream()) { + if (!php_stream_cast((php_stream*)out_fp, PHP_STREAM_AS_STDIO, &out_fp, REPORT_ERRORS)) { + RETURN_FALSE; + } + } +#endif convert_to_string_ex(req); Index: ext/standard/basic_functions.c =================================================================== RCS file: /repository/php4/ext/standard/basic_functions.c,v retrieving revision 1.442 diff -u -w -B -b -r1.442 basic_functions.c --- ext/standard/basic_functions.c 10 Feb 2002 17:38:15 -0000 1.442 +++ ext/standard/basic_functions.c 18 Feb 2002 12:00:54 -0000 @@ -20,6 +20,7 @@ /* $Id: basic_functions.c,v 1.442 2002/02/10 17:38:15 jason Exp $ */ #include "php.h" +#include "php_streams.h" #include "php_main.h" #include "php_globals.h" #include "php_ini.h" @@ -274,6 +275,7 @@ PHP_FE(wordwrap, NULL) PHP_FE(htmlspecialchars, NULL) PHP_FE(htmlentities, NULL) + PHP_FE(html_entity_decode, NULL) PHP_FE(get_html_translation_table, NULL) PHP_NAMED_FE(md5,php_if_md5, NULL) PHP_NAMED_FE(md5_file,php_if_md5_file, NULL) @@ -990,6 +992,16 @@ if (PG(allow_url_fopen)) { + +#if HAVE_PHP_STREAM + if (FAILURE == php_register_url_stream_wrapper("http", php_stream_url_wrap_http TSRMLS_CC)) + return FAILURE; +# if HAVE_OPENSSL_EXT + if (FAILURE == php_register_url_stream_wrapper("https", php_stream_url_wrap_http TSRMLS_CC)) + return FAILURE; +# endif +#endif + if (FAILURE == php_register_url_wrapper("http", php_fopen_url_wrap_http TSRMLS_CC)) { return FAILURE; } Index: ext/standard/file.c =================================================================== RCS file: /repository/php4/ext/standard/file.c,v retrieving revision 1.204 diff -u -w -B -b -r1.204 file.c --- ext/standard/file.c 11 Jan 2002 10:44:24 -0000 1.204 +++ ext/standard/file.c 18 Feb 2002 12:00:55 -0000 @@ -17,7 +17,7 @@ | Andi Gutmans | | Zeev Suraski | | PHP 4.0 patches by Thies C. Arntzen (thies@thieso.net) | - | PHP 4.1 streams by Wez Furlong (wez@thebrainroom.com) | + | PHP streams by Wez Furlong (wez@thebrainroom.com) | +----------------------------------------------------------------------+ */ @@ -72,6 +72,7 @@ #endif #include "fsock.h" #include "fopen_wrappers.h" +#include "php_streams.h" #include "php_globals.h" #ifdef HAVE_SYS_FILE_H @@ -135,8 +136,8 @@ static void _file_stream_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { php_stream *stream = (php_stream*)rsrc->ptr; - - php_stream_close(stream); + /* the stream might be a pipe, so set the return value for pclose */ + FG(pclose_ret) = php_stream_close(stream); } #endif @@ -568,6 +569,17 @@ php_error(E_WARNING, "tmpfile(): %s", strerror(errno)); RETURN_FALSE; } +#if HAVE_PHP_STREAM + { + php_stream * stream = php_stream_fopen_from_file(fp, "r+"); + if (stream == NULL) { + zend_error(E_WARNING, "tmpfile(): %s", strerror(errno)); + RETURN_FALSE; + } + ZEND_REGISTER_RESOURCE(return_value, stream, le_stream); + return; + } +#endif ZEND_REGISTER_RESOURCE(return_value, fp, le_fopen); } /* }}} */ @@ -586,7 +598,7 @@ convert_to_string_ex(zfilename); convert_to_string_ex(zmode); - stream = php_stream_fopen(Z_STRVAL_PP(zfilename), Z_STRVAL_PP(zmode)); + stream = php_stream_fopen(Z_STRVAL_PP(zfilename), Z_STRVAL_PP(zmode), NULL TSRMLS_C); if (stream == NULL) { zend_error(E_WARNING, "%s(): unable to open %s: %s", get_active_function_name(TSRMLS_C), Z_STRVAL_PP(zfilename), strerror(errno)); @@ -607,6 +619,9 @@ int *sock; int use_include_path = 0; int issock=0, socketd=0; +#if HAVE_PHP_STREAM + php_stream * stream; +#endif switch(ZEND_NUM_ARGS()) { case 2: @@ -626,6 +641,19 @@ } convert_to_string_ex(arg1); convert_to_string_ex(arg2); + +#if HAVE_PHP_STREAM + stream = php_stream_open_wrapper(Z_STRVAL_PP(arg1), Z_STRVAL_PP(arg2), + use_include_path | ENFORCE_SAFE_MODE | REPORT_ERRORS, + NULL TSRMLS_CC); + if (stream == NULL) { + RETURN_FALSE; + } + FG(fgetss_state) = 0; + ZEND_REGISTER_RESOURCE(return_value, stream, le_stream); + return; +#endif + p = estrndup(Z_STRVAL_PP(arg2), Z_STRLEN_PP(arg2)); /* @@ -742,12 +770,13 @@ { zval **arg1; void *what; + int type; if (ARG_COUNT(ht) != 1 || zend_get_parameters_ex(1, &arg1) == FAILURE) { WRONG_PARAM_COUNT; } - what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", NULL, 1, le_popen); + what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 2, le_popen, le_stream); ZEND_VERIFY_RESOURCE(what); zend_list_delete(Z_LVAL_PP(arg1)); @@ -850,7 +879,19 @@ } #if HAVE_PHP_STREAM else if (type == le_stream) { - if (php_stream_cast((php_stream *) what, PHP_STREAM_AS_SOCKETD, (void *) &socketd, 1) == FAILURE) { + if (php_stream_is((php_stream*)what, PHP_STREAM_IS_SOCKET)) { + /* TODO: check if the blocking mode is changed elsewhere, and see if we + * can integrate these calls into php_stream_sock_set_blocking */ + php_stream_cast((php_stream *) what, PHP_STREAM_AS_SOCKETD, (void *) &socketd, REPORT_ERRORS); + + if (php_set_sock_blocking(socketd, block) == FAILURE) + RETURN_FALSE; + + php_stream_sock_set_blocking((php_stream*)what, block == 0 ? 0 : 1); + RETURN_TRUE; + } + + if (php_stream_cast((php_stream *) what, PHP_STREAM_AS_SOCKETD, (void *) &socketd, REPORT_ERRORS) == FAILURE) { RETURN_FALSE; } } @@ -889,10 +930,9 @@ zend_get_parameters_ex(ZEND_NUM_ARGS(), &socket, &seconds, µseconds)==FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(socket TSRMLS_CC, -1, "File-Handle", &type, 1, le_socket); + + what = zend_fetch_resource(socket TSRMLS_CC, -1, "File-Handle", &type, 2, le_socket, le_stream); ZEND_VERIFY_RESOURCE(what); - socketd = *(int *) what; convert_to_long_ex(seconds); t.tv_sec = Z_LVAL_PP(seconds); @@ -905,6 +945,23 @@ else t.tv_usec = 0; + if (type == le_socket) { + socketd = *(int *) what; + } +#if HAVE_PHP_STREAM + else if (type == le_stream) { + if (php_stream_is((php_stream*)what, PHP_STREAM_IS_SOCKET)) { + php_stream_sock_set_timeout((php_stream*)what, &t); + RETURN_TRUE; + } + + if (php_stream_cast((php_stream *) what, PHP_STREAM_AS_SOCKETD, (void *) &socketd, 1) == FAILURE) { + RETURN_FALSE; + } + } +#endif + + php_sockset_timeout(socketd, &t); RETURN_TRUE; #endif /* HAVE_SYS_TIME_H */ @@ -926,11 +983,24 @@ if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(ZEND_NUM_ARGS(), &socket) == FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(socket TSRMLS_CC, -1, "File-Handle", &type, 1, le_socket); + what = zend_fetch_resource(socket TSRMLS_CC, -1, "File-Handle", &type, 2, le_socket, le_stream); ZEND_VERIFY_RESOURCE(what); + if (type == le_socket) { socketd = *(int *) what; + } +#if HAVE_PHP_STREAM + else if (type == le_stream) { + if (php_stream_is((php_stream*)what, PHP_STREAM_IS_SOCKET)) { + /* TODO: implement */ + RETURN_FALSE; + } + if (php_stream_cast((php_stream *) what, PHP_STREAM_AS_SOCKETD, (void *) &socketd, 1) == FAILURE) { + RETURN_FALSE; + } + } +#endif + sock = php_get_socket(socketd); array_init(return_value); @@ -1356,11 +1426,13 @@ break; } - /* XXX: add stream support --Wez. */ - - what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 2, le_fopen, le_popen); + what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_stream); ZEND_VERIFY_RESOURCE(what); - +#if HAVE_PHP_STREAM + if (type == le_stream && !php_stream_is((php_stream*)what, PHP_STREAM_IS_STDIO)) { + RETURN_FALSE; + } +#endif convert_to_long_ex(arg2); buff = Z_LVAL_PP(arg2); @@ -1381,15 +1453,24 @@ { zval **arg1; void *what; + int type; if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg1) == FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", NULL, 2, le_fopen, le_popen); + what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_stream); ZEND_VERIFY_RESOURCE(what); +#if HAVE_PHP_STREAM + if (type == le_stream) { + if (-1 == php_stream_rewind((php_stream*)what)) { + RETURN_FALSE; + } + RETURN_TRUE; + } +#endif + rewind((FILE *) what); RETURN_TRUE; } @@ -1402,15 +1483,25 @@ zval **arg1; void *what; long ret; + int type; if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg1) == FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", NULL, 2, le_fopen, le_popen); + what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 2, le_fopen, le_popen, le_stream); ZEND_VERIFY_RESOURCE(what); +#if HAVE_PHP_STREAM + if (type == le_stream) { + ret = php_stream_tell((php_stream*)what); + if (ret == -1) { + RETURN_FALSE; + } + RETURN_LONG(ret); + } +#endif + ret = ftell((FILE *) what); if (ret == -1) { RETURN_FALSE; @@ -1427,14 +1518,14 @@ zval **arg1, **arg2, **arg3; int argcount = ZEND_NUM_ARGS(), whence = SEEK_SET; void *what; + int type; if (argcount < 2 || argcount > 3 || zend_get_parameters_ex(argcount, &arg1, &arg2, &arg3) == FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", NULL, 2, le_fopen, le_popen); + what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_stream); ZEND_VERIFY_RESOURCE(what); convert_to_long_ex(arg2); @@ -1443,6 +1534,13 @@ whence = Z_LVAL_PP(arg3); } +#if HAVE_PHP_STREAM + if (type == le_stream) { + RETURN_LONG(php_stream_seek((php_stream*)what, Z_LVAL_PP(arg2), whence)); + } +#endif + + RETURN_LONG(fseek((FILE *) what, Z_LVAL_PP(arg2), whence)); } @@ -1508,13 +1606,59 @@ } /* }}} */ +#if HAVE_PHP_STREAM +static size_t php_passthru_stream(php_stream * stream TSRMLS_DC) +{ + size_t bcount = 0; + int ready = 0; + char buf[8192]; + int fd; + +#ifdef HAVE_MMAP + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET) + && php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0)) + { + struct stat sbuf; + off_t off; + void *p; + size_t len; + + fstat(fd, &sbuf); + + if (sbuf.st_size > sizeof(buf)) { + off = php_stream_tell(stream); + len = sbuf.st_size - off; + p = mmap(0, len, PROT_READ, MAP_SHARED, fd, off); + if (p != (void *) MAP_FAILED) { + BG(mmap_file) = p; + BG(mmap_len) = len; + PHPWRITE(p, len); + BG(mmap_file) = NULL; + munmap(p, len); + bcount += len; + ready = 1; + } + } + } +#endif + if(!ready) { + int b; + + while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) { + PHPWRITE(buf, b); + bcount += b; + } + } + return bcount; +} +#endif + /* {{{ php_passthru_fd */ static size_t php_passthru_fd(int socketd, FILE *fp, int issock TSRMLS_DC) { size_t bcount = 0; int ready = 0; char buf[8192]; - /* XXX: add stream support --Wez. */ #ifdef HAVE_MMAP if (!issock) { @@ -1567,6 +1711,9 @@ int use_include_path=0; int issock=0, socketd=0; int rsrc_id; +#if HAVE_PHP_STREAM + php_stream * stream; +#endif /* check args */ switch (ZEND_NUM_ARGS()) { @@ -1587,6 +1734,17 @@ } convert_to_string_ex(arg1); +#if HAVE_PHP_STREAM + stream = php_stream_open_wrapper(Z_STRVAL_PP(arg1), "rb", + use_include_path | ENFORCE_SAFE_MODE | REPORT_ERRORS, + NULL TSRMLS_CC); + if (stream) { + size = php_passthru_stream(stream TSRMLS_CC); + php_stream_close(stream); + } + RETURN_LONG(size); +#endif + /* * We need a better way of returning error messages from * php_fopen_wrapper(). @@ -1663,10 +1821,17 @@ WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_socket); + what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 4, le_fopen, le_popen, le_socket, le_stream); ZEND_VERIFY_RESOURCE(what); +#if HAVE_PHP_STREAM + if (type == le_stream) { + size = php_passthru_stream((php_stream*)what TSRMLS_CC); + RETURN_LONG(size); + } +#endif + + if (type == le_socket) { issock = 1; socketd = *(int *) what; @@ -1760,17 +1925,32 @@ if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &fp, &size) == FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(fp TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_socket); + what = zend_fetch_resource(fp TSRMLS_CC,-1, "File-Handle", &type, 4, le_fopen, le_popen, le_socket, le_stream); ZEND_VERIFY_RESOURCE(what); - if (type == le_socket) { + convert_to_long_ex(size); + +#if HAVE_PHP_STREAM + if (type == le_stream) { + int fd; + + if (php_stream_is((php_stream*)what, PHP_STREAM_IS_SOCKET)) { php_error(E_WARNING, "can't truncate sockets!"); RETURN_FALSE; } + if (php_stream_cast((php_stream*)what, PHP_STREAM_AS_FD, (void*)&fd, 1)) { + ret = ftruncate(fd, Z_LVAL_PP(size)); + RETURN_LONG(ret + 1); + } + RETURN_FALSE; + } +#endif - convert_to_long_ex(size); + if (type == le_socket) { + php_error(E_WARNING, "can't truncate sockets!"); + RETURN_FALSE; + } ret = ftruncate(fileno((FILE *) what), Z_LVAL_PP(size)); RETURN_LONG(ret + 1); @@ -1787,6 +1967,7 @@ int type; void *what; struct stat stat_sb; + int fd; char *stat_sb_names[13]={"dev", "ino", "mode", "nlink", "uid", "gid", "rdev", "size", "atime", "mtime", "ctime", "blksize", "blocks"}; @@ -1794,12 +1975,25 @@ if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &fp) == FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(fp TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_socket); + what = zend_fetch_resource(fp TSRMLS_CC,-1, "File-Handle", &type, 4, le_fopen, le_popen, le_socket, le_stream); ZEND_VERIFY_RESOURCE(what); - if (fstat(fileno((FILE *) what), &stat_sb)) { +#if HAVE_PHP_STREAM + if (type == le_stream) { + if (!php_stream_cast((php_stream*)what, PHP_STREAM_AS_FD, (void*)&fd, 1)) { + RETURN_FALSE; + } + } + else +#endif + if (type == le_socket) { + fd = *(int*)what; + } + else + fd = fileno((FILE*) what); + + if (fstat(fd, &stat_sb)) { RETURN_FALSE; } @@ -1971,16 +2165,10 @@ if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &arg1, &arg2) == FAILURE) { WRONG_PARAM_COUNT; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_socket); + what = zend_fetch_resource(arg1 TSRMLS_CC,-1, "File-Handle", &type, 4, le_fopen, le_popen, le_socket, le_stream); ZEND_VERIFY_RESOURCE(what); - if (type == le_socket) { - issock = 1; - socketd = *(int *) what; - } - convert_to_long_ex(arg2); len = Z_LVAL_PP(arg2); if (len < 0) { @@ -1988,9 +2176,22 @@ RETURN_FALSE; } + if (type == le_socket) { + issock = 1; + socketd = *(int *) what; + } + Z_STRVAL_P(return_value) = emalloc(len + 1); /* needed because recv doesnt put a null at the end*/ + Z_STRVAL_P(return_value)[Z_STRLEN_P(return_value)] = 0; +#if HAVE_PHP_STREAM + if (type == le_stream) { + php_stream * stream = (php_stream*)what; + Z_STRLEN_P(return_value) = php_stream_read(stream, Z_STRVAL_P(return_value), len); + } + else +#endif if (!issock) { #ifdef HAVE_FLUSHIO if (type == le_fopen) { @@ -2001,7 +2202,6 @@ } else { Z_STRLEN_P(return_value) = SOCK_FREAD(Z_STRVAL_P(return_value), len, socketd); } - Z_STRVAL_P(return_value)[Z_STRLEN_P(return_value)] = 0; if (PG(magic_quotes_runtime)) { Z_STRVAL_P(return_value) = php_addslashes(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), &Z_STRLEN_P(return_value), 1 TSRMLS_CC); @@ -2025,6 +2225,9 @@ int issock=0; int socketd=0; void *what; +#if HAVE_PHP_STREAM + php_stream * stream; +#endif switch(ZEND_NUM_ARGS()) { case 2: @@ -2051,15 +2254,19 @@ /* NOTREACHED */ break; } - /* XXX: add stream support --Wez. */ - what = zend_fetch_resource(fd TSRMLS_CC,-1, "File-Handle", &type, 3, le_fopen, le_popen, le_socket); + what = zend_fetch_resource(fd TSRMLS_CC,-1, "File-Handle", &type, 4, le_fopen, le_popen, le_socket, le_stream); ZEND_VERIFY_RESOURCE(what); if (type == le_socket) { issock = 1; socketd = *(int *) what; } +#if HAVE_PHP_STREAM + if (type == le_stream) { + stream = (php_stream*)what; + } +#endif convert_to_long_ex(bytes); len = Z_LVAL_PP(bytes); @@ -2071,6 +2278,17 @@ buf = emalloc(len + 1); /*needed because recv doesnt set null char at end*/ memset(buf, 0, len + 1); + +#if HAVE_PHP_STREAM + if (type == le_stream) { + if (php_stream_gets(stream, buf, len) == NULL) { + efree(buf); + RETURN_FALSE; + } + } + else +#endif + if (FP_FGETS(buf, len, socketd, (FILE *) what, issock) == NULL) { efree(buf); RETURN_FALSE; @@ -2132,6 +2350,19 @@ /* read a new line from input, as at start of routine */ memset(buf, 0, len+1); + +#if HAVE_PHP_STREAM + if (type == le_stream) { + if (php_stream_gets(stream, buf, len) == NULL) { + efree(lineEnd); + efree(temp); + efree(buf); + zval_ptr_dtor(&return_value); + RETURN_FALSE; + } + } + else +#endif if (FP_FGETS(buf, len, socketd, (FILE *) what, issock) == NULL) { efree(lineEnd); efree(temp); @@ -2218,7 +2449,6 @@ ptr = *buf = emalloc(step); max_len = step; - /* XXX: add stream support --Wez. */ while((ret = FP_FREAD(ptr, max_len - len, socket, fp, issock))) { len += ret; Index: ext/standard/fsock.c =================================================================== RCS file: /repository/php4/ext/standard/fsock.c,v retrieving revision 1.84 diff -u -w -B -b -r1.84 fsock.c --- ext/standard/fsock.c 11 Dec 2001 15:30:31 -0000 1.84 +++ ext/standard/fsock.c 18 Feb 2002 12:00:56 -0000 @@ -152,6 +152,139 @@ /* }}} */ /* {{{ php_fsockopen() */ +#if HAVE_PHP_STREAM +static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) +{ + char * host; + int host_len; + int port = -1; + zval * zerrno = NULL, * zerrstr = NULL; + double timeout = 60; + unsigned long conv; + struct timeval tv; + char * hashkey = NULL; + php_stream * stream = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lzzd", &host, &host_len, &port, &zerrno, &zerrstr, &timeout) == FAILURE) { + RETURN_FALSE; + } + + hashkey = emalloc(host_len + 10); + sprintf(hashkey, "%s:%d", host, port); + +#if 0 + if (persistent && zend_hash_find(&FG(ht_fsock_keys), key, strlen(key) + 1, + (void *) &stream) == SUCCESS) + { + efree(key); + ZEND_REGISTER_RESOURCE(return_value, stream, php_file_le_stream()); + return; + } +#else + persistent = 0; /* don't allow them for the moment */ +#endif + + /* prepare the timeout value for use */ + conv = (unsigned long) (timeout * 1000000.0); + tv.tv_sec = conv / 1000000; + tv.tv_usec = conv % 1000000; + + if (zerrno) { + zval_dtor(zerrno); + ZVAL_LONG(zerrno, 0); + } + if (zerrstr) { + zval_dtor(zerrstr); + ZVAL_STRING(zerrno, "", 1); + } + + if (port != -1) { /* connect to a host */ + enum php_sslflags_t { php_ssl_none, php_ssl_v23, php_ssl_tls }; + enum php_sslflags_t ssl_flags; + struct { + char * proto; + int protolen; + int socktype; + enum php_sslflags_t ssl_flags; + /* more flags to be added here */ + } sockmodes[] = { + { "udp://", 6, SOCK_DGRAM, php_ssl_none }, + { "tcp://", 6, SOCK_STREAM, php_ssl_none }, + { "ssl://", 6, SOCK_STREAM, php_ssl_v23 }, + { "tls://", 6, SOCK_STREAM, php_ssl_tls }, + /* more modes to be added here */ + { NULL, 0, 0 } + }; + int socktype = SOCK_STREAM; + int i; + + for (i = 0; sockmodes[i].proto != NULL; i++) { + if (strncmp(host, sockmodes[i].proto, sockmodes[i].protolen) == 0) { + ssl_flags = sockmodes[i].ssl_flags; + socktype = sockmodes[i].socktype; + host += sockmodes[i].protolen; + break; + } + } +#if !HAVE_OPENSSL_EXT + if (ssl_flags != php_ssl_none) { + zend_error(E_WARNING, "%s(): no SSL support in this build", get_active_function_name(TSRMLS_C)); + } + else +#endif + stream = php_stream_sock_open_host(host, port, socktype, timeout, persistent); + +#if HAVE_OPENSSL_EXT + if (stream) { + int ssl_ret = FAILURE; + switch(ssl_flags) { + case php_ssl_v23: + ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, SSLv23_client_method()); + break; + case php_ssl_tls: + ssl_ret = php_stream_sock_ssl_activate_with_method(stream, 1, TLSv1_client_method()); + break; + default: + /* unknown ?? */ + } + if (ssl_ret == FAILURE) + zend_error(E_WARNING, "failed to activate SSL mode %d", ssl_flags); + } +#endif + + } else + stream = php_stream_sock_open_unix(host, persistent, &tv); + + if (stream && persistent) { +#if 0 + zend_hash_update(&FG(ht_fsock_keys), hashkey, strlen(hashkey) + 1, + &stream, sizeof(stream), NULL); +#endif + } + +out: + + efree(hashkey); + + if (stream == NULL) { + if (zerrno) { + zval_dtor(zerrno); + ZVAL_LONG(zerrno, errno); + } + if (zerrstr) { + zval_dtor(zerrstr); + ZVAL_STRING(zerrno, strerror(errno), 1); + } + + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, stream, php_file_le_stream()); +} + +#endif + + /* This function takes an optional third argument which should be passed by reference. The error code from the connect call is written @@ -288,14 +421,22 @@ Open Internet or Unix domain socket connection */ PHP_FUNCTION(fsockopen) { +#if HAVE_PHP_STREAM + php_fsockopen_stream(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +#else php_fsockopen(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +#endif } /* }}} */ /* {{{ proto int pfsockopen(string hostname, int port [, int errno [, string errstr [, float timeout]]]) Open persistent Internet or Unix domain socket connection */ PHP_FUNCTION(pfsockopen) { +#if HAVE_PHP_STREAM + php_fsockopen_stream(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +#else php_fsockopen(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +#endif } /* }}} */ @@ -633,95 +774,6 @@ return ret; } - -/* {{{ stream abstraction */ -#if HAVE_PHP_STREAM -static size_t php_sockop_write(php_stream * stream, const char * buf, size_t count) -{ - php_sockbuf * sock = (php_sockbuf*)stream->abstract; - return send(sock->socket, buf, count, 0); -} - -static size_t php_sockop_read(php_stream * stream, char * buf, size_t count) -{ - php_sockbuf * sock = (php_sockbuf*)stream->abstract; - size_t ret = 0; - - if (sock->is_blocked) - php_sockread_total(sock, count); - else - php_sockread(sock); - - if(count < 0) - return ret; - - ret = MIN(TOREAD(sock), count); - if (ret) { - memcpy(buf, READPTR(sock), ret); - sock->readpos += ret; - } - - return ret; -} - -static int php_sockop_close(php_stream * stream) -{ - php_sockbuf * sock = (php_sockbuf*)stream->abstract; - - SOCK_CLOSE(sock->socket); - SOCK_DESTROY(sock); - - return 0; -} - -static int php_sockop_flush(php_stream * stream) -{ - php_sockbuf * sock = (php_sockbuf*)stream->abstract; - return fsync(sock->socket); -} - -static int php_sockop_cast(php_stream * stream, int castas, void ** ret) -{ - php_sockbuf * sock = (php_sockbuf*)stream->abstract; - TSRMLS_FETCH(); - - switch(castas) { - case PHP_STREAM_AS_STDIO: - if (ret) { - /* DANGER!: data buffered in stream->readbuf will be forgotten! */ - if (TOREAD(sock) > 0) - zend_error(E_WARNING, "%s(): buffered data lost during conversion to FILE*!", get_active_function_name(TSRMLS_C)); - *ret = fdopen(sock->socket, stream->mode); - if (*ret) - return SUCCESS; - return FAILURE; - } - return SUCCESS; - case PHP_STREAM_AS_FD: - case PHP_STREAM_AS_SOCKETD: - if (ret) - *ret = (void*)sock->socket; - return SUCCESS; - default: - return FAILURE; - } -} - -static char * php_sockop_gets(php_stream * stream, char *buf, size_t size) -{ - php_sockbuf * sock = (php_sockbuf*)stream->abstract; - return php_sock_fgets_internal(buf, size, sock); -} - -php_stream_ops php_stream_socket_ops = { - php_sockop_write, php_sockop_read, - php_sockop_close, php_sockop_flush, - NULL, php_sockop_gets, - php_sockop_cast, - "socket" -}; -#endif -/* }}} */ /* {{{ php_sock_fread() */ Index: ext/standard/fsock.h =================================================================== RCS file: /repository/php4/ext/standard/fsock.h,v retrieving revision 1.40 diff -u -w -B -b -r1.40 fsock.h --- ext/standard/fsock.h 11 Dec 2001 15:30:31 -0000 1.40 +++ ext/standard/fsock.h 18 Feb 2002 12:00:56 -0000 @@ -31,11 +31,6 @@ #include "php_network.h" -#if HAVE_PHP_STREAM -extern php_stream_ops php_stream_socket_ops; -#endif - -/* stream->abstract points to an instance of this */ struct php_sockbuf { int socket; unsigned char *readbuf; @@ -50,9 +45,6 @@ size_t chunk_size; struct timeval timeout; char timeout_event; -#if HAVE_PHP_STREAM - php_stream * stream; -#endif }; typedef struct php_sockbuf php_sockbuf; Index: ext/standard/html.c =================================================================== RCS file: /repository/php4/ext/standard/html.c,v retrieving revision 1.39 diff -u -w -B -b -r1.39 html.c --- ext/standard/html.c 11 Dec 2001 15:30:31 -0000 1.39 +++ ext/standard/html.c 18 Feb 2002 12:00:56 -0000 @@ -23,7 +23,7 @@ #include "php.h" #include "reg.h" #include "html.h" - +#include "php_string.h" #if HAVE_LOCALE_H #include #endif @@ -123,6 +123,21 @@ { NULL } }; +static const struct { + unsigned short charcode; + char * entity; + int entitylen; + int flags; +} basic_entities[] = { + { '&', "&", 5, 0 }, + { '"', """, 6, ENT_HTML_QUOTE_DOUBLE }, + { '\'', "'", 6, ENT_HTML_QUOTE_SINGLE }, + { '<', "<", 4, 0 }, + { '>', ">", 4, 0 }, + { 0, NULL, 0, 0 } +}; + + /* {{{ get_next_char */ inline static unsigned short get_next_char(enum entity_charset charset, @@ -385,19 +400,88 @@ } /* }}} */ +/* {{{ php_unescape_html_entities + */ +PHPAPI char *php_unescape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset) +{ + int i, maxlen, len, retlen; + int j, k; + char *replaced, *ret; + enum entity_charset charset = determine_charset(hint_charset); + int matches_map; + unsigned char replacement[15]; + + ret = estrdup(old); + retlen = oldlen; + + if (all) { + /* look for a match in the maps for this charset */ + for (j=0; entity_map[j].charset != cs_terminator; j++) { + if (entity_map[j].charset != charset) + continue; + + for (k = entity_map[j].basechar; k <= entity_map[j].endchar; k++) { + unsigned char entity[32]; + int entity_length = 0; + + if (entity_map[j].table[k - entity_map[j].basechar] == NULL) + continue; + + + entity[0] = '&'; + entity_length = strlen(entity_map[j].table[k - entity_map[j].basechar]); + strncpy(&entity[1], entity_map[j].table[k - entity_map[j].basechar], sizeof(entity) - 2); + entity[entity_length+1] = ';'; + entity[entity_length+2] = '\0'; + entity_length += 2; + + /* When we have MBCS entities in the tables above, this will need to handle it */ + if (k > 0xff) + zend_error(E_WARNING, "cannot yet handle MBCS in html_entity_decode()!"); + replacement[0] = k; + replacement[1] = '\0'; + + replaced = php_str_to_str(ret, retlen, entity, entity_length, replacement, 1, &retlen); + efree(ret); + ret = replaced; + } + } + } + + for (j = 0; basic_entities[j].charcode != 0; j++) { + + if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0) + continue; + + replacement[0] = basic_entities[j].charcode; + replacement[1] = '\0'; + + replaced = php_str_to_str(ret, retlen, basic_entities[j].entity, basic_entities[j].entitylen, replacement, 1, &retlen); + efree(ret); + ret = replaced; + } + + *newlen = retlen; + return ret; +} +/* }}} */ + + + + /* {{{ php_escape_html_entities */ PHPAPI char *php_escape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset) { - int i, maxlen, len; - char *new; + int i, j, maxlen, len; + char *replaced; enum entity_charset charset = determine_charset(hint_charset); int matches_map; maxlen = 2 * oldlen; if (maxlen < 128) maxlen = 128; - new = emalloc (maxlen); + replaced = emalloc (maxlen); len = 0; i = 0; @@ -409,11 +493,10 @@ matches_map = 0; if (len + 9 > maxlen) - new = erealloc (new, maxlen += 128); + replaced = erealloc (replaced, maxlen += 128); if (all) { /* look for a match in the maps for this charset */ - int j; unsigned char * rep; @@ -435,41 +518,42 @@ } if (matches_map) { - new[len++] = '&'; - strcpy(new + len, rep); + replaced[len++] = '&'; + strcpy(replaced + len, rep); len += strlen(rep); - new[len++] = ';'; + replaced[len++] = ';'; } } if (!matches_map) { - if (38 == this_char) { - memcpy (new + len, "&", 5); - len += 5; - } else if (34 == this_char && !(quote_style&ENT_NOQUOTES)) { - memcpy (new + len, """, 6); - len += 6; - } else if (39 == this_char && (quote_style&ENT_QUOTES)) { - memcpy (new + len, "'", 6); - len += 6; - } else if (60 == this_char) { - memcpy (new + len, "<", 4); - len += 4; - } else if (62 == this_char) { - memcpy (new + len, ">", 4); - len += 4; - } else if (this_char > 0xff) { + int is_basic = 0; + + for (j = 0; basic_entities[j].charcode != 0; j++) { + if ((basic_entities[j].charcode != this_char) || + (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0)) + continue; + + memcpy(replaced + len, basic_entities[j].entity, basic_entities[j].entitylen); + len += basic_entities[j].entitylen; + + is_basic = 1; + break; + + } + if (!is_basic) { + if (this_char > 0xff) { /* a wide char without a named entity; pass through the original sequence */ - memcpy(new + len, mbsequence, mbseqlen); + memcpy(replaced + len, mbsequence, mbseqlen); len += mbseqlen; - } else { - new [len++] = (unsigned char)this_char; + + } else + replaced [len++] = (unsigned char)this_char; } } } - new [len] = '\0'; + replaced [len] = '\0'; *newlen = len; - return new; + return replaced; } @@ -481,15 +565,15 @@ { char *str, *hint_charset = NULL; int str_len, hint_charset_len, len, quote_style = ENT_COMPAT; - char *new; + char *replaced; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, "e_style, &hint_charset, &hint_charset_len) == FAILURE) { return; } - new = php_escape_html_entities(str, str_len, &len, all, quote_style, hint_charset); - RETVAL_STRINGL(new, len, 0); + replaced = php_escape_html_entities(str, str_len, &len, all, quote_style, hint_charset); + RETVAL_STRINGL(replaced, len, 0); } /* }}} */ @@ -516,6 +600,25 @@ } /* }}} */ +/* {{{ proto string html_entity_decode(string string [, int quote_style][, string charset]) + Convert all applicable characters to HTML entities */ +PHP_FUNCTION(html_entity_decode) +{ + char *str, *hint_charset = NULL; + int str_len, hint_charset_len, len, quote_style = ENT_COMPAT; + char *replaced; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len, + "e_style, &hint_charset, &hint_charset_len) == FAILURE) { + return; + } + + replaced = php_unescape_html_entities(str, str_len, &len, 1, quote_style, hint_charset); + RETVAL_STRINGL(replaced, len, 0); +} +/* }}} */ + + /* {{{ proto string htmlentities(string string [, int quote_style][, string charset]) Convert all applicable characters to HTML entities */ PHP_FUNCTION(htmlentities) @@ -562,15 +665,14 @@ /* break thru */ case HTML_SPECIALCHARS: - ind[0]=38; add_assoc_string(return_value, ind, "&", 1); - if(quote_style&ENT_QUOTES) { - ind[0]=39; add_assoc_string(return_value, ind, "'", 1); - } - if(!(quote_style&ENT_NOQUOTES)) { - ind[0]=34; add_assoc_string(return_value, ind, """, 1); + for (j = 0; basic_entities[j].charcode != 0; j++) { + + if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0) + continue; + + ind[0] = basic_entities[j].charcode; + add_assoc_string(return_value, ind, basic_entities[j].entity, 1); } - ind[0]=60; add_assoc_string(return_value, ind, "<", 1); - ind[0]=62; add_assoc_string(return_value, ind, ">", 1); break; } } Index: ext/standard/html.h =================================================================== RCS file: /repository/php4/ext/standard/html.h,v retrieving revision 1.11 diff -u -w -B -b -r1.11 html.h --- ext/standard/html.h 11 Dec 2001 15:30:31 -0000 1.11 +++ ext/standard/html.h 18 Feb 2002 12:00:56 -0000 @@ -21,14 +21,19 @@ #ifndef HTML_H #define HTML_H -#define ENT_COMPAT 1 -#define ENT_QUOTES 2 -#define ENT_NOQUOTES 4 +#define ENT_HTML_QUOTE_NONE 0 +#define ENT_HTML_QUOTE_SINGLE 1 +#define ENT_HTML_QUOTE_DOUBLE 2 + +#define ENT_COMPAT ENT_HTML_QUOTE_DOUBLE +#define ENT_QUOTES (ENT_HTML_QUOTE_DOUBLE | ENT_HTML_QUOTE_SINGLE) +#define ENT_NOQUOTES ENT_HTML_QUOTE_NONE void register_html_constants(INIT_FUNC_ARGS); PHP_FUNCTION(htmlspecialchars); PHP_FUNCTION(htmlentities); +PHP_FUNCTION(html_entity_decode); PHP_FUNCTION(get_html_translation_table); PHPAPI char *php_escape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char * hint_charset); Index: ext/standard/http_fopen_wrapper.c =================================================================== RCS file: /repository/php4/ext/standard/http_fopen_wrapper.c,v retrieving revision 1.22 diff -u -w -B -b -r1.22 http_fopen_wrapper.c --- ext/standard/http_fopen_wrapper.c 23 Dec 2001 00:46:13 -0000 1.22 +++ ext/standard/http_fopen_wrapper.c 18 Feb 2002 12:00:57 -0000 @@ -15,12 +15,14 @@ | Authors: Rasmus Lerdorf | | Jim Winstead | | Hartmut Holzgraefe | + | Wez Furlong | +----------------------------------------------------------------------+ */ /* $Id: http_fopen_wrapper.c,v 1.22 2001/12/23 00:46:13 alexwaugh Exp $ */ #include "php.h" #include "php_globals.h" +#include "php_streams.h" #include "php_network.h" #include @@ -66,7 +68,243 @@ #include "php_fopen_wrappers.h" -#define HTTP_HEADER_BLOCK_SIZE 128 +#define HTTP_HEADER_BLOCK_SIZE 1024 + +#if HAVE_PHP_STREAM + +php_stream * php_stream_url_wrap_http(char * path, char * mode, int options, char ** opened_path TSRMLS_DC) +{ + php_stream * stream = NULL; + php_url * resource = NULL; + int use_ssl; + char * scratch = NULL; + char * tmp = NULL; + int scratch_len = 0; + int body = 0; + char location[HTTP_HEADER_BLOCK_SIZE]; + zval * response_header = NULL; + int reqok = 0; + char * http_header_line = NULL; + + resource = php_url_parse(path); + if (resource == NULL) + return NULL; + + use_ssl = resource->scheme && resource->scheme[4] == 's'; + + /* choose default ports */ + if (use_ssl && resource->port == 0) + resource->port = 443; + else if (resource->port == 0) + resource->port = 80; + + stream = php_stream_sock_open_host(resource->host, resource->port, SOCK_STREAM, 0, 0); + if (stream == NULL) + goto out; + +#if HAVE_OPENSSL_EXT + if (use_ssl) { + if (php_stream_sock_ssl_activate(stream, 1) == FAILURE) { + if (options & REPORT_ERRORS) { + zend_error(E_WARNING, "Unable to activate SSL mode"); + } + php_stream_close(stream); + stream = NULL; + goto out; + } + } +#endif + + scratch_len = strlen(path) + 32; + scratch = emalloc(scratch_len); + + strcpy(scratch, "GET "); + + /* file */ + if (resource->path && *resource->path) + strlcat(scratch, resource->path, scratch_len); + else + strlcat(scratch, "/", scratch_len); + + /* query string */ + if (resource->query) { + strlcat(scratch, "?", scratch_len); + strlcat(scratch, resource->query, scratch_len); + } + + /* protocol version we are speaking */ + strlcat(scratch, " HTTP/1.0\r\n", scratch_len); + + /* send it */ + php_stream_write(stream, scratch, strlen(scratch)); + + /* authz header if it was specified */ + if (resource->user && resource->pass) { + /* scratch is large enough, since it was made large enough for the whole URL */ + strcpy(scratch, resource->user); + strcat(scratch, ":"); + strcat(scratch, resource->pass); + + tmp = php_base64_encode((unsigned char*)scratch, strlen(scratch), NULL); + + if (snprintf(scratch, scratch_len, "Authorization: Basic %s\r\n", tmp) > 0) + php_stream_write(stream, scratch, strlen(scratch)); + + efree(tmp); + tmp = NULL; + } + + /* if the user has configured who they are, send a From: line */ + if (cfg_get_string("from", &tmp) == SUCCESS) { + if (snprintf(scratch, scratch_len, "From: %s\r\n", tmp) > 0) + php_stream_write(stream, scratch, strlen(scratch)); + } + + /* Send Host: header so name-based virtual hosts work */ + if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) { + if (snprintf(scratch, scratch_len, "Host: %s:%i\r\n", resource->host, resource->port) > 0) + php_stream_write(stream, scratch, strlen(scratch)); + } + else if (snprintf(scratch, scratch_len, "Host: %s\r\n", resource->host) > 0) + php_stream_write(stream, scratch, strlen(scratch)); + + php_stream_write_string(stream, "User-Agent: PHP/" PHP_VERSION "\r\n\r\n"); + + location[0] = '\0'; + + MAKE_STD_ZVAL(response_header); + array_init(response_header); + + if (!php_stream_eof(stream)) { + /* get response header */ + char tmp_line[128]; + + if (php_stream_gets(stream, tmp_line, sizeof(tmp_line)-1) != NULL) { + zval * http_response; + + MAKE_STD_ZVAL(http_response); + if (strncmp(tmp_line + 8, " 200 ", 5) == 0) + reqok = 1; + Z_STRLEN_P(http_response) = strlen(tmp_line); + Z_STRVAL_P(http_response) = estrndup(tmp_line, Z_STRLEN_P(http_response)); + if (Z_STRVAL_P(http_response)[Z_STRLEN_P(http_response)-1]=='\n') { + Z_STRVAL_P(http_response)[Z_STRLEN_P(http_response)-1]=0; + Z_STRLEN_P(http_response)--; + if (Z_STRVAL_P(http_response)[Z_STRLEN_P(http_response)-1]=='\r') { + Z_STRVAL_P(http_response)[Z_STRLEN_P(http_response)-1]=0; + Z_STRLEN_P(http_response)--; + } + } + Z_TYPE_P(http_response) = IS_STRING; + zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_response, sizeof(zval *), NULL); + } + } + + /* read past HTTP headers */ + + http_header_line = emalloc(HTTP_HEADER_BLOCK_SIZE); + + while (!body && !php_stream_eof(stream)) { + + if (php_stream_gets(stream, http_header_line, HTTP_HEADER_BLOCK_SIZE-1) != NULL) { + char * p; + int found_eol = 0; + int http_header_line_length; + + http_header_line[HTTP_HEADER_BLOCK_SIZE-1] = '\0'; + p = http_header_line; + while(*p) { + while(*p == '\n' || *p == '\r') { + *p = '\0'; + p--; + found_eol = 1; + } + if (found_eol) + break; + p++; + } + http_header_line_length = p-http_header_line+1; + + if (!strncasecmp(http_header_line, "Location: ", 10)) + strlcpy(location, http_header_line + 10, sizeof(location)); + + if (http_header_line[0] == '\0') + body = 1; + else { + zval * http_header; + + MAKE_STD_ZVAL(http_header); + + ZVAL_STRINGL(http_header, http_header_line, http_header_line_length, 1); + + zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header, sizeof(zval *), NULL); + } + } + else + break; + } + + if (!reqok) { + php_stream_close(stream); + stream = NULL; + + if (location[0] != '\0') { + + zval **response_header_new, *entry, **entryp; + char new_path[HTTP_HEADER_BLOCK_SIZE]; + + *new_path='\0'; + if (strlen(location)<8 || (strncasecmp(location, "http://", 7) && strncasecmp(location, "https://", 8))) { + strcpy(new_path, resource->scheme); + strlcat(new_path, resource->host, sizeof(new_path)); + if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) { + snprintf(new_path+strlen(new_path), sizeof(new_path)-strlen(new_path)-1, ":%d", resource->port); + } + if (*location != '/') { + php_dirname(resource->path, strlen(resource->path)); + snprintf (new_path+strlen(new_path), sizeof(new_path)-strlen(new_path)-1, "%s/", resource->path); + } + strlcat(new_path, location, sizeof(new_path)); + } + else { + strlcpy(new_path, location, sizeof(new_path)); + } + stream = php_stream_url_wrap_http(new_path, mode, options, opened_path TSRMLS_CC); + if (zend_hash_find(EG(active_symbol_table), "http_response_header", + sizeof("http_response_header"), (void **) &response_header_new) == SUCCESS) { + entryp = &entry; + MAKE_STD_ZVAL(entry); + ZVAL_EMPTY_STRING(entry); + zend_hash_next_index_insert(Z_ARRVAL_P(response_header), entryp, sizeof(zval *), NULL); + zend_hash_internal_pointer_reset(Z_ARRVAL_PP(response_header_new)); + while (zend_hash_get_current_data(Z_ARRVAL_PP(response_header_new), (void **)&entryp) == SUCCESS) { + zval_add_ref(entryp); + zend_hash_next_index_insert(Z_ARRVAL_P(response_header), entryp, sizeof(zval *), NULL); + zend_hash_move_forward(Z_ARRVAL_PP(response_header_new)); + } + } + } + else if (options & REPORT_ERRORS) + zend_error(E_WARNING, "HTTP request failed!"); + } +out: + if (http_header_line) + efree(http_header_line); + if (scratch) + efree(scratch); + php_url_free(resource); + + if (response_header) + ZEND_SET_SYMBOL(EG(active_symbol_table), "http_response_header", response_header); + + return stream; +} + + + +#endif + + /* {{{ php_fopen_url_wrap_http */ Index: ext/standard/php_fopen_wrappers.h =================================================================== RCS file: /repository/php4/ext/standard/php_fopen_wrappers.h,v retrieving revision 1.5 diff -u -w -B -b -r1.5 php_fopen_wrappers.h --- ext/standard/php_fopen_wrappers.h 11 Dec 2001 15:30:35 -0000 1.5 +++ ext/standard/php_fopen_wrappers.h 18 Feb 2002 12:00:57 -0000 @@ -27,4 +27,8 @@ FILE *php_fopen_url_wrap_ftp(const char *, char *, int, int *, int *, char ** TSRMLS_DC); FILE *php_fopen_url_wrap_php(const char *, char *, int, int *, int *, char ** TSRMLS_DC); +#if HAVE_PHP_STREAM +php_stream * php_stream_url_wrap_http(char * path, char * mode, int options, char ** opened_path TSRMLS_DC); +#endif + #endif Index: main/fopen_wrappers.h =================================================================== RCS file: /repository/php4/main/fopen_wrappers.h,v retrieving revision 1.32 diff -u -w -B -b -r1.32 fopen_wrappers.h --- main/fopen_wrappers.h 1 Feb 2002 20:04:14 -0000 1.32 +++ main/fopen_wrappers.h 18 Feb 2002 12:01:00 -0000 @@ -33,6 +33,8 @@ #endif #define ENFORCE_SAFE_MODE 4 +#define REPORT_ERRORS 8 + #ifdef PHP_WIN32 # define SOCK_ERR INVALID_SOCKET # define SOCK_CONN_ERR SOCKET_ERROR Index: main/main.c =================================================================== RCS file: /repository/php4/main/main.c,v retrieving revision 1.422 diff -u -w -B -b -r1.422 main.c --- main/main.c 14 Feb 2002 20:16:05 -0000 1.422 +++ main/main.c 18 Feb 2002 12:01:00 -0000 @@ -73,6 +73,7 @@ #include "php_content_types.h" #include "php_ticks.h" #include "php_logos.h" +#include "php_streams.h" #include "SAPI.h" /* }}} */ @@ -900,6 +901,16 @@ return FAILURE; } +#if HAVE_PHP_STREAM + /* initialize stream wrappers registry + * (this uses configuration parameters from php.ini) + */ + if (php_init_stream_wrappers(TSRMLS_C) == FAILURE) { + php_printf("PHP: Unable to initialize stream url wrappers.\n"); + return FAILURE; + } +#endif + /* initialize registry for images to be used in phpinfo() (this uses configuration parameters from php.ini) */ @@ -998,6 +1009,11 @@ sapi_flush(TSRMLS_C); zend_shutdown(TSRMLS_C); + +#if HAVE_PHP_STREAM + php_shutdown_stream_wrappers(TSRMLS_C); +#endif + php_shutdown_fopen_wrappers(TSRMLS_C); php_shutdown_info_logos(); UNREGISTER_INI_ENTRIES(); Index: main/network.c =================================================================== RCS file: /repository/php4/main/network.c,v retrieving revision 1.29 diff -u -w -B -b -r1.29 network.c --- main/network.c 12 Feb 2002 11:00:15 -0000 1.29 +++ main/network.c 18 Feb 2002 12:01:00 -0000 @@ -17,6 +17,9 @@ */ /* $Id: network.c,v 1.29 2002/02/12 11:00:15 sas Exp $ */ +#define PHP_SOCK_CHUNK_SIZE 8192 +#define MAX_CHUNKS_PER_READ 10 + #include "php.h" #ifdef PHP_WIN32 @@ -407,6 +410,396 @@ } } /* }}} */ + +#if HAVE_PHP_STREAM + +PHPAPI php_stream * php_stream_sock_open_from_socket(int socket, int persistent) +{ + php_stream * stream; + php_netstream_data_t * sock; + + sock = pemalloc(sizeof(php_netstream_data_t), persistent); + memset(sock, 0, sizeof(php_netstream_data_t)); + + sock->is_blocked = 1; + sock->chunk_size = PHP_SOCK_CHUNK_SIZE; + sock->timeout.tv_sec = -1; + sock->socket = socket; + + stream = php_stream_alloc(&php_stream_socket_ops, sock, persistent, "r+"); + + if (stream == NULL) + pefree(sock, persistent); + + return stream; +} + +PHPAPI php_stream * php_stream_sock_open_host(const char * host, unsigned short port, + int socktype, int timeout, int persistent) +{ + int socket; + + socket = php_hostconnect(host, port, socktype, timeout); + + if (socket == -1) + return NULL; + + return php_stream_sock_open_from_socket(socket, persistent); +} + +PHPAPI php_stream * php_stream_sock_open_unix(const char * path, int persistent, struct timeval * timeout) +{ +#if defined(AF_UNIX) + int socketd; + struct sockaddr_un unix_addr; + + socketd = socket(PF_UNIX, SOCK_STREAM, 0); + if (socketd == SOCK_ERR) + return NULL; + + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strlcpy(unix_addr.sun_path, path, sizeof(unix_addr.sun_path)); + + if (php_connect_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), timeout) == SOCK_CONN_ERR) + return NULL; + + return php_stream_sock_open_from_socket(socketd, persistent); +#else + return NULL; +#endif +} + +#if HAVE_OPENSSL_EXT +PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream * stream, int activate, SSL_METHOD * method) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + SSL_CTX * ctx = NULL; + + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) + return FAILURE; + + if (activate == sock->ssl_active) + return SUCCESS; /* already in desired mode */ + + if (activate && sock->ssl_handle == NULL) { + ctx = SSL_CTX_new(method); + if (ctx == NULL) + return FAILURE; + + sock->ssl_handle = SSL_new(ctx); + if (sock->ssl_handle == NULL) { + SSL_CTX_free(ctx); + return FAILURE; + } + + SSL_set_fd(sock->ssl_handle, sock->socket); + } + + if (activate) { + if (SSL_connect(sock->ssl_handle) <= 0) { + SSL_shutdown(sock->ssl_handle); + return FAILURE; + } + sock->ssl_active = activate; + } + else { + SSL_shutdown(sock->ssl_handle); + sock->ssl_active = 0; + } + return SUCCESS; +} +#endif + +PHPAPI void php_stream_sock_set_timeout(php_stream * stream, struct timeval *timeout) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) + return; + + sock->timeout = *timeout; + sock->timeout_event = 0; +} + +PHPAPI int php_stream_sock_set_blocking(php_stream * stream, int mode) +{ + int oldmode; + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) + return 0; + + oldmode = sock->is_blocked; + sock->is_blocked = mode; + + return oldmode; +} + + +#define TOREAD(sock) ((sock)->writepos - (sock)->readpos) +#define READPTR(sock) ((sock)->readbuf + (sock)->readpos) +#define WRITEPTR(sock) ((sock)->readbuf + (sock)->writepos) + +static size_t php_sockop_write(php_stream * stream, const char * buf, size_t count) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + return SSL_write(sock->ssl_handle, buf, count); +#endif + return send(sock->socket, buf, count, 0); +} +static void php_sock_stream_wait_for_data(php_stream * stream, php_netstream_data_t * sock) +{ + fd_set fdr, tfdr; + int retval; + struct timeval timeout, *ptimeout; + + FD_ZERO(&fdr); + FD_SET(sock->socket, &fdr); + sock->timeout_event = 0; + + if (sock->timeout.tv_sec == -1) + ptimeout = NULL; + else + ptimeout = &timeout; + + while(1) { + tfdr = fdr; + timeout = sock->timeout; + + retval = select(sock->socket + 1, &tfdr, NULL, NULL, ptimeout); + + if (retval == 0) + sock->timeout_event = 1; + + if (retval >= 0) + break; + } +} + +static size_t php_sock_stream_read_internal(php_stream * stream, php_netstream_data_t * sock) +{ + char buf[PHP_SOCK_CHUNK_SIZE]; + int nr_bytes; + size_t nr_read = 0; + + /* For blocking sockets, we wait until there is some + data to read (real data or EOF) + + Otherwise, recv() may time out and return 0 and + therefore sock->eof would be set errornously. + */ + + + if(sock->is_blocked) { + php_sock_stream_wait_for_data(stream, sock); + if (sock->timeout_event) + return 0; + } + + /* read at a maximum sock->chunk_size */ +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + nr_bytes = SSL_read(sock->ssl_handle, buf, sock->chunk_size); + else +#endif + nr_bytes = recv(sock->socket, buf, sock->chunk_size, 0); + if(nr_bytes > 0) { + if(sock->writepos + nr_bytes > sock->readbuflen) { + sock->readbuflen += sock->chunk_size; + sock->readbuf = perealloc(sock->readbuf, sock->readbuflen, + php_stream_is_persistent(stream)); + } + memcpy(WRITEPTR(sock), buf, nr_bytes); + sock->writepos += nr_bytes; + nr_read = nr_bytes; + } else if(nr_bytes == 0 || (nr_bytes < 0 && errno != EWOULDBLOCK)) { + sock->eof = 1; + } + + return nr_read; + +} + +static size_t php_sock_stream_read(php_stream * stream, php_netstream_data_t * sock) +{ + size_t nr_bytes; + size_t nr_read = 0; + int i; + + for(i = 0; !sock->eof && i < MAX_CHUNKS_PER_READ; i++) { + nr_bytes = php_sock_stream_read_internal(stream, sock); + if(nr_bytes == 0) break; + nr_read += nr_bytes; + } + + return nr_read; +} + +static size_t php_sockop_read(php_stream * stream, char * buf, size_t count) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + size_t ret = 0; + + if (sock->is_blocked) { + while(!sock->eof && TOREAD(sock) < count && !sock->timeout_event) + php_sock_stream_read_internal(stream, sock); + } + else + php_sock_stream_read(stream, sock); + + if(count < 0) + return ret; + + ret = MIN(TOREAD(sock), count); + if (ret) { + memcpy(buf, READPTR(sock), ret); + sock->readpos += ret; + } + + return ret; +} + +static int php_sockop_close(php_stream * stream) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) { + SSL_shutdown(sock->ssl_handle); + sock->ssl_active = 0; + SSL_free(sock->ssl_handle); + sock->ssl_handle = NULL; + } +#endif + + shutdown(sock->socket, 0); + closesocket(sock->socket); + + if (sock->readbuf) + pefree(sock->readbuf, php_stream_is_persistent(stream)); + + pefree(sock, php_stream_is_persistent(stream)); + + return 0; +} + +static int php_sockop_flush(php_stream * stream) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + return fsync(sock->socket); +} + +static int php_sockop_cast(php_stream * stream, int castas, void ** ret) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + TSRMLS_FETCH(); + + switch(castas) { + case PHP_STREAM_AS_STDIO: +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + return FAILURE; +#endif + if (ret) { + /* DANGER!: data buffered in stream->readbuf will be forgotten! */ + if (TOREAD(sock) > 0) + zend_error(E_WARNING, "%s(): buffered data lost during conversion to FILE*!", get_active_function_name(TSRMLS_C)); + *ret = fdopen(sock->socket, stream->mode); + if (*ret) + return SUCCESS; + return FAILURE; + } + return SUCCESS; + case PHP_STREAM_AS_FD: + case PHP_STREAM_AS_SOCKETD: +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + return FAILURE; +#endif + if (ret) + *ret = (void*)sock->socket; + return SUCCESS; + default: + return FAILURE; + } +} + +#define SEARCHCR() do { \ + if (TOREAD(sock)) { \ + for (p = READPTR(sock), pe = p + MIN(TOREAD(sock), maxlen); \ + *p != '\n'; ) \ + if (++p >= pe) { \ + p = NULL; \ + break; \ + } \ + } else \ + p = NULL; \ +} while (0) + + +static char * php_sockop_gets(php_stream * stream, char *buf, size_t maxlen) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + char *p = NULL, *pe; + char *ret = NULL; + size_t amount = 0; + + if (maxlen==0) { + buf[0] = 0; + return buf; + } + + SEARCHCR(); + + if(!p) { + if(sock->is_blocked) { + while(!p && !sock->eof && !sock->timeout_event && TOREAD(sock) < maxlen) { + php_sock_stream_read_internal(stream, sock); + SEARCHCR(); + } + } else { + php_sock_stream_read(stream, sock); + SEARCHCR(); + } + } + + if(p) { + amount = (ptrdiff_t) p - (ptrdiff_t) READPTR(sock) + 1; + } else { + amount = TOREAD(sock); + } + + amount = MIN(amount, maxlen); + + if(amount > 0) { + memcpy(buf, READPTR(sock), amount); + sock->readpos += amount; + } + buf[amount] = '\0'; + + /* signal error only, if we don't return data from this call and + if there is no data to read and if the eof flag is set */ + if(amount || TOREAD(sock) || !sock->eof) { + ret = buf; + } + + return ret; +} + +php_stream_ops php_stream_socket_ops = { + php_sockop_write, php_sockop_read, + php_sockop_close, php_sockop_flush, + NULL, php_sockop_gets, + php_sockop_cast, + "socket" +}; + + +#endif + /* * Local variables: Index: main/php_network.h =================================================================== RCS file: /repository/php4/main/php_network.h,v retrieving revision 1.11 diff -u -w -B -b -r1.11 php_network.h --- main/php_network.h 10 Feb 2002 12:35:29 -0000 1.11 +++ main/php_network.h 18 Feb 2002 12:01:01 -0000 @@ -27,8 +27,17 @@ # undef FD_SETSIZE # include "arpa/inet.h" # define socklen_t unsigned int +#else +# undef closesocket +# define closesocket close +#endif + +#ifndef HAVE_SHUTDOWN +#undef shutdown +#define shutdown(s,n) /* nothing */ #endif + #ifdef HAVE_NETINET_IN_H # include #endif @@ -41,6 +50,10 @@ #include #endif +#if HAVE_OPENSSL_EXT +#include +#endif + #ifdef HAVE_SOCKADDR_STORAGE typedef struct sockaddr_storage php_sockaddr_storage; #else @@ -55,6 +68,45 @@ PHPAPI int php_connect_nonb(int sockfd, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout); void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port); int php_sockaddr_size(php_sockaddr_storage *addr); + +#if HAVE_PHP_STREAM +struct _php_netstream_data_t { + int socket; + unsigned char *readbuf; + size_t readbuflen; + size_t readpos; + size_t writepos; + char eof; + char is_blocked; + size_t chunk_size; + struct timeval timeout; + char timeout_event; +#if HAVE_OPENSSL_EXT + /* openssl specific bits here */ + SSL * ssl_handle; + int ssl_active; +#endif +}; +typedef struct _php_netstream_data_t php_netstream_data_t; + +extern php_stream_ops php_stream_socket_ops; +#define PHP_STREAM_IS_SOCKET (&php_stream_socket_ops) + +PHPAPI php_stream * php_stream_sock_open_from_socket(int socket, int persistent); +/* open a connection to a host using php_hostconnect and return a stream */ +PHPAPI php_stream * php_stream_sock_open_host(const char * host, unsigned short port, + int socktype, int timeout, int persistent); +PHPAPI php_stream * php_stream_sock_open_unix(const char * path, int persistent, struct timeval * timeout); + +PHPAPI void php_stream_sock_set_timeout(php_stream * stream, struct timeval *timeout); +PHPAPI int php_stream_sock_set_blocking(php_stream * stream, int mode); + +#if HAVE_OPENSSL_EXT +PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream * stream, int activate, SSL_METHOD * method); +#define php_stream_sock_ssl_activate(stream, activate) php_stream_sock_ssl_activate_with_method((stream), (activate), SSLv23_client_method()) + +#endif +#endif #endif /* _PHP_NETWORK_H */ Index: main/php_streams.h =================================================================== RCS file: /repository/php4/main/php_streams.h,v retrieving revision 1.5 diff -u -w -B -b -r1.5 php_streams.h --- main/php_streams.h 11 Dec 2001 15:31:05 -0000 1.5 +++ main/php_streams.h 18 Feb 2002 12:01:01 -0000 @@ -69,15 +69,19 @@ PHPAPI off_t php_stream_tell(php_stream * stream); PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count); PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count); +#define php_stream_write_string(stream, str) php_stream_write(stream, str, strlen(str)) PHPAPI int php_stream_eof(php_stream * stream); PHPAPI int php_stream_getc(php_stream * stream); PHPAPI int php_stream_flush(php_stream * stream); PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen); -/* operations for a stdio FILE; the FILE * must be placed in stream->abstract */ +/* operations for a stdio FILE; use the php_stream_fopen_XXX funcs below */ extern php_stream_ops php_stream_stdio_ops; /* like fopen, but returns a stream */ -PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode); +PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode, char **opened_path TSRMLS_DC); +PHPAPI php_stream * php_stream_fopen_with_path(char * filename, char * mode, char * path, char **opened_path TSRMLS_DC); +PHPAPI php_stream * php_stream_fopen_from_file(FILE * file, const char * mode); +PHPAPI php_stream * php_stream_fopen_from_pipe(FILE * file, const char * mode); /* coerce the stream into some other form */ /* cast as a stdio FILE * */ @@ -93,7 +97,21 @@ /* use this to check if a stream is of a particular type: * PHPAPI int php_stream_is(php_stream * stream, php_stream_ops * ops); */ -#define php_stream_is(stream, anops) (stream->ops == anops) +#define php_stream_is(stream, anops) ((stream)->ops == anops) +#define PHP_STREAM_IS_STDIO &php_stream_stdio_ops + +#define php_stream_is_persistent(stream) (stream)->is_persistent + +/* Wrappers support */ +/* options uses the IGNORE_URL family of defines from fopen_wrappers.h */ +typedef php_stream * (*php_stream_factory_func_t)(char * filename, char * mode, int options, char ** opened_path TSRMLS_DC); + +int php_init_stream_wrappers(TSRMLS_D); +int php_shutdown_stream_wrappers(TSRMLS_D); +PHPAPI int php_register_url_stream_wrapper(char * protocol, php_stream_factory_func_t wrapper TSRMLS_DC); +PHPAPI int php_unregister_url_stream_wrapper(char * protocol, php_stream_factory_func_t wrapper TSRMLS_DC); + +PHPAPI php_stream * php_stream_open_wrapper(char * path, char * mode, int options, char ** opened_path TSRMLS_DC); #endif /* HAVE_PHP_STREAM */ Index: main/streams.c =================================================================== RCS file: /repository/php4/main/streams.c,v retrieving revision 1.8 diff -u -w -B -b -r1.8 streams.c --- main/streams.c 11 Dec 2001 15:31:05 -0000 1.8 +++ main/streams.c 18 Feb 2002 12:01:01 -0000 @@ -14,11 +14,18 @@ +----------------------------------------------------------------------+ | Authors: | | Wez Furlong (wez@thebrainroom.com) | + | Borrowed code from: | + | Rasmus Lerdorf | + | Jim Winstead | +----------------------------------------------------------------------+ */ +/* define this to allow us to fallback on older fopen wrapper handlers */ +#define STREAMS_SUPPORT_FOPEN_WRAPPER 1 + #define _GNU_SOURCE #include "php.h" +#include "php_globals.h" #if HAVE_PHP_STREAM @@ -28,18 +35,10 @@ #include "build-defs.h" #endif -#define MAX_CHUNK_SIZE 8192 - -#define TOREAD(stream) ((stream)->readbuf.writepos - (stream)->readbuf.readpos) -#define TOWRITE(stream) ((stream)->readbuf.writepos - (stream)->readbuf.readpos) - -#define READPTR(stream) ((stream)->readbuf.buffer + (stream)->readbuf.readpos) -#define WRITEPTR(stream) ((stream)->readbuf.buffer + (stream)->readbuf.writepos) - -#define READ_MAX(stream, max) if (stream->is_blocked) stream_read_total(sock, max); else stream_readahead(sock) +static HashTable url_stream_wrappers_hash; /* allocate a new stream for a particular ops */ -PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, int persistent, const char * mode) +PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, int persistent, const char * mode) /* {{{ */ { php_stream * ret; @@ -55,8 +54,9 @@ return ret; } +/* }}} */ -PHPAPI int php_stream_free(php_stream * stream, int call_dtor) +PHPAPI int php_stream_free(php_stream * stream, int call_dtor) /* {{{ */ { int ret = 1; @@ -87,7 +87,9 @@ return ret; } +/* }}} */ +/* {{{ generic stream operations */ PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t size) { return stream->ops->read(stream, buf, size); @@ -148,13 +150,6 @@ PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count) { - if (strchr(stream->mode, 'w') == NULL) { - TSRMLS_FETCH(); - - zend_error(E_WARNING, "%s(): stream was not opened for writing", get_active_function_name(TSRMLS_C)); - return 0; - } - return stream->ops->write(stream, buf, count); } @@ -175,44 +170,101 @@ zend_error(E_WARNING, "streams of type %s do not support seeking", stream->ops->label); return -1; } +/* }}} */ + + + +/* {{{ ------- STDIO stream implementation -------*/ -/*------- STDIO stream implementation -------*/ +typedef struct { + FILE * file; + int is_pipe; /* use pclose */ +#if HAVE_FLUSHIO + char last_op; +#endif +} php_stdio_stream_data; + +PHPAPI php_stream * php_stream_fopen_from_file(FILE * file, const char * mode) +{ + php_stdio_stream_data * self; + self = emalloc(sizeof(*self)); + self->file = file; + self->is_pipe = 0; + return php_stream_alloc(&php_stream_stdio_ops, self, 0, mode); +} + +PHPAPI php_stream * php_stream_fopen_from_pipe(FILE * file, const char * mode) +{ + php_stdio_stream_data * self; + + self = emalloc(sizeof(*self)); + self->file = file; + self->is_pipe = 1; + return php_stream_alloc(&php_stream_stdio_ops, self, 0, mode); +} static size_t php_stdiop_write(php_stream * stream, const char * buf, size_t count) { - return fwrite(buf, 1, count, (FILE*)stream->abstract); + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; + +#if HAVE_FLUSHIO + if (data->last_op == 'r') + fseek(data->file, 0, SEEK_CUR); + data->last_op = 'w'; +#endif + + return fwrite(buf, 1, count, data->file); } static size_t php_stdiop_read(php_stream * stream, char * buf, size_t count) { + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; + if (buf == NULL && count == 0) { /* check for EOF condition */ - if (feof((FILE*)stream->abstract)) { + if (feof(data->file)) { return EOF; } return 0; } - return fread(buf, 1, count, (FILE*)stream->abstract); + +#if HAVE_FLUSHIO + if (data->last_op == 'w') + fseek(data->file, 0, SEEK_CUR); + data->last_op = 'r'; +#endif + + return fread(buf, 1, count, ((php_stdio_stream_data*)stream->abstract)->file); } static int php_stdiop_close(php_stream * stream) { - return fclose((FILE*)stream->abstract); + int ret; + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; + + if (data->is_pipe) + ret = pclose(data->file); + else + ret = fclose(data->file); + + efree(data); + + return ret; } static int php_stdiop_flush(php_stream * stream) { - return fflush((FILE*)stream->abstract); + return fflush(((php_stdio_stream_data*)stream->abstract)->file); } static int php_stdiop_seek(php_stream * stream, off_t offset, int whence) { - return fseek((FILE*)stream->abstract, offset, whence); + return fseek(((php_stdio_stream_data*)stream->abstract)->file, offset, whence); } static char * php_stdiop_gets(php_stream * stream, char * buf, size_t size) { - return fgets(buf, size, (FILE*)stream->abstract); + return fgets(buf, size, ((php_stdio_stream_data*)stream->abstract)->file); } static int php_stdiop_cast(php_stream * stream, int castas, void ** ret) { @@ -221,11 +273,11 @@ switch (castas) { case PHP_STREAM_AS_STDIO: if (ret) - *ret = stream->abstract; + *ret = ((php_stdio_stream_data*)stream->abstract)->file; return SUCCESS; case PHP_STREAM_AS_FD: - fd = fileno((FILE*)stream->abstract); + fd = fileno(((php_stdio_stream_data*)stream->abstract)->file); if (fd < 0) return FAILURE; if (ret) @@ -243,21 +295,153 @@ "STDIO" }; -PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode) +PHPAPI php_stream * php_stream_fopen_with_path(char * filename, char * mode, char * path, char **opened_path TSRMLS_DC) /* {{{ */ { - FILE * fp = fopen(filename, mode); + /* code ripped off from fopen_wrappers.c */ + char *pathbuf, *ptr, *end; + char *exec_fname; + char trypath[MAXPATHLEN]; + struct stat sb; + php_stream *stream; + int path_length; + int filename_length; + int exec_fname_length; + + if (opened_path) { + *opened_path = NULL; + } + + if(!filename) { + return NULL; + } + + filename_length = strlen(filename); + + /* Relative path open */ + if (*filename == '.') { + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { + return NULL; + } + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + } + + /* + * files in safe_mode_include_dir (or subdir) are excluded from + * safe mode GID/UID checks + */ + + /* Absolute path open */ + if (IS_ABSOLUTE_PATH(filename, filename_length)) { + if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0) + /* filename is in safe_mode_include_dir (or subdir) */ + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) + return NULL; + + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + } + + if (!path || (path && !*path)) { + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { + return NULL; + } + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + } + + /* check in provided path */ + /* append the calling scripts' current working directory + * as a fall back case + */ + if (zend_is_executing(TSRMLS_C)) { + exec_fname = zend_get_executed_filename(TSRMLS_C); + exec_fname_length = strlen(exec_fname); + path_length = strlen(path); + + while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if ((exec_fname && exec_fname[0] == '[') + || exec_fname_length<=0) { + /* [no active file] or no path */ + pathbuf = estrdup(path); + } else { + pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1); + memcpy(pathbuf, path, path_length); + pathbuf[path_length] = DEFAULT_DIR_SEPARATOR; + memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length); + pathbuf[path_length + exec_fname_length +1] = '\0'; + } + } else { + pathbuf = estrdup(path); + } + + ptr = pathbuf; + + while (ptr && *ptr) { + end = strchr(ptr, DEFAULT_DIR_SEPARATOR); + if (end != NULL) { + *end = '\0'; + end++; + } + snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename); + if (PG(safe_mode)) { + if (VCWD_STAT(trypath, &sb) == 0) { + /* file exists ... check permission */ + if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0) || + php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM)) + /* UID ok, or trypath is in safe_mode_include_dir */ + stream = php_stream_fopen(trypath, mode, opened_path TSRMLS_CC); + else + stream = NULL; + + efree(pathbuf); + return stream; + } + } + stream = php_stream_fopen(trypath, mode, opened_path TSRMLS_CC); + if (stream) { + efree(pathbuf); + return stream; + } + ptr = end; + } /* end provided path */ + + efree(pathbuf); + return NULL; + +} +/* }}} */ + +PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode, char **opened_path TSRMLS_DC) +{ + FILE * fp; + char * realpath; + + realpath = expand_filepath(filename, NULL TSRMLS_C); + + fp = fopen(realpath, mode); if (fp) { - php_stream * ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, mode); + php_stream * ret = php_stream_fopen_from_file(fp, mode); + + if (ret) { + if (opened_path) { + *opened_path = realpath; + realpath = NULL; + } + if (realpath) + efree(realpath); - if (ret) return ret; + } fclose(fp); } + efree(realpath); return NULL; } +/* }}} */ +/* {{{ STDIO with fopencookie */ #if HAVE_FOPENCOOKIE static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size) { @@ -287,8 +471,9 @@ #else /* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */ #endif +/* }}} */ -PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err) +PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err) /* {{{ */ { if (castas == PHP_STREAM_AS_STDIO) { if (stream->stdiocast) { @@ -355,7 +540,122 @@ return SUCCESS; +} /* }}} */ + +int php_init_stream_wrappers(TSRMLS_D) +{ + if (PG(allow_url_fopen)) + return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1); + return SUCCESS; } + +int php_shutdown_stream_wrappers(TSRMLS_D) +{ + if (PG(allow_url_fopen)) + zend_hash_destroy(&url_stream_wrappers_hash); + return SUCCESS; +} + +PHPAPI int php_register_url_stream_wrapper(char * protocol, php_stream_factory_func_t wrapper TSRMLS_DC) +{ + if (PG(allow_url_fopen)) + return zend_hash_add(&url_stream_wrappers_hash, protocol, strlen(protocol), &wrapper, sizeof(wrapper), NULL); + return FAILURE; +} +PHPAPI int php_unregister_url_stream_wrapper(char * protocol, php_stream_factory_func_t wrapper TSRMLS_DC) +{ + if (PG(allow_url_fopen)) + return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol)); + return SUCCESS; +} + +static php_stream * php_stream_open_url(char * path, char * mode, int options, char ** opened_path TSRMLS_DC) +{ + php_stream_factory_func_t * wrapper; + const char * p, *protocol = NULL; + int n = 0; + + for (p = path; isalnum((int)*p); p++) + n++; + + if ((*p == ':') && (n > 1)) + protocol = path; + + if (protocol) { +#ifdef STREAMS_SUPPORT_FOPEN_WRAPPER + FILE * fallback; + int issock; + int socketd; +#endif + + if (FAILURE == zend_hash_find(&url_stream_wrappers_hash, (char*)protocol, n, (void**)&wrapper)) { + wrapper = NULL; + protocol = NULL; + } + if (wrapper) + return (*wrapper)(path, mode, options, opened_path TSRMLS_CC); + + /* no luck there. We could try the regular fopen wrappers stuff here */ +#ifdef STREAMS_SUPPORT_FOPEN_WRAPPER + fallback = php_fopen_wrapper(path, mode, options, &issock, &socketd, opened_path TSRMLS_CC); + + if (issock) + return php_stream_sock_open_from_socket(socketd, 0); + + if (fallback) + return php_stream_fopen_from_file(fallback, mode); +#endif + } + + if (!protocol || !strncasecmp(protocol, "file", n)) { + if (protocol && path[n+1] == '/' && path[n+2] == '/') { + zend_error(E_WARNING, "remote host file access not supported, %s", path); + return NULL; + } + if (protocol) + path += n + 1; + + /* fall back on regular file access */ + return php_stream_open_wrapper(path, mode, (options & ~REPORT_ERRORS) | IGNORE_URL, + opened_path TSRMLS_CC); + } + return NULL; +} + +PHPAPI php_stream * php_stream_open_wrapper(char * path, char * mode, int options, char ** opened_path TSRMLS_DC) +{ + php_stream * stream = NULL; + + if (opened_path) + *opened_path = NULL; + + if (!path || !*path) + return NULL; + + if (PG(allow_url_fopen) && !(options & IGNORE_URL)) { + stream = php_stream_open_url(path, mode, options, opened_path TSRMLS_CC); + goto out; + } + + if ((options & USE_PATH) && PG(include_path) != NULL) { + stream = php_stream_fopen_with_path(path, mode, PG(include_path), opened_path TSRMLS_CC); + goto out; + } + + if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM))) + return NULL; + + stream = php_stream_fopen(path, mode, opened_path TSRMLS_CC); +out: + if (stream == NULL && (options & REPORT_ERRORS)) { + char * tmp = estrdup(path); + php_strip_url_passwd(tmp); + zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_CC), tmp, strerror(errno)); + efree(tmp); + } + return stream; +} + #endif /* Index: tests/strings/003.phpt =================================================================== RCS file: /repository/php4/tests/strings/003.phpt,v retrieving revision 1.3 diff -u -w -B -b -r1.3 003.phpt --- tests/strings/003.phpt 21 Mar 2001 12:47:46 -0000 1.3 +++ tests/strings/003.phpt 18 Feb 2002 12:01:09 -0000 @@ -5,9 +5,15 @@ --FILE-- \"&εΔ\n"); -echo htmlentities ("<>\"&εΔ\n"); +$sc_encoded = htmlspecialchars ("<>\"&εΔ\n"); +echo $sc_encoded; +$ent_encoded = htmlentities ("<>\"&εΔ\n"); +echo $ent_encoded; +echo html_entity_decode($sc_encoded); +echo html_entity_decode($ent_encoded); ?> --EXPECT-- <>"&εΔ <>"&åÄ +<>"&εΔ +<>"&εΔ