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