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