with streams opened with, inter alia, the 'xb' mode).
obtained with ReflectionClass::getProperties(). (Gustavo)
- Fixed covariance of return-by-ref constraints. (Etienne)
+- Fixed bug #53241 (stream casting that relies on fdopen/fopencookie fails
+ with streams opened with, inter alia, the 'xb' mode). (Gustavo)
- Fixed bug #53198 (changing INI setting "from" with ini_set did not have any
effect). (Gustavo)
- Fixed bug #53180 (post_max_size=0 not disabling the limit when the content
--- /dev/null
+--TEST--\r
+Bug #53241 (stream casting that relies on fdopen/fopencookie fails with 'xb' mode)\r
+--SKIPIF--\r
+<?php\r
+/* unfortunately no standard function does a cast to FILE*, so we need\r
+ * curl to test this */\r
+if (!extension_loaded("curl")) exit("skip curl extension not loaded");\r
+--FILE--\r
+<?php\r
+$fn = __DIR__ . "/test.tmp";\r
+@unlink($fn);\r
+$fh = fopen($fn, 'xb');\r
+$ch = curl_init('http://www.yahoo.com/');\r
+var_dump(curl_setopt($ch, CURLOPT_FILE, $fh));\r
+echo "Done.\n";\r
+--EXPECT--\r
+bool(true)\r
+Done.\r
#define PHP_STREAM_CAST_MASK (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE | PHP_STREAM_CAST_INTERNAL)
BEGIN_EXTERN_C()
PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC);
+/* This functions transforms the first char to 'w' if it's not 'r', 'a' or 'w'
+ * and strips any subsequent chars except '+' and 'b'.
+ * Use this to sanitize stream->mode if you call e.g. fdopen, fopencookie or
+ * any other function that expects standard modes and you allow non-standard
+ * ones. result should be a char[5]. */
+PHPAPI void php_stream_rep_nonstand_mode(php_stream *stream, char *result);
END_EXTERN_C()
/* use this to check if a stream can be cast into another form */
#define php_stream_can_cast(stream, as) _php_stream_cast((stream), (as), NULL, 0 TSRMLS_CC)
#endif
/* }}} */
+/* {{{ php_stream_rep_nonstand_mode
+ * Result should have at least size 5, e.g. to write wbx+\0 */
+PHPAPI void php_stream_rep_nonstand_mode(php_stream *stream, char *result)
+{
+ /* replace modes not supported by fdopen and fopencookie, but supported
+ * by PHP's fread(), so that their calls won't fail */
+ const char *cur_mode = stream->mode;
+ int has_plus = 0,
+ has_bin = 0,
+ i,
+ res_curs = 0;
+
+ if (cur_mode[0] == 'r' || cur_mode[0] == 'w' || cur_mode[0] == 'a') {
+ result[res_curs++] = cur_mode[0];
+ } else {
+ /* assume cur_mode[0] is 'c' or 'x'; substitute by 'w', which should not
+ * truncate anything in fdopen/fopencookie */
+ result[res_curs++] = 'w';
+
+ /* x is allowed (at least by glibc & compat), but not as the 1st mode
+ * as in PHP and in any case is (at best) ignored by fdopen and fopencookie */
+ }
+
+ /* assume current mode has at most length 4 (e.g. wbn+) */
+ for (i = 1; i < 4 && cur_mode[i] != '\0'; i++) {
+ if (cur_mode[i] == 'b') {
+ has_bin = 1;
+ } else if (cur_mode[i] == '+') {
+ has_plus = 1;
+ }
+ /* ignore 'n', 't' or other stuff */
+ }
+
+ if (has_bin) {
+ result[res_curs++] = 'b';
+ }
+ if (has_plus) {
+ result[res_curs++] = '+';
+ }
+
+ result[res_curs] = '\0';
+}
+/* }}} */
+
/* {{{ php_stream_cast */
PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC)
{
goto exit_success;
}
- *(FILE**)ret = fopencookie(stream, stream->mode, PHP_STREAM_COOKIE_FUNCTIONS);
+ {
+ char fixed_mode[5];
+ php_stream_rep_nonstand_mode(stream, fixed_mode);
+ *(FILE**)ret = fopencookie(stream, fixed_mode, PHP_STREAM_COOKIE_FUNCTIONS);
+ }
if (*ret != NULL) {
off_t pos;
if (data->file == NULL) {
/* we were opened as a plain file descriptor, so we
* need fdopen now */
- data->file = fdopen(data->fd, stream->mode);
+ char fixed_mode[5];
+ php_stream_rep_nonstand_mode(stream, fixed_mode);
+ data->file = fdopen(data->fd, fixed_mode);
if (data->file == NULL) {
return FAILURE;
}