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