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