xref: /PHP-8.3/ext/standard/streamsfuncs.c (revision ba909d7c)
1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Authors: Wez Furlong <wez@thebrainroom.com>                          |
14   |          Sara Golemon <pollita@php.net>                              |
15   +----------------------------------------------------------------------+
16 */
17 
18 #include "php.h"
19 #include "php_globals.h"
20 #include "ext/standard/flock_compat.h"
21 #include "ext/standard/file.h"
22 #include "ext/standard/php_filestat.h"
23 #include "php_open_temporary_file.h"
24 #include "ext/standard/basic_functions.h"
25 #include "php_ini.h"
26 #include "streamsfuncs.h"
27 #include "php_network.h"
28 #include "php_string.h"
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 
33 #ifndef PHP_WIN32
34 #define php_select(m, r, w, e, t)	select(m, r, w, e, t)
35 typedef unsigned long long php_timeout_ull;
36 #else
37 #include "win32/select.h"
38 #include "win32/sockets.h"
39 #include "win32/console.h"
40 typedef unsigned __int64 php_timeout_ull;
41 #endif
42 
43 #define GET_CTX_OPT(stream, wrapper, name, val) (PHP_STREAM_CONTEXT(stream) && NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), wrapper, name)))
44 
45 static php_stream_context *decode_context_param(zval *contextresource);
46 
47 /* Streams based network functions */
48 
49 #ifdef HAVE_SOCKETPAIR
50 /* {{{ Creates a pair of connected, indistinguishable socket streams */
PHP_FUNCTION(stream_socket_pair)51 PHP_FUNCTION(stream_socket_pair)
52 {
53 	zend_long domain, type, protocol;
54 	php_stream *s1, *s2;
55 	php_socket_t pair[2];
56 
57 	ZEND_PARSE_PARAMETERS_START(3, 3)
58 		Z_PARAM_LONG(domain)
59 		Z_PARAM_LONG(type)
60 		Z_PARAM_LONG(protocol)
61 	ZEND_PARSE_PARAMETERS_END();
62 
63 	if (0 != socketpair((int)domain, (int)type, (int)protocol, pair)) {
64 		char errbuf[256];
65 		php_error_docref(NULL, E_WARNING, "Failed to create sockets: [%d]: %s",
66 			php_socket_errno(), php_socket_strerror(php_socket_errno(), errbuf, sizeof(errbuf)));
67 		RETURN_FALSE;
68 	}
69 
70     s1 = php_stream_sock_open_from_socket(pair[0], 0);
71     if (s1 == NULL) {
72         close(pair[0]);
73         close(pair[1]);
74         php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair");
75         RETURN_FALSE;
76     }
77     s2 = php_stream_sock_open_from_socket(pair[1], 0);
78     if (s2 == NULL) {
79         php_stream_free(s1, PHP_STREAM_FREE_CLOSE);
80         close(pair[1]);
81         php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair");
82         RETURN_FALSE;
83     }
84 
85     array_init(return_value);
86 
87 	/* set the __exposed flag.
88 	 * php_stream_to_zval() does, add_next_index_resource() does not */
89 	php_stream_auto_cleanup(s1);
90 	php_stream_auto_cleanup(s2);
91 
92 	add_next_index_resource(return_value, s1->res);
93 	add_next_index_resource(return_value, s2->res);
94 }
95 /* }}} */
96 #endif
97 
98 /* {{{ Open a client connection to a remote address */
PHP_FUNCTION(stream_socket_client)99 PHP_FUNCTION(stream_socket_client)
100 {
101 	zend_string *host;
102 	zval *zerrno = NULL, *zerrstr = NULL, *zcontext = NULL;
103 	double timeout;
104 	bool timeout_is_null = 1;
105 	php_timeout_ull conv;
106 	struct timeval tv;
107 	char *hashkey = NULL;
108 	php_stream *stream = NULL;
109 	int err;
110 	zend_long flags = PHP_STREAM_CLIENT_CONNECT;
111 	zend_string *errstr = NULL;
112 	php_stream_context *context = NULL;
113 
114 	ZEND_PARSE_PARAMETERS_START(1, 6)
115 		Z_PARAM_STR(host)
116 		Z_PARAM_OPTIONAL
117 		Z_PARAM_ZVAL(zerrno)
118 		Z_PARAM_ZVAL(zerrstr)
119 		Z_PARAM_DOUBLE_OR_NULL(timeout, timeout_is_null)
120 		Z_PARAM_LONG(flags)
121 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
122 	ZEND_PARSE_PARAMETERS_END();
123 
124 	RETVAL_FALSE;
125 
126 	if (timeout_is_null) {
127 		timeout = (double)FG(default_socket_timeout);
128 	} else if (!zend_finite(timeout)) {
129 		zend_argument_value_error(4, "must be a finite value");
130 		RETURN_THROWS();
131 	}
132 
133 	context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
134 
135 	if (flags & PHP_STREAM_CLIENT_PERSISTENT) {
136 		spprintf(&hashkey, 0, "stream_socket_client__%s", ZSTR_VAL(host));
137 	}
138 
139 	/* prepare the timeout value for use */
140 	struct timeval *tv_pointer;
141 	if (timeout < 0.0 || timeout >= (double) PHP_TIMEOUT_ULL_MAX / 1000000.0) {
142 		tv_pointer = NULL;
143 	} else {
144 		conv = (php_timeout_ull) (timeout * 1000000.0);
145 #ifdef PHP_WIN32
146 		tv.tv_sec = (long)(conv / 1000000);
147 		tv.tv_usec = (long)(conv % 1000000);
148 #else
149 		tv.tv_sec = conv / 1000000;
150 		tv.tv_usec = conv % 1000000;
151 #endif
152 		tv_pointer = &tv;
153 	}
154 
155 	if (zerrno) {
156 		ZEND_TRY_ASSIGN_REF_LONG(zerrno, 0);
157 	}
158 	if (zerrstr) {
159 		ZEND_TRY_ASSIGN_REF_EMPTY_STRING(zerrstr);
160 	}
161 
162 	stream = php_stream_xport_create(ZSTR_VAL(host), ZSTR_LEN(host), REPORT_ERRORS,
163 			STREAM_XPORT_CLIENT | (flags & PHP_STREAM_CLIENT_CONNECT ? STREAM_XPORT_CONNECT : 0) |
164 			(flags & PHP_STREAM_CLIENT_ASYNC_CONNECT ? STREAM_XPORT_CONNECT_ASYNC : 0),
165 			hashkey, tv_pointer, context, &errstr, &err);
166 
167 
168 	if (stream == NULL) {
169 		/* host might contain binary characters */
170 		zend_string *quoted_host = php_addslashes(host);
171 
172 		php_error_docref(NULL, E_WARNING, "Unable to connect to %s (%s)", ZSTR_VAL(quoted_host), errstr == NULL ? "Unknown error" : ZSTR_VAL(errstr));
173 		zend_string_release_ex(quoted_host, 0);
174 	}
175 
176 	if (hashkey) {
177 		efree(hashkey);
178 	}
179 
180 	if (stream == NULL) {
181 		if (zerrno) {
182 			ZEND_TRY_ASSIGN_REF_LONG(zerrno, err);
183 		}
184 		if (zerrstr && errstr) {
185 			ZEND_TRY_ASSIGN_REF_STR(zerrstr, errstr);
186 		} else if (errstr) {
187 			zend_string_release_ex(errstr, 0);
188 		}
189 		RETURN_FALSE;
190 	}
191 
192 	if (errstr) {
193 		zend_string_release_ex(errstr, 0);
194 	}
195 
196 	php_stream_to_zval(stream, return_value);
197 
198 }
199 /* }}} */
200 
201 /* {{{ Create a server socket bound to localaddress */
PHP_FUNCTION(stream_socket_server)202 PHP_FUNCTION(stream_socket_server)
203 {
204 	char *host;
205 	size_t host_len;
206 	zval *zerrno = NULL, *zerrstr = NULL, *zcontext = NULL;
207 	php_stream *stream = NULL;
208 	int err = 0;
209 	zend_long flags = STREAM_XPORT_BIND | STREAM_XPORT_LISTEN;
210 	zend_string *errstr = NULL;
211 	php_stream_context *context = NULL;
212 
213 	RETVAL_FALSE;
214 
215 	ZEND_PARSE_PARAMETERS_START(1, 5)
216 		Z_PARAM_STRING(host, host_len)
217 		Z_PARAM_OPTIONAL
218 		Z_PARAM_ZVAL(zerrno)
219 		Z_PARAM_ZVAL(zerrstr)
220 		Z_PARAM_LONG(flags)
221 		Z_PARAM_RESOURCE_OR_NULL(zcontext)
222 	ZEND_PARSE_PARAMETERS_END();
223 
224 	context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
225 
226 	if (zerrno) {
227 		ZEND_TRY_ASSIGN_REF_LONG(zerrno, 0);
228 	}
229 	if (zerrstr) {
230 		ZEND_TRY_ASSIGN_REF_EMPTY_STRING(zerrstr);
231 	}
232 
233 	stream = php_stream_xport_create(host, host_len, REPORT_ERRORS,
234 			STREAM_XPORT_SERVER | (int)flags,
235 			NULL, NULL, context, &errstr, &err);
236 
237 	if (stream == NULL) {
238 		php_error_docref(NULL, E_WARNING, "Unable to connect to %s (%s)", host, errstr == NULL ? "Unknown error" : ZSTR_VAL(errstr));
239 	}
240 
241 	if (stream == NULL) {
242 		if (zerrno) {
243 			ZEND_TRY_ASSIGN_REF_LONG(zerrno, err);
244 		}
245 		if (zerrstr && errstr) {
246 			ZEND_TRY_ASSIGN_REF_STR(zerrstr, errstr);
247 		} else if (errstr) {
248 			zend_string_release_ex(errstr, 0);
249 		}
250 		RETURN_FALSE;
251 	}
252 
253 	if (errstr) {
254 		zend_string_release_ex(errstr, 0);
255 	}
256 
257 	php_stream_to_zval(stream, return_value);
258 }
259 /* }}} */
260 
261 /* {{{ Accept a client connection from a server socket */
PHP_FUNCTION(stream_socket_accept)262 PHP_FUNCTION(stream_socket_accept)
263 {
264 	double timeout;
265 	bool timeout_is_null = 1;
266 	zval *zpeername = NULL;
267 	zend_string *peername = NULL;
268 	php_timeout_ull conv;
269 	struct timeval tv;
270 	php_stream *stream = NULL, *clistream = NULL;
271 	zval *zstream;
272 	zend_string *errstr = NULL;
273 
274 	ZEND_PARSE_PARAMETERS_START(1, 3)
275 		Z_PARAM_RESOURCE(zstream)
276 		Z_PARAM_OPTIONAL
277 		Z_PARAM_DOUBLE_OR_NULL(timeout, timeout_is_null)
278 		Z_PARAM_ZVAL(zpeername)
279 	ZEND_PARSE_PARAMETERS_END();
280 
281 	if (timeout_is_null) {
282 		timeout = (double)FG(default_socket_timeout);
283 	} else if (!zend_finite(timeout)) {
284 		zend_argument_value_error(2, "must be a finite value");
285 		RETURN_THROWS();
286 	}
287 
288 	php_stream_from_zval(stream, zstream);
289 
290 	/* prepare the timeout value for use */
291 	struct timeval *tv_pointer;
292 	if (timeout < 0.0 || timeout >= (double) PHP_TIMEOUT_ULL_MAX / 1000000.0) {
293 		tv_pointer = NULL;
294 	} else {
295 		conv = (php_timeout_ull) (timeout * 1000000.0);
296 #ifdef PHP_WIN32
297 		tv.tv_sec = (long)(conv / 1000000);
298 		tv.tv_usec = (long)(conv % 1000000);
299 #else
300 		tv.tv_sec = conv / 1000000;
301 		tv.tv_usec = conv % 1000000;
302 #endif
303 		tv_pointer = &tv;
304 	}
305 
306 	if (0 == php_stream_xport_accept(stream, &clistream,
307 				zpeername ? &peername : NULL,
308 				NULL, NULL,
309 				tv_pointer, &errstr
310 				) && clistream) {
311 
312 		if (peername) {
313 			ZEND_TRY_ASSIGN_REF_STR(zpeername, peername);
314 		}
315 		php_stream_to_zval(clistream, return_value);
316 	} else {
317 		if (peername) {
318 			zend_string_release(peername);
319 		}
320 		php_error_docref(NULL, E_WARNING, "Accept failed: %s", errstr ? ZSTR_VAL(errstr) : "Unknown error");
321 		RETVAL_FALSE;
322 	}
323 
324 	if (errstr) {
325 		zend_string_release_ex(errstr, 0);
326 	}
327 }
328 /* }}} */
329 
330 /* {{{ Returns either the locally bound or remote name for a socket stream */
PHP_FUNCTION(stream_socket_get_name)331 PHP_FUNCTION(stream_socket_get_name)
332 {
333 	php_stream *stream;
334 	zval *zstream;
335 	bool want_peer;
336 	zend_string *name = NULL;
337 
338 	ZEND_PARSE_PARAMETERS_START(2, 2)
339 		Z_PARAM_RESOURCE(zstream)
340 		Z_PARAM_BOOL(want_peer)
341 	ZEND_PARSE_PARAMETERS_END();
342 
343 	php_stream_from_zval(stream, zstream);
344 
345 	if (0 != php_stream_xport_get_name(stream, want_peer,
346 				&name,
347 				NULL, NULL
348 				) || !name) {
349 		RETURN_FALSE;
350 	}
351 
352 	if ((ZSTR_LEN(name) == 0) || (ZSTR_VAL(name)[0] == 0)) {
353 		zend_string_release_ex(name, 0);
354 		RETURN_FALSE;
355 	}
356 
357 	RETVAL_STR(name);
358 }
359 /* }}} */
360 
361 /* {{{ Send data to a socket stream.  If target_addr is specified it must be in dotted quad (or [ipv6]) format */
PHP_FUNCTION(stream_socket_sendto)362 PHP_FUNCTION(stream_socket_sendto)
363 {
364 	php_stream *stream;
365 	zval *zstream;
366 	zend_long flags = 0;
367 	char *data, *target_addr = NULL;
368 	size_t datalen, target_addr_len = 0;
369 	php_sockaddr_storage sa;
370 	socklen_t sl = 0;
371 
372 	ZEND_PARSE_PARAMETERS_START(2, 4)
373 		Z_PARAM_RESOURCE(zstream)
374 		Z_PARAM_STRING(data, datalen)
375 		Z_PARAM_OPTIONAL
376 		Z_PARAM_LONG(flags)
377 		Z_PARAM_STRING(target_addr, target_addr_len)
378 	ZEND_PARSE_PARAMETERS_END();
379 	php_stream_from_zval(stream, zstream);
380 
381 	if (target_addr_len) {
382 		/* parse the address */
383 		if (FAILURE == php_network_parse_network_address_with_port(target_addr, target_addr_len, (struct sockaddr*)&sa, &sl)) {
384 			php_error_docref(NULL, E_WARNING, "Failed to parse `%s' into a valid network address", target_addr);
385 			RETURN_FALSE;
386 		}
387 	}
388 
389 	RETURN_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl));
390 }
391 /* }}} */
392 
393 /* {{{ Receives data from a socket stream */
PHP_FUNCTION(stream_socket_recvfrom)394 PHP_FUNCTION(stream_socket_recvfrom)
395 {
396 	php_stream *stream;
397 	zval *zstream, *zremote = NULL;
398 	zend_string *remote_addr = NULL;
399 	zend_long to_read = 0;
400 	zend_string *read_buf;
401 	zend_long flags = 0;
402 	int recvd;
403 
404 	ZEND_PARSE_PARAMETERS_START(2, 4)
405 		Z_PARAM_RESOURCE(zstream)
406 		Z_PARAM_LONG(to_read)
407 		Z_PARAM_OPTIONAL
408 		Z_PARAM_LONG(flags)
409 		Z_PARAM_ZVAL(zremote)
410 	ZEND_PARSE_PARAMETERS_END();
411 
412 	php_stream_from_zval(stream, zstream);
413 
414 	if (zremote) {
415 		ZEND_TRY_ASSIGN_REF_NULL(zremote);
416 	}
417 
418 	if (to_read <= 0) {
419 		zend_argument_value_error(2, "must be greater than 0");
420 		RETURN_THROWS();
421 	}
422 
423 	read_buf = zend_string_alloc(to_read, 0);
424 
425 	recvd = php_stream_xport_recvfrom(stream, ZSTR_VAL(read_buf), to_read, (int)flags, NULL, NULL,
426 			zremote ? &remote_addr : NULL
427 			);
428 
429 	if (recvd >= 0) {
430 		if (zremote && remote_addr) {
431 			ZEND_TRY_ASSIGN_REF_STR(zremote, remote_addr);
432 		}
433 		ZSTR_VAL(read_buf)[recvd] = '\0';
434 		ZSTR_LEN(read_buf) = recvd;
435 		RETURN_NEW_STR(read_buf);
436 	}
437 
438 	zend_string_efree(read_buf);
439 	RETURN_FALSE;
440 }
441 /* }}} */
442 
443 /* {{{ Reads all remaining bytes (or up to maxlen bytes) from a stream and returns them as a string. */
PHP_FUNCTION(stream_get_contents)444 PHP_FUNCTION(stream_get_contents)
445 {
446 	php_stream *stream;
447 	zval *zsrc;
448 	zend_long maxlen, desiredpos = -1L;
449 	bool maxlen_is_null = 1;
450 	zend_string *contents;
451 
452 	ZEND_PARSE_PARAMETERS_START(1, 3)
453 		Z_PARAM_RESOURCE(zsrc)
454 		Z_PARAM_OPTIONAL
455 		Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null)
456 		Z_PARAM_LONG(desiredpos)
457 	ZEND_PARSE_PARAMETERS_END();
458 
459 	if (maxlen_is_null) {
460 		maxlen = (ssize_t) PHP_STREAM_COPY_ALL;
461 	} else if (maxlen < 0 && maxlen != (ssize_t)PHP_STREAM_COPY_ALL) {
462 		zend_argument_value_error(2, "must be greater than or equal to -1");
463 		RETURN_THROWS();
464 	}
465 
466 	php_stream_from_zval(stream, zsrc);
467 
468 	if (desiredpos >= 0) {
469 		int		seek_res = 0;
470 		zend_off_t	position;
471 
472 		position = php_stream_tell(stream);
473 		if (position >= 0 && desiredpos > position) {
474 			/* use SEEK_CUR to allow emulation in streams that don't support seeking */
475 			seek_res = php_stream_seek(stream, desiredpos - position, SEEK_CUR);
476 		} else if (desiredpos < position)  {
477 			/* desired position before position or error on tell */
478 			seek_res = php_stream_seek(stream, desiredpos, SEEK_SET);
479 		}
480 
481 		if (seek_res != 0) {
482 			php_error_docref(NULL, E_WARNING,
483 				"Failed to seek to position " ZEND_LONG_FMT " in the stream", desiredpos);
484 			RETURN_FALSE;
485 		}
486 	}
487 
488 	if ((contents = php_stream_copy_to_mem(stream, maxlen, 0))) {
489 		RETURN_STR(contents);
490 	} else {
491 		RETURN_EMPTY_STRING();
492 	}
493 }
494 /* }}} */
495 
496 /* {{{ Reads up to maxlen bytes from source stream and writes them to dest stream. */
PHP_FUNCTION(stream_copy_to_stream)497 PHP_FUNCTION(stream_copy_to_stream)
498 {
499 	php_stream *src, *dest;
500 	zval *zsrc, *zdest;
501 	zend_long maxlen, pos = 0;
502 	bool maxlen_is_null = 1;
503 	size_t len;
504 	int ret;
505 
506 	ZEND_PARSE_PARAMETERS_START(2, 4)
507 		Z_PARAM_RESOURCE(zsrc)
508 		Z_PARAM_RESOURCE(zdest)
509 		Z_PARAM_OPTIONAL
510 		Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null)
511 		Z_PARAM_LONG(pos)
512 	ZEND_PARSE_PARAMETERS_END();
513 
514 	if (maxlen_is_null) {
515 		maxlen = PHP_STREAM_COPY_ALL;
516 	}
517 
518 	php_stream_from_zval(src, zsrc);
519 	php_stream_from_zval(dest, zdest);
520 
521 	if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) {
522 		php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", pos);
523 		RETURN_FALSE;
524 	}
525 
526 	ret = php_stream_copy_to_stream_ex(src, dest, maxlen, &len);
527 
528 	if (ret != SUCCESS) {
529 		RETURN_FALSE;
530 	}
531 	RETURN_LONG(len);
532 }
533 /* }}} */
534 
535 /* {{{ Retrieves header/meta data from streams/file pointers */
PHP_FUNCTION(stream_get_meta_data)536 PHP_FUNCTION(stream_get_meta_data)
537 {
538 	zval *zstream;
539 	php_stream *stream;
540 
541 	ZEND_PARSE_PARAMETERS_START(1, 1)
542 		Z_PARAM_RESOURCE(zstream)
543 	ZEND_PARSE_PARAMETERS_END();
544 
545 	php_stream_from_zval(stream, zstream);
546 
547 	array_init(return_value);
548 
549 	if (!php_stream_populate_meta_data(stream, return_value)) {
550 		add_assoc_bool(return_value, "timed_out", 0);
551 		add_assoc_bool(return_value, "blocked", 1);
552 		add_assoc_bool(return_value, "eof", php_stream_eof(stream));
553 	}
554 
555 	if (!Z_ISUNDEF(stream->wrapperdata)) {
556 		Z_ADDREF_P(&stream->wrapperdata);
557 		add_assoc_zval(return_value, "wrapper_data", &stream->wrapperdata);
558 	}
559 	if (stream->wrapper) {
560 		add_assoc_string(return_value, "wrapper_type", (char *)stream->wrapper->wops->label);
561 	}
562 	add_assoc_string(return_value, "stream_type", (char *)stream->ops->label);
563 
564 	add_assoc_string(return_value, "mode", stream->mode);
565 
566 #if 0	/* TODO: needs updating for new filter API */
567 	if (stream->filterhead) {
568 		php_stream_filter *filter;
569 
570 		MAKE_STD_ZVAL(newval);
571 		array_init(newval);
572 
573 		for (filter = stream->filterhead; filter != NULL; filter = filter->next) {
574 			add_next_index_string(newval, (char *)filter->fops->label);
575 		}
576 
577 		add_assoc_zval(return_value, "filters", newval);
578 	}
579 #endif
580 
581 	add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos);
582 
583 	add_assoc_bool(return_value, "seekable", (stream->ops->seek) && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0);
584 	if (stream->orig_path) {
585 		add_assoc_string(return_value, "uri", stream->orig_path);
586 	}
587 
588 }
589 /* }}} */
590 
591 /* {{{ Retrieves list of registered socket transports */
PHP_FUNCTION(stream_get_transports)592 PHP_FUNCTION(stream_get_transports)
593 {
594 	HashTable *stream_xport_hash;
595 	zend_string *stream_xport;
596 
597 	ZEND_PARSE_PARAMETERS_NONE();
598 
599 	stream_xport_hash = php_stream_xport_get_hash();
600 	array_init(return_value);
601 	ZEND_HASH_MAP_FOREACH_STR_KEY(stream_xport_hash, stream_xport) {
602 		add_next_index_str(return_value, zend_string_copy(stream_xport));
603 	} ZEND_HASH_FOREACH_END();
604 }
605 /* }}} */
606 
607 /* {{{ Retrieves list of registered stream wrappers */
PHP_FUNCTION(stream_get_wrappers)608 PHP_FUNCTION(stream_get_wrappers)
609 {
610 	HashTable *url_stream_wrappers_hash;
611 	zend_string *stream_protocol;
612 
613 	ZEND_PARSE_PARAMETERS_NONE();
614 
615 	url_stream_wrappers_hash = php_stream_get_url_stream_wrappers_hash();
616 	array_init(return_value);
617 	ZEND_HASH_MAP_FOREACH_STR_KEY(url_stream_wrappers_hash, stream_protocol) {
618 		if (stream_protocol) {
619 			add_next_index_str(return_value, zend_string_copy(stream_protocol));
620 		}
621 	} ZEND_HASH_FOREACH_END();
622 
623 }
624 /* }}} */
625 
626 /* {{{ stream_select related functions */
stream_array_to_fd_set(zval * stream_array,fd_set * fds,php_socket_t * max_fd)627 static int stream_array_to_fd_set(zval *stream_array, fd_set *fds, php_socket_t *max_fd)
628 {
629 	zval *elem;
630 	php_stream *stream;
631 	int cnt = 0;
632 
633 	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
634 		return 0;
635 	}
636 
637 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(stream_array), elem) {
638 		/* Temporary int fd is needed for the STREAM data type on windows, passing this_fd directly to php_stream_cast()
639 			would eventually bring a wrong result on x64. php_stream_cast() casts to int internally, and this will leave
640 			the higher bits of a SOCKET variable uninitialized on systems with little endian. */
641 		php_socket_t this_fd;
642 
643 		ZVAL_DEREF(elem);
644 		php_stream_from_zval_no_verify(stream, elem);
645 		if (stream == NULL) {
646 			continue;
647 		}
648 		/* get the fd.
649 		 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
650 		 * when casting.  It is only used here so that the buffered data warning
651 		 * is not displayed.
652 		 * */
653 		if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) && this_fd != -1) {
654 
655 			PHP_SAFE_FD_SET(this_fd, fds);
656 
657 			if (this_fd > *max_fd) {
658 				*max_fd = this_fd;
659 			}
660 			cnt++;
661 		}
662 	} ZEND_HASH_FOREACH_END();
663 	return cnt ? 1 : 0;
664 }
665 
stream_array_from_fd_set(zval * stream_array,fd_set * fds)666 static int stream_array_from_fd_set(zval *stream_array, fd_set *fds)
667 {
668 	zval *elem, *dest_elem;
669 	HashTable *ht;
670 	php_stream *stream;
671 	int ret = 0;
672 	zend_string *key;
673 	zend_ulong num_ind;
674 
675 	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
676 		return 0;
677 	}
678 	ht = zend_new_array(zend_hash_num_elements(Z_ARRVAL_P(stream_array)));
679 
680 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) {
681 		php_socket_t this_fd;
682 
683 		ZVAL_DEREF(elem);
684 		php_stream_from_zval_no_verify(stream, elem);
685 		if (stream == NULL) {
686 			continue;
687 		}
688 		/* get the fd
689 		 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
690 		 * when casting.  It is only used here so that the buffered data warning
691 		 * is not displayed.
692 		 */
693 		if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) && this_fd != SOCK_ERR) {
694 			if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
695 				if (!key) {
696 					dest_elem = zend_hash_index_update(ht, num_ind, elem);
697 				} else {
698 					dest_elem = zend_hash_update(ht, key, elem);
699 				}
700 
701 				zval_add_ref(dest_elem);
702 				ret++;
703 				continue;
704 			}
705 		}
706 	} ZEND_HASH_FOREACH_END();
707 
708 	/* destroy old array and add new one */
709 	zval_ptr_dtor(stream_array);
710 	ZVAL_ARR(stream_array, ht);
711 
712 	return ret;
713 }
714 
stream_array_emulate_read_fd_set(zval * stream_array)715 static int stream_array_emulate_read_fd_set(zval *stream_array)
716 {
717 	zval *elem, *dest_elem;
718 	HashTable *ht;
719 	php_stream *stream;
720 	int ret = 0;
721 	zend_ulong num_ind;
722 	zend_string *key;
723 
724 	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
725 		return 0;
726 	}
727 	ht = zend_new_array(zend_hash_num_elements(Z_ARRVAL_P(stream_array)));
728 
729 	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) {
730 		ZVAL_DEREF(elem);
731 		php_stream_from_zval_no_verify(stream, elem);
732 		if (stream == NULL) {
733 			continue;
734 		}
735 		if ((stream->writepos - stream->readpos) > 0) {
736 			/* allow readable non-descriptor based streams to participate in stream_select.
737 			 * Non-descriptor streams will only "work" if they have previously buffered the
738 			 * data.  Not ideal, but better than nothing.
739 			 * This branch of code also allows blocking streams with buffered data to
740 			 * operate correctly in stream_select.
741 			 * */
742 			if (!key) {
743 				dest_elem = zend_hash_index_update(ht, num_ind, elem);
744 			} else {
745 				dest_elem = zend_hash_update(ht, key, elem);
746 			}
747 			zval_add_ref(dest_elem);
748 			ret++;
749 			continue;
750 		}
751 	} ZEND_HASH_FOREACH_END();
752 
753 	if (ret > 0) {
754 		/* destroy old array and add new one */
755 		zval_ptr_dtor(stream_array);
756 		ZVAL_ARR(stream_array, ht);
757 	} else {
758 		zend_array_destroy(ht);
759 	}
760 
761 	return ret;
762 }
763 /* }}} */
764 
765 /* {{{ Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */
PHP_FUNCTION(stream_select)766 PHP_FUNCTION(stream_select)
767 {
768 	zval *r_array, *w_array, *e_array;
769 	struct timeval tv, *tv_p = NULL;
770 	fd_set rfds, wfds, efds;
771 	php_socket_t max_fd = 0;
772 	int retval, sets = 0;
773 	zend_long sec, usec = 0;
774 	bool secnull;
775 	bool usecnull = 1;
776 	int set_count, max_set_count = 0;
777 
778 	ZEND_PARSE_PARAMETERS_START(4, 5)
779 		Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0)
780 		Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0)
781 		Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0)
782 		Z_PARAM_LONG_OR_NULL(sec, secnull)
783 		Z_PARAM_OPTIONAL
784 		Z_PARAM_LONG_OR_NULL(usec, usecnull)
785 	ZEND_PARSE_PARAMETERS_END();
786 
787 	FD_ZERO(&rfds);
788 	FD_ZERO(&wfds);
789 	FD_ZERO(&efds);
790 
791 	if (r_array != NULL) {
792 		set_count = stream_array_to_fd_set(r_array, &rfds, &max_fd);
793 		if (set_count > max_set_count)
794 			max_set_count = set_count;
795 		sets += set_count;
796 	}
797 
798 	if (w_array != NULL) {
799 		set_count = stream_array_to_fd_set(w_array, &wfds, &max_fd);
800 		if (set_count > max_set_count)
801 			max_set_count = set_count;
802 		sets += set_count;
803 	}
804 
805 	if (e_array != NULL) {
806 		set_count = stream_array_to_fd_set(e_array, &efds, &max_fd);
807 		if (set_count > max_set_count)
808 			max_set_count = set_count;
809 		sets += set_count;
810 	}
811 
812 	if (!sets) {
813 		zend_value_error("No stream arrays were passed");
814 		RETURN_THROWS();
815 	}
816 
817 	if (!PHP_SAFE_MAX_FD(max_fd, max_set_count)) {
818 		RETURN_FALSE;
819 	}
820 
821 	if (secnull && !usecnull) {
822 		if (usec != 0) {
823 			zend_argument_value_error(5, "must be null when argument #4 ($seconds) is null");
824 			RETURN_THROWS();
825 		}
826 	}
827 
828 	/* If seconds is not set to null, build the timeval, else we wait indefinitely */
829 	if (!secnull) {
830 		if (sec < 0) {
831 			zend_argument_value_error(4, "must be greater than or equal to 0");
832 			RETURN_THROWS();
833 		} else if (usec < 0) {
834 			zend_argument_value_error(5, "must be greater than or equal to 0");
835 			RETURN_THROWS();
836 		}
837 
838 		/* Windows, Solaris and BSD do not like microsecond values which are >= 1 sec */
839 		tv.tv_sec = (long)(sec + (usec / 1000000));
840 		tv.tv_usec = (long)(usec % 1000000);
841 		tv_p = &tv;
842 	}
843 
844 	/* slight hack to support buffered data; if there is data sitting in the
845 	 * read buffer of any of the streams in the read array, let's pretend
846 	 * that we selected, but return only the readable sockets */
847 	if (r_array != NULL) {
848 		retval = stream_array_emulate_read_fd_set(r_array);
849 		if (retval > 0) {
850 			if (w_array != NULL) {
851 				zval_ptr_dtor(w_array);
852 				ZVAL_EMPTY_ARRAY(w_array);
853 			}
854 			if (e_array != NULL) {
855 				zval_ptr_dtor(e_array);
856 				ZVAL_EMPTY_ARRAY(e_array);
857 			}
858 			RETURN_LONG(retval);
859 		}
860 	}
861 
862 	retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p);
863 
864 	if (retval == -1) {
865 		php_error_docref(NULL, E_WARNING, "Unable to select [%d]: %s (max_fd=%d)",
866 				errno, strerror(errno), max_fd);
867 		RETURN_FALSE;
868 	}
869 
870 	if (r_array != NULL) stream_array_from_fd_set(r_array, &rfds);
871 	if (w_array != NULL) stream_array_from_fd_set(w_array, &wfds);
872 	if (e_array != NULL) stream_array_from_fd_set(e_array, &efds);
873 
874 	RETURN_LONG(retval);
875 }
876 /* }}} */
877 
878 /* {{{ stream_context related functions */
user_space_stream_notifier(php_stream_context * context,int notifycode,int severity,char * xmsg,int xcode,size_t bytes_sofar,size_t bytes_max,void * ptr)879 static void user_space_stream_notifier(php_stream_context *context, int notifycode, int severity,
880 		char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr)
881 {
882 	zval *callback = &context->notifier->ptr;
883 	zval retval;
884 	zval zvs[6];
885 	int i;
886 
887 	ZVAL_LONG(&zvs[0], notifycode);
888 	ZVAL_LONG(&zvs[1], severity);
889 	if (xmsg) {
890 		ZVAL_STRING(&zvs[2], xmsg);
891 	} else {
892 		ZVAL_NULL(&zvs[2]);
893 	}
894 	ZVAL_LONG(&zvs[3], xcode);
895 	ZVAL_LONG(&zvs[4], bytes_sofar);
896 	ZVAL_LONG(&zvs[5], bytes_max);
897 
898 	if (FAILURE == call_user_function(NULL, NULL, callback, &retval, 6, zvs)) {
899 		php_error_docref(NULL, E_WARNING, "Failed to call user notifier");
900 	}
901 	for (i = 0; i < 6; i++) {
902 		zval_ptr_dtor(&zvs[i]);
903 	}
904 	zval_ptr_dtor(&retval);
905 }
906 
user_space_stream_notifier_dtor(php_stream_notifier * notifier)907 static void user_space_stream_notifier_dtor(php_stream_notifier *notifier)
908 {
909 	if (notifier && Z_TYPE(notifier->ptr) != IS_UNDEF) {
910 		zval_ptr_dtor(&notifier->ptr);
911 		ZVAL_UNDEF(&notifier->ptr);
912 	}
913 }
914 
parse_context_options(php_stream_context * context,HashTable * options)915 static int parse_context_options(php_stream_context *context, HashTable *options)
916 {
917 	zval *wval, *oval;
918 	zend_string *wkey, *okey;
919 	int ret = SUCCESS;
920 
921 	ZEND_HASH_FOREACH_STR_KEY_VAL(options, wkey, wval) {
922 		ZVAL_DEREF(wval);
923 		if (wkey && Z_TYPE_P(wval) == IS_ARRAY) {
924 			if (!HT_IS_PACKED(Z_ARRVAL_P(wval))) {
925 				ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(wval), okey, oval) {
926 					if (okey) {
927 						php_stream_context_set_option(context, ZSTR_VAL(wkey), ZSTR_VAL(okey), oval);
928 					}
929 				} ZEND_HASH_FOREACH_END();
930 			}
931 		} else {
932 			zend_value_error("Options should have the form [\"wrappername\"][\"optionname\"] = $value");
933 			return FAILURE;
934 		}
935 	} ZEND_HASH_FOREACH_END();
936 
937 	return ret;
938 }
939 
parse_context_params(php_stream_context * context,HashTable * params)940 static int parse_context_params(php_stream_context *context, HashTable *params)
941 {
942 	int ret = SUCCESS;
943 	zval *tmp;
944 
945 	if (NULL != (tmp = zend_hash_str_find(params, "notification", sizeof("notification")-1))) {
946 
947 		if (context->notifier) {
948 			php_stream_notification_free(context->notifier);
949 			context->notifier = NULL;
950 		}
951 
952 		context->notifier = php_stream_notification_alloc();
953 		context->notifier->func = user_space_stream_notifier;
954 		ZVAL_COPY(&context->notifier->ptr, tmp);
955 		context->notifier->dtor = user_space_stream_notifier_dtor;
956 	}
957 	if (NULL != (tmp = zend_hash_str_find(params, "options", sizeof("options")-1))) {
958 		if (Z_TYPE_P(tmp) == IS_ARRAY) {
959 			return parse_context_options(context, Z_ARRVAL_P(tmp));
960 		} else {
961 			zend_type_error("Invalid stream/context parameter");
962 			return FAILURE;
963 		}
964 	}
965 
966 	return ret;
967 }
968 
969 /* given a zval which is either a stream or a context, return the underlying
970  * stream_context.  If it is a stream that does not have a context assigned, it
971  * will create and assign a context and return that.  */
decode_context_param(zval * contextresource)972 static php_stream_context *decode_context_param(zval *contextresource)
973 {
974 	php_stream_context *context = NULL;
975 
976 	context = zend_fetch_resource_ex(contextresource, NULL, php_le_stream_context());
977 	if (context == NULL) {
978 		php_stream *stream;
979 
980 		stream = zend_fetch_resource2_ex(contextresource, NULL, php_file_le_stream(), php_file_le_pstream());
981 
982 		if (stream) {
983 			context = PHP_STREAM_CONTEXT(stream);
984 			if (context == NULL) {
985 				/* Only way this happens is if file is opened with NO_DEFAULT_CONTEXT
986 				   param, but then something is called which requires a context.
987 				   Don't give them the default one though since they already said they
988 	 			   didn't want it. */
989 				context = php_stream_context_alloc();
990 				stream->ctx = context->res;
991 			}
992 		}
993 	}
994 
995 	return context;
996 }
997 /* }}} */
998 
999 /* {{{ Retrieve options for a stream/wrapper/context */
PHP_FUNCTION(stream_context_get_options)1000 PHP_FUNCTION(stream_context_get_options)
1001 {
1002 	zval *zcontext;
1003 	php_stream_context *context;
1004 
1005 	ZEND_PARSE_PARAMETERS_START(1, 1)
1006 		Z_PARAM_RESOURCE(zcontext)
1007 	ZEND_PARSE_PARAMETERS_END();
1008 
1009 	context = decode_context_param(zcontext);
1010 	if (!context) {
1011 		zend_argument_type_error(1, "must be a valid stream/context");
1012 		RETURN_THROWS();
1013 	}
1014 
1015 	ZVAL_COPY(return_value, &context->options);
1016 }
1017 /* }}} */
1018 
1019 /* {{{ Set an option for a wrapper */
PHP_FUNCTION(stream_context_set_option)1020 PHP_FUNCTION(stream_context_set_option)
1021 {
1022 	zval *zcontext = NULL;
1023 	php_stream_context *context;
1024 	zend_string *wrappername;
1025 	HashTable *options;
1026 	char *optionname = NULL;
1027 	size_t optionname_len;
1028 	zval *zvalue = NULL;
1029 
1030 	ZEND_PARSE_PARAMETERS_START(2, 4)
1031 		Z_PARAM_RESOURCE(zcontext)
1032 		Z_PARAM_ARRAY_HT_OR_STR(options, wrappername)
1033 		Z_PARAM_OPTIONAL
1034 		Z_PARAM_STRING_OR_NULL(optionname, optionname_len)
1035 		Z_PARAM_ZVAL(zvalue)
1036 	ZEND_PARSE_PARAMETERS_END();
1037 
1038 	/* figure out where the context is coming from exactly */
1039 	if (!(context = decode_context_param(zcontext))) {
1040 		zend_argument_type_error(1, "must be a valid stream/context");
1041 		RETURN_THROWS();
1042 	}
1043 
1044 	if (options) {
1045 		if (optionname) {
1046 			zend_argument_value_error(3, "must be null when argument #2 ($wrapper_or_options) is an array");
1047 			RETURN_THROWS();
1048 		}
1049 
1050 		if (zvalue) {
1051 			zend_argument_value_error(4, "cannot be provided when argument #2 ($wrapper_or_options) is an array");
1052 			RETURN_THROWS();
1053 		}
1054 
1055 		RETURN_BOOL(parse_context_options(context, options) == SUCCESS);
1056 	} else {
1057 		if (!optionname) {
1058 			zend_argument_value_error(3, "cannot be null when argument #2 ($wrapper_or_options) is a string");
1059 			RETURN_THROWS();
1060 		}
1061 		if (!zvalue) {
1062 			zend_argument_value_error(4, "must be provided when argument #2 ($wrapper_or_options) is a string");
1063 			RETURN_THROWS();
1064 		}
1065 		php_stream_context_set_option(context, ZSTR_VAL(wrappername), optionname, zvalue);
1066 		RETURN_TRUE;
1067 	}
1068 }
1069 /* }}} */
1070 
PHP_FUNCTION(stream_context_set_options)1071 PHP_FUNCTION(stream_context_set_options)
1072 {
1073 	zval *zcontext = NULL;
1074 	php_stream_context *context;
1075 	HashTable *options;
1076 
1077 	ZEND_PARSE_PARAMETERS_START(2, 2)
1078 		Z_PARAM_RESOURCE(zcontext)
1079 		Z_PARAM_ARRAY_HT(options)
1080 	ZEND_PARSE_PARAMETERS_END();
1081 
1082 	/* figure out where the context is coming from exactly */
1083 	if (!(context = decode_context_param(zcontext))) {
1084 		zend_argument_type_error(1, "must be a valid stream/context");
1085 		RETURN_THROWS();
1086 	}
1087 
1088 	RETURN_BOOL(parse_context_options(context, options) == SUCCESS);
1089 }
1090 
1091 /* {{{ Set parameters for a file context */
PHP_FUNCTION(stream_context_set_params)1092 PHP_FUNCTION(stream_context_set_params)
1093 {
1094 	HashTable *params;
1095 	zval *zcontext;
1096 	php_stream_context *context;
1097 
1098 	ZEND_PARSE_PARAMETERS_START(2, 2)
1099 		Z_PARAM_RESOURCE(zcontext)
1100 		Z_PARAM_ARRAY_HT(params)
1101 	ZEND_PARSE_PARAMETERS_END();
1102 
1103 	context = decode_context_param(zcontext);
1104 	if (!context) {
1105 		zend_argument_type_error(1, "must be a valid stream/context");
1106 		RETURN_THROWS();
1107 	}
1108 
1109 	RETVAL_BOOL(parse_context_params(context, params) == SUCCESS);
1110 }
1111 /* }}} */
1112 
1113 /* {{{ Get parameters of a file context */
PHP_FUNCTION(stream_context_get_params)1114 PHP_FUNCTION(stream_context_get_params)
1115 {
1116 	zval *zcontext;
1117 	php_stream_context *context;
1118 
1119 	ZEND_PARSE_PARAMETERS_START(1, 1)
1120 		Z_PARAM_RESOURCE(zcontext)
1121 	ZEND_PARSE_PARAMETERS_END();
1122 
1123 	context = decode_context_param(zcontext);
1124 	if (!context) {
1125 		zend_argument_type_error(1, "must be a valid stream/context");
1126 		RETURN_THROWS();
1127 	}
1128 
1129 	array_init(return_value);
1130 	if (context->notifier && Z_TYPE(context->notifier->ptr) != IS_UNDEF && context->notifier->func == user_space_stream_notifier) {
1131 		Z_TRY_ADDREF(context->notifier->ptr);
1132 		add_assoc_zval_ex(return_value, "notification", sizeof("notification")-1, &context->notifier->ptr);
1133 	}
1134 	Z_TRY_ADDREF(context->options);
1135 	add_assoc_zval_ex(return_value, "options", sizeof("options")-1, &context->options);
1136 }
1137 /* }}} */
1138 
1139 /* {{{ Get a handle on the default file/stream context and optionally set parameters */
PHP_FUNCTION(stream_context_get_default)1140 PHP_FUNCTION(stream_context_get_default)
1141 {
1142 	HashTable *params = NULL;
1143 	php_stream_context *context;
1144 
1145 	ZEND_PARSE_PARAMETERS_START(0, 1)
1146 		Z_PARAM_OPTIONAL
1147 		Z_PARAM_ARRAY_HT_OR_NULL(params)
1148 	ZEND_PARSE_PARAMETERS_END();
1149 
1150 	if (FG(default_context) == NULL) {
1151 		FG(default_context) = php_stream_context_alloc();
1152 	}
1153 	context = FG(default_context);
1154 
1155 	if (params) {
1156 		if (parse_context_options(context, params) == FAILURE) {
1157 			RETURN_THROWS();
1158 		}
1159 	}
1160 
1161 	php_stream_context_to_zval(context, return_value);
1162 }
1163 /* }}} */
1164 
1165 /* {{{ Set default file/stream context, returns the context as a resource */
PHP_FUNCTION(stream_context_set_default)1166 PHP_FUNCTION(stream_context_set_default)
1167 {
1168 	HashTable *options;
1169 	php_stream_context *context;
1170 
1171 	ZEND_PARSE_PARAMETERS_START(1, 1)
1172 		Z_PARAM_ARRAY_HT(options)
1173 	ZEND_PARSE_PARAMETERS_END();
1174 
1175 	if (FG(default_context) == NULL) {
1176 		FG(default_context) = php_stream_context_alloc();
1177 	}
1178 	context = FG(default_context);
1179 
1180 	if (parse_context_options(context, options) == FAILURE) {
1181 		RETURN_THROWS();
1182 	}
1183 
1184 	php_stream_context_to_zval(context, return_value);
1185 }
1186 /* }}} */
1187 
1188 /* {{{ Create a file context and optionally set parameters */
PHP_FUNCTION(stream_context_create)1189 PHP_FUNCTION(stream_context_create)
1190 {
1191 	HashTable *options = NULL;
1192 	HashTable *params = NULL;
1193 	php_stream_context *context;
1194 
1195 	ZEND_PARSE_PARAMETERS_START(0, 2)
1196 		Z_PARAM_OPTIONAL
1197 		Z_PARAM_ARRAY_HT_OR_NULL(options)
1198 		Z_PARAM_ARRAY_HT_OR_NULL(params)
1199 	ZEND_PARSE_PARAMETERS_END();
1200 
1201 	context = php_stream_context_alloc();
1202 
1203 	if (options) {
1204 		parse_context_options(context, options);
1205 	}
1206 
1207 	if (params) {
1208 		parse_context_params(context, params);
1209 	}
1210 
1211 	RETURN_RES(context->res);
1212 }
1213 /* }}} */
1214 
1215 /* {{{ streams filter functions */
apply_filter_to_stream(int append,INTERNAL_FUNCTION_PARAMETERS)1216 static void apply_filter_to_stream(int append, INTERNAL_FUNCTION_PARAMETERS)
1217 {
1218 	zval *zstream;
1219 	php_stream *stream;
1220 	char *filtername;
1221 	size_t filternamelen;
1222 	zend_long read_write = 0;
1223 	zval *filterparams = NULL;
1224 	php_stream_filter *filter = NULL;
1225 	int ret;
1226 
1227 	ZEND_PARSE_PARAMETERS_START(2, 4)
1228 		Z_PARAM_RESOURCE(zstream)
1229 		Z_PARAM_STRING(filtername, filternamelen)
1230 		Z_PARAM_OPTIONAL
1231 		Z_PARAM_LONG(read_write)
1232 		Z_PARAM_ZVAL(filterparams)
1233 	ZEND_PARSE_PARAMETERS_END();
1234 
1235 	php_stream_from_zval(stream, zstream);
1236 
1237 	if ((read_write & PHP_STREAM_FILTER_ALL) == 0) {
1238 		/* Chain not specified.
1239 		 * Examine stream->mode to determine which filters are needed
1240 		 * There's no harm in attaching a filter to an unused chain,
1241 		 * but why waste the memory and clock cycles?
1242 		 */
1243 		if (strchr(stream->mode, 'r') || strchr(stream->mode, '+')) {
1244 			read_write |= PHP_STREAM_FILTER_READ;
1245 		}
1246 		if (strchr(stream->mode, 'w') || strchr(stream->mode, '+') || strchr(stream->mode, 'a')) {
1247 			read_write |= PHP_STREAM_FILTER_WRITE;
1248 		}
1249 	}
1250 
1251 	if (read_write & PHP_STREAM_FILTER_READ) {
1252 		filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream));
1253 		if (filter == NULL) {
1254 			RETURN_FALSE;
1255 		}
1256 
1257 		if (append) {
1258 			ret = php_stream_filter_append_ex(&stream->readfilters, filter);
1259 		} else {
1260 			ret = php_stream_filter_prepend_ex(&stream->readfilters, filter);
1261 		}
1262 		if (ret != SUCCESS) {
1263 			php_stream_filter_remove(filter, 1);
1264 			RETURN_FALSE;
1265 		}
1266 	}
1267 
1268 	if (read_write & PHP_STREAM_FILTER_WRITE) {
1269 		filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream));
1270 		if (filter == NULL) {
1271 			RETURN_FALSE;
1272 		}
1273 
1274 		if (append) {
1275 			ret = php_stream_filter_append_ex(&stream->writefilters, filter);
1276 		} else {
1277 			ret = php_stream_filter_prepend_ex(&stream->writefilters, filter);
1278 		}
1279 		if (ret != SUCCESS) {
1280 			php_stream_filter_remove(filter, 1);
1281 			RETURN_FALSE;
1282 		}
1283 	}
1284 
1285 	if (filter) {
1286 		filter->res = zend_register_resource(filter, php_file_le_stream_filter());
1287 		GC_ADDREF(filter->res);
1288 		RETURN_RES(filter->res);
1289 	} else {
1290 		RETURN_FALSE;
1291 	}
1292 }
1293 /* }}} */
1294 
1295 /* {{{ Prepend a filter to a stream */
PHP_FUNCTION(stream_filter_prepend)1296 PHP_FUNCTION(stream_filter_prepend)
1297 {
1298 	apply_filter_to_stream(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1299 }
1300 /* }}} */
1301 
1302 /* {{{ Append a filter to a stream */
PHP_FUNCTION(stream_filter_append)1303 PHP_FUNCTION(stream_filter_append)
1304 {
1305 	apply_filter_to_stream(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1306 }
1307 /* }}} */
1308 
1309 /* {{{ Flushes any data in the filter's internal buffer, removes it from the chain, and frees the resource */
PHP_FUNCTION(stream_filter_remove)1310 PHP_FUNCTION(stream_filter_remove)
1311 {
1312 	zval *zfilter;
1313 	php_stream_filter *filter;
1314 
1315 	ZEND_PARSE_PARAMETERS_START(1, 1)
1316 		Z_PARAM_RESOURCE(zfilter)
1317 	ZEND_PARSE_PARAMETERS_END();
1318 
1319 	filter = zend_fetch_resource(Z_RES_P(zfilter), "stream filter", php_file_le_stream_filter());
1320 	if (!filter) {
1321 		RETURN_THROWS();
1322 	}
1323 
1324 	if (php_stream_filter_flush(filter, 1) == FAILURE) {
1325 		php_error_docref(NULL, E_WARNING, "Unable to flush filter, not removing");
1326 		RETURN_FALSE;
1327 	}
1328 
1329 	zend_list_close(Z_RES_P(zfilter));
1330 	php_stream_filter_remove(filter, 1);
1331 	RETURN_TRUE;
1332 }
1333 /* }}} */
1334 
1335 /* {{{ Read up to maxlen bytes from a stream or until the ending string is found */
PHP_FUNCTION(stream_get_line)1336 PHP_FUNCTION(stream_get_line)
1337 {
1338 	char *str = NULL;
1339 	size_t str_len = 0;
1340 	zend_long max_length;
1341 	zval *zstream;
1342 	zend_string *buf;
1343 	php_stream *stream;
1344 
1345 	ZEND_PARSE_PARAMETERS_START(2, 3)
1346 		Z_PARAM_RESOURCE(zstream)
1347 		Z_PARAM_LONG(max_length)
1348 		Z_PARAM_OPTIONAL
1349 		Z_PARAM_STRING(str, str_len)
1350 	ZEND_PARSE_PARAMETERS_END();
1351 
1352 	if (max_length < 0) {
1353 		zend_argument_value_error(2, "must be greater than or equal to 0");
1354 		RETURN_THROWS();
1355 	}
1356 	if (!max_length) {
1357 		max_length = PHP_SOCK_CHUNK_SIZE;
1358 	}
1359 
1360 	php_stream_from_zval(stream, zstream);
1361 
1362 	if ((buf = php_stream_get_record(stream, max_length, str, str_len))) {
1363 		RETURN_STR(buf);
1364 	} else {
1365 		RETURN_FALSE;
1366 	}
1367 }
1368 
1369 /* }}} */
1370 
1371 /* {{{ Set blocking/non-blocking mode on a socket or stream */
PHP_FUNCTION(stream_set_blocking)1372 PHP_FUNCTION(stream_set_blocking)
1373 {
1374 	zval *zstream;
1375 	bool block;
1376 	php_stream *stream;
1377 
1378 	ZEND_PARSE_PARAMETERS_START(2, 2)
1379 		Z_PARAM_RESOURCE(zstream)
1380 		Z_PARAM_BOOL(block)
1381 	ZEND_PARSE_PARAMETERS_END();
1382 
1383 	php_stream_from_zval(stream, zstream);
1384 
1385 	if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL) == -1) {
1386 		RETURN_FALSE;
1387 	}
1388 
1389 	RETURN_TRUE;
1390 }
1391 
1392 /* }}} */
1393 
1394 /* {{{ Set timeout on stream read to seconds + microseonds */
1395 #if defined(HAVE_SYS_TIME_H) || defined(PHP_WIN32)
PHP_FUNCTION(stream_set_timeout)1396 PHP_FUNCTION(stream_set_timeout)
1397 {
1398 	zval *socket;
1399 	zend_long seconds, microseconds = 0;
1400 	struct timeval t;
1401 	php_stream *stream;
1402 	int argc = ZEND_NUM_ARGS();
1403 
1404 	ZEND_PARSE_PARAMETERS_START(2, 3)
1405 		Z_PARAM_RESOURCE(socket)
1406 		Z_PARAM_LONG(seconds)
1407 		Z_PARAM_OPTIONAL
1408 		Z_PARAM_LONG(microseconds)
1409 	ZEND_PARSE_PARAMETERS_END();
1410 
1411 	php_stream_from_zval(stream, socket);
1412 
1413 #ifdef PHP_WIN32
1414 	t.tv_sec = (long)seconds;
1415 
1416 	if (argc == 3) {
1417 		t.tv_usec = (long)(microseconds % 1000000);
1418 		t.tv_sec +=(long)(microseconds / 1000000);
1419 	} else {
1420 		t.tv_usec = 0;
1421 	}
1422 #else
1423 	t.tv_sec = seconds;
1424 
1425 	if (argc == 3) {
1426 		t.tv_usec = microseconds % 1000000;
1427 		t.tv_sec += microseconds / 1000000;
1428 	} else {
1429 		t.tv_usec = 0;
1430 	}
1431 #endif
1432 
1433 	if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)) {
1434 		RETURN_TRUE;
1435 	}
1436 
1437 	RETURN_FALSE;
1438 }
1439 #endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */
1440 /* }}} */
1441 
1442 /* {{{ Set file write buffer */
PHP_FUNCTION(stream_set_write_buffer)1443 PHP_FUNCTION(stream_set_write_buffer)
1444 {
1445 	zval *arg1;
1446 	int ret;
1447 	zend_long arg2;
1448 	size_t buff;
1449 	php_stream *stream;
1450 
1451 	ZEND_PARSE_PARAMETERS_START(2, 2)
1452 		Z_PARAM_RESOURCE(arg1)
1453 		Z_PARAM_LONG(arg2)
1454 	ZEND_PARSE_PARAMETERS_END();
1455 
1456 	php_stream_from_zval(stream, arg1);
1457 
1458 	buff = arg2;
1459 
1460 	/* if buff is 0 then set to non-buffered */
1461 	if (buff == 0) {
1462 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1463 	} else {
1464 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1465 	}
1466 
1467 	RETURN_LONG(ret == 0 ? 0 : EOF);
1468 }
1469 /* }}} */
1470 
1471 /* {{{ Set the stream chunk size */
PHP_FUNCTION(stream_set_chunk_size)1472 PHP_FUNCTION(stream_set_chunk_size)
1473 {
1474 	int			ret;
1475 	zend_long		csize;
1476 	zval		*zstream;
1477 	php_stream	*stream;
1478 
1479 	ZEND_PARSE_PARAMETERS_START(2, 2)
1480 		Z_PARAM_RESOURCE(zstream)
1481 		Z_PARAM_LONG(csize)
1482 	ZEND_PARSE_PARAMETERS_END();
1483 
1484 	if (csize <= 0) {
1485 		zend_argument_value_error(2, "must be greater than 0");
1486 		RETURN_THROWS();
1487 	}
1488 	/* stream.chunk_size is actually a size_t, but php_stream_set_option
1489 	 * can only use an int to accept the new value and return the old one.
1490 	 * In any case, values larger than INT_MAX for a chunk size make no sense.
1491 	 */
1492 	if (csize > INT_MAX) {
1493 		zend_argument_value_error(2, "is too large");
1494 		RETURN_THROWS();
1495 	}
1496 
1497 	php_stream_from_zval(stream, zstream);
1498 
1499 	ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL);
1500 
1501 	RETURN_LONG(ret > 0 ? (zend_long)ret : (zend_long)EOF);
1502 }
1503 /* }}} */
1504 
1505 /* {{{ Set file read buffer */
PHP_FUNCTION(stream_set_read_buffer)1506 PHP_FUNCTION(stream_set_read_buffer)
1507 {
1508 	zval *arg1;
1509 	int ret;
1510 	zend_long arg2;
1511 	size_t buff;
1512 	php_stream *stream;
1513 
1514 	ZEND_PARSE_PARAMETERS_START(2, 2)
1515 		Z_PARAM_RESOURCE(arg1)
1516 		Z_PARAM_LONG(arg2)
1517 	ZEND_PARSE_PARAMETERS_END();
1518 
1519 	php_stream_from_zval(stream, arg1);
1520 
1521 	buff = arg2;
1522 
1523 	/* if buff is 0 then set to non-buffered */
1524 	if (buff == 0) {
1525 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1526 	} else {
1527 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1528 	}
1529 
1530 	RETURN_LONG(ret == 0 ? 0 : EOF);
1531 }
1532 /* }}} */
1533 
1534 /* {{{ Enable or disable a specific kind of crypto on the stream */
PHP_FUNCTION(stream_socket_enable_crypto)1535 PHP_FUNCTION(stream_socket_enable_crypto)
1536 {
1537 	zend_long cryptokind = 0;
1538 	zval *zstream, *zsessstream = NULL;
1539 	php_stream *stream, *sessstream = NULL;
1540 	bool enable, cryptokindnull = 1;
1541 	int ret;
1542 
1543 	ZEND_PARSE_PARAMETERS_START(2, 4)
1544 		Z_PARAM_RESOURCE(zstream)
1545 		Z_PARAM_BOOL(enable)
1546 		Z_PARAM_OPTIONAL
1547 		Z_PARAM_LONG_OR_NULL(cryptokind, cryptokindnull)
1548 		Z_PARAM_RESOURCE_OR_NULL(zsessstream)
1549 	ZEND_PARSE_PARAMETERS_END();
1550 
1551 	php_stream_from_zval(stream, zstream);
1552 
1553 	if (enable) {
1554 		if (cryptokindnull) {
1555 			zval *val;
1556 
1557 			if (!GET_CTX_OPT(stream, "ssl", "crypto_method", val)) {
1558 				zend_argument_value_error(3, "must be specified when enabling encryption");
1559 				RETURN_THROWS();
1560 			}
1561 
1562 			cryptokind = Z_LVAL_P(val);
1563 		}
1564 
1565 		if (zsessstream) {
1566 			php_stream_from_zval(sessstream, zsessstream);
1567 		}
1568 
1569 		if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream) < 0) {
1570 			RETURN_FALSE;
1571 		}
1572 	}
1573 
1574 	ret = php_stream_xport_crypto_enable(stream, enable);
1575 	switch (ret) {
1576 		case -1:
1577 			RETURN_FALSE;
1578 
1579 		case 0:
1580 			RETURN_LONG(0);
1581 
1582 		default:
1583 			RETURN_TRUE;
1584 	}
1585 }
1586 /* }}} */
1587 
1588 /* {{{ Determine what file will be opened by calls to fopen() with a relative path */
PHP_FUNCTION(stream_resolve_include_path)1589 PHP_FUNCTION(stream_resolve_include_path)
1590 {
1591 	zend_string *filename;
1592 	zend_string *resolved_path;
1593 
1594 	ZEND_PARSE_PARAMETERS_START(1, 1)
1595 		Z_PARAM_PATH_STR(filename)
1596 	ZEND_PARSE_PARAMETERS_END();
1597 
1598 	resolved_path = zend_resolve_path(filename);
1599 
1600 	if (resolved_path) {
1601 		RETURN_STR(resolved_path);
1602 	}
1603 	RETURN_FALSE;
1604 }
1605 /* }}} */
1606 
1607 /* {{{ */
PHP_FUNCTION(stream_is_local)1608 PHP_FUNCTION(stream_is_local)
1609 {
1610 	zval *zstream;
1611 	php_stream *stream = NULL;
1612 	php_stream_wrapper *wrapper = NULL;
1613 
1614 	ZEND_PARSE_PARAMETERS_START(1, 1)
1615 		Z_PARAM_ZVAL(zstream)
1616 	ZEND_PARSE_PARAMETERS_END();
1617 
1618 	if (Z_TYPE_P(zstream) == IS_RESOURCE) {
1619 		php_stream_from_zval(stream, zstream);
1620 		if (stream == NULL) {
1621 			RETURN_FALSE;
1622 		}
1623 		wrapper = stream->wrapper;
1624 	} else {
1625 		if (!try_convert_to_string(zstream)) {
1626 			RETURN_THROWS();
1627 		}
1628 
1629 		wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0);
1630 	}
1631 
1632 	if (!wrapper) {
1633 		RETURN_FALSE;
1634 	}
1635 
1636 	RETURN_BOOL(wrapper->is_url==0);
1637 }
1638 /* }}} */
1639 
1640 /* {{{ Tells whether the stream supports locking through flock(). */
PHP_FUNCTION(stream_supports_lock)1641 PHP_FUNCTION(stream_supports_lock)
1642 {
1643 	php_stream *stream;
1644 	zval *zsrc;
1645 
1646 	ZEND_PARSE_PARAMETERS_START(1, 1)
1647 		Z_PARAM_RESOURCE(zsrc)
1648 	ZEND_PARSE_PARAMETERS_END();
1649 
1650 	php_stream_from_zval(stream, zsrc);
1651 
1652 	if (!php_stream_supports_lock(stream)) {
1653 		RETURN_FALSE;
1654 	}
1655 
1656 	RETURN_TRUE;
1657 }
1658 
1659 /* {{{ Check if a stream is a TTY. */
PHP_FUNCTION(stream_isatty)1660 PHP_FUNCTION(stream_isatty)
1661 {
1662 	zval *zsrc;
1663 	php_stream *stream;
1664 	php_socket_t fileno;
1665 
1666 	ZEND_PARSE_PARAMETERS_START(1, 1)
1667 		Z_PARAM_RESOURCE(zsrc)
1668 	ZEND_PARSE_PARAMETERS_END();
1669 
1670 	php_stream_from_zval(stream, zsrc);
1671 
1672 	/* get the fd.
1673 	 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting.
1674 	 * It is only used here so that the buffered data warning is not displayed.
1675 	 */
1676 	if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
1677 		php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
1678 	} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
1679 		php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
1680 	} else {
1681 		RETURN_FALSE;
1682 	}
1683 
1684 #ifdef PHP_WIN32
1685 	/* Check if the Windows standard handle is redirected to file */
1686 	RETVAL_BOOL(php_win32_console_fileno_is_console(fileno));
1687 #elif HAVE_UNISTD_H
1688 	/* Check if the file descriptor identifier is a terminal */
1689 	RETVAL_BOOL(isatty(fileno));
1690 #else
1691 	{
1692 		zend_stat_t stat = {0};
1693 		RETVAL_BOOL(zend_fstat(fileno, &stat) == 0 && (stat.st_mode & /*S_IFMT*/0170000) == /*S_IFCHR*/0020000);
1694 	}
1695 #endif
1696 }
1697 
1698 #ifdef PHP_WIN32
1699 /* {{{ Get or set VT100 support for the specified stream associated to an
1700    output buffer of a Windows console.
1701 */
PHP_FUNCTION(sapi_windows_vt100_support)1702 PHP_FUNCTION(sapi_windows_vt100_support)
1703 {
1704 	zval *zsrc;
1705 	php_stream *stream;
1706 	bool enable, enable_is_null = 1;
1707 	zend_long fileno;
1708 
1709 	ZEND_PARSE_PARAMETERS_START(1, 2)
1710 		Z_PARAM_RESOURCE(zsrc)
1711 		Z_PARAM_OPTIONAL
1712 		Z_PARAM_BOOL_OR_NULL(enable, enable_is_null)
1713 	ZEND_PARSE_PARAMETERS_END();
1714 
1715 	php_stream_from_zval(stream, zsrc);
1716 
1717 	/* get the fd.
1718 	 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting.
1719 	 * It is only used here so that the buffered data warning is not displayed.
1720 	 */
1721 	if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
1722 		php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
1723 	} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
1724 		php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
1725 	} else {
1726 		if (!enable_is_null) {
1727 			php_error_docref(
1728 				NULL,
1729 				E_WARNING,
1730 				"not able to analyze the specified stream"
1731 			);
1732 		}
1733 		RETURN_FALSE;
1734 	}
1735 
1736 	/* Check if the file descriptor is a console */
1737 	if (!php_win32_console_fileno_is_console(fileno)) {
1738 		RETURN_FALSE;
1739 	}
1740 
1741 	if (enable_is_null) {
1742 		/* Check if the Windows standard handle has VT100 control codes enabled */
1743 		if (php_win32_console_fileno_has_vt100(fileno)) {
1744 			RETURN_TRUE;
1745 		}
1746 		else {
1747 			RETURN_FALSE;
1748 		}
1749 	}
1750 	else {
1751 		/* Enable/disable VT100 control codes support for the specified Windows standard handle */
1752 		if (php_win32_console_fileno_set_vt100(fileno, enable ? TRUE : FALSE)) {
1753 			RETURN_TRUE;
1754 		}
1755 		else {
1756 			RETURN_FALSE;
1757 		}
1758 	}
1759 }
1760 #endif
1761 
1762 #ifdef HAVE_SHUTDOWN
1763 /* {{{ causes all or part of a full-duplex connection on the socket associated
1764 	with stream to be shut down.  If how is SHUT_RD,  further receptions will
1765 	be disallowed. If how is SHUT_WR, further transmissions will be disallowed.
1766 	If how is SHUT_RDWR,  further  receptions and transmissions will be
1767 	disallowed. */
PHP_FUNCTION(stream_socket_shutdown)1768 PHP_FUNCTION(stream_socket_shutdown)
1769 {
1770 	zend_long how;
1771 	zval *zstream;
1772 	php_stream *stream;
1773 
1774 	ZEND_PARSE_PARAMETERS_START(2, 2)
1775 		Z_PARAM_RESOURCE(zstream)
1776 		Z_PARAM_LONG(how)
1777 	ZEND_PARSE_PARAMETERS_END();
1778 
1779 	if (how != STREAM_SHUT_RD &&
1780 	    how != STREAM_SHUT_WR &&
1781 	    how != STREAM_SHUT_RDWR) {
1782 	    zend_argument_value_error(2, "must be one of STREAM_SHUT_RD, STREAM_SHUT_WR, or STREAM_SHUT_RDWR");
1783 		RETURN_THROWS();
1784 	}
1785 
1786 	php_stream_from_zval(stream, zstream);
1787 
1788 	RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0);
1789 }
1790 /* }}} */
1791 #endif
1792