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