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