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		ZVAL_DEREF(elem);
617		php_stream_from_zval_no_verify(stream, elem);
618		if (stream == NULL) {
619			continue;
620		}
621		/* get the fd.
622		 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
623		 * when casting.  It is only used here so that the buffered data warning
624		 * is not displayed.
625		 * */
626		if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) && this_fd != -1) {
627
628			PHP_SAFE_FD_SET(this_fd, fds);
629
630			if (this_fd > *max_fd) {
631				*max_fd = this_fd;
632			}
633			cnt++;
634		}
635	} ZEND_HASH_FOREACH_END();
636	return cnt ? 1 : 0;
637}
638
639static int stream_array_from_fd_set(zval *stream_array, fd_set *fds)
640{
641	zval *elem, *dest_elem, new_array;
642	php_stream *stream;
643	int ret = 0;
644	zend_string *key;
645	zend_ulong num_ind;
646
647	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
648		return 0;
649	}
650	ZVAL_NEW_ARR(&new_array);
651	zend_hash_init(Z_ARRVAL(new_array), zend_hash_num_elements(Z_ARRVAL_P(stream_array)), NULL, ZVAL_PTR_DTOR, 0);
652
653	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) {
654		php_socket_t this_fd;
655
656		ZVAL_DEREF(elem);
657		php_stream_from_zval_no_verify(stream, elem);
658		if (stream == NULL) {
659			continue;
660		}
661		/* get the fd
662		 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
663		 * when casting.  It is only used here so that the buffered data warning
664		 * is not displayed.
665		 */
666		if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) && this_fd != SOCK_ERR) {
667			if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
668				if (!key) {
669					dest_elem = zend_hash_index_update(Z_ARRVAL(new_array), num_ind, elem);
670				} else {
671					dest_elem = zend_hash_update(Z_ARRVAL(new_array), key, elem);
672				}
673
674				if (dest_elem) {
675					zval_add_ref(dest_elem);
676				}
677				ret++;
678				continue;
679			}
680		}
681	} ZEND_HASH_FOREACH_END();
682
683	/* destroy old array and add new one */
684	zend_array_destroy(Z_ARR_P(stream_array));
685	Z_ARR_P(stream_array) = Z_ARR(new_array);
686
687	return ret;
688}
689
690static int stream_array_emulate_read_fd_set(zval *stream_array)
691{
692	zval *elem, *dest_elem, new_array;
693	php_stream *stream;
694	int ret = 0;
695
696	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
697		return 0;
698	}
699	ZVAL_NEW_ARR(&new_array);
700	zend_hash_init(Z_ARRVAL(new_array), zend_hash_num_elements(Z_ARRVAL_P(stream_array)), NULL, ZVAL_PTR_DTOR, 0);
701
702	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(stream_array), elem) {
703		ZVAL_DEREF(elem);
704		php_stream_from_zval_no_verify(stream, elem);
705		if (stream == NULL) {
706			continue;
707		}
708		if ((stream->writepos - stream->readpos) > 0) {
709			/* allow readable non-descriptor based streams to participate in stream_select.
710			 * Non-descriptor streams will only "work" if they have previously buffered the
711			 * data.  Not ideal, but better than nothing.
712			 * This branch of code also allows blocking streams with buffered data to
713			 * operate correctly in stream_select.
714			 * */
715			dest_elem = zend_hash_next_index_insert(Z_ARRVAL(new_array), elem);
716			if (dest_elem) {
717				zval_add_ref(dest_elem);
718			}
719			ret++;
720			continue;
721		}
722	} ZEND_HASH_FOREACH_END();
723
724	if (ret > 0) {
725		/* destroy old array and add new one */
726		zend_array_destroy(Z_ARR_P(stream_array));
727		Z_ARR_P(stream_array) = Z_ARR(new_array);
728	} else {
729		zend_array_destroy(Z_ARR(new_array));
730	}
731
732	return ret;
733}
734/* }}} */
735
736/* {{{ proto int stream_select(array &read_streams, array &write_streams, array &except_streams, int tv_sec[, int tv_usec])
737   Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */
738PHP_FUNCTION(stream_select)
739{
740	zval *r_array, *w_array, *e_array;
741	struct timeval tv, *tv_p = NULL;
742	fd_set rfds, wfds, efds;
743	php_socket_t max_fd = 0;
744	int retval, sets = 0;
745	zend_long sec, usec = 0;
746	zend_bool secnull;
747	int set_count, max_set_count = 0;
748
749#ifndef FAST_ZPP
750	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/!a/!a/!l!|l", &r_array, &w_array, &e_array, &sec, &secnull, &usec) == FAILURE) {
751		return;
752	}
753#else
754	ZEND_PARSE_PARAMETERS_START(4, 5)
755		Z_PARAM_ARRAY_EX(r_array, 1, 1)
756		Z_PARAM_ARRAY_EX(w_array, 1, 1)
757		Z_PARAM_ARRAY_EX(e_array, 1, 1)
758		Z_PARAM_LONG_EX(sec, secnull, 1, 0)
759		Z_PARAM_OPTIONAL
760		Z_PARAM_LONG(usec)
761	ZEND_PARSE_PARAMETERS_END();
762#endif
763
764	FD_ZERO(&rfds);
765	FD_ZERO(&wfds);
766	FD_ZERO(&efds);
767
768	if (r_array != NULL) {
769		set_count = stream_array_to_fd_set(r_array, &rfds, &max_fd);
770		if (set_count > max_set_count)
771			max_set_count = set_count;
772		sets += set_count;
773	}
774
775	if (w_array != NULL) {
776		set_count = stream_array_to_fd_set(w_array, &wfds, &max_fd);
777		if (set_count > max_set_count)
778			max_set_count = set_count;
779		sets += set_count;
780	}
781
782	if (e_array != NULL) {
783		set_count = stream_array_to_fd_set(e_array, &efds, &max_fd);
784		if (set_count > max_set_count)
785			max_set_count = set_count;
786		sets += set_count;
787	}
788
789	if (!sets) {
790		php_error_docref(NULL, E_WARNING, "No stream arrays were passed");
791		RETURN_FALSE;
792	}
793
794	PHP_SAFE_MAX_FD(max_fd, max_set_count);
795
796	/* If seconds is not set to null, build the timeval, else we wait indefinitely */
797	if (!secnull) {
798		if (sec < 0) {
799			php_error_docref(NULL, E_WARNING, "The seconds parameter must be greater than 0");
800			RETURN_FALSE;
801		} else if (usec < 0) {
802			php_error_docref(NULL, E_WARNING, "The microseconds parameter must be greater than 0");
803			RETURN_FALSE;
804		}
805
806		/* Windows, Solaris and BSD do not like microsecond values which are >= 1 sec */
807		tv.tv_sec = (long)(sec + (usec / 1000000));
808		tv.tv_usec = (long)(usec % 1000000);
809		tv_p = &tv;
810	}
811
812	/* slight hack to support buffered data; if there is data sitting in the
813	 * read buffer of any of the streams in the read array, let's pretend
814	 * that we selected, but return only the readable sockets */
815	if (r_array != NULL) {
816		retval = stream_array_emulate_read_fd_set(r_array);
817		if (retval > 0) {
818			if (w_array != NULL) {
819				zend_hash_clean(Z_ARRVAL_P(w_array));
820			}
821			if (e_array != NULL) {
822				zend_hash_clean(Z_ARRVAL_P(e_array));
823			}
824			RETURN_LONG(retval);
825		}
826	}
827
828	retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p);
829
830	if (retval == -1) {
831		php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
832				errno, strerror(errno), max_fd);
833		RETURN_FALSE;
834	}
835
836	if (r_array != NULL) stream_array_from_fd_set(r_array, &rfds);
837	if (w_array != NULL) stream_array_from_fd_set(w_array, &wfds);
838	if (e_array != NULL) stream_array_from_fd_set(e_array, &efds);
839
840	RETURN_LONG(retval);
841}
842/* }}} */
843
844/* {{{ stream_context related functions */
845static void user_space_stream_notifier(php_stream_context *context, int notifycode, int severity,
846		char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr)
847{
848	zval *callback = &context->notifier->ptr;
849	zval retval;
850	zval zvs[6];
851	int i;
852
853	ZVAL_LONG(&zvs[0], notifycode);
854	ZVAL_LONG(&zvs[1], severity);
855	if (xmsg) {
856		ZVAL_STRING(&zvs[2], xmsg);
857	} else {
858		ZVAL_NULL(&zvs[2]);
859	}
860	ZVAL_LONG(&zvs[3], xcode);
861	ZVAL_LONG(&zvs[4], bytes_sofar);
862	ZVAL_LONG(&zvs[5], bytes_max);
863
864	if (FAILURE == call_user_function_ex(EG(function_table), NULL, callback, &retval, 6, zvs, 0, NULL)) {
865		php_error_docref(NULL, E_WARNING, "failed to call user notifier");
866	}
867	for (i = 0; i < 6; i++) {
868		zval_ptr_dtor(&zvs[i]);
869	}
870	zval_ptr_dtor(&retval);
871}
872
873static void user_space_stream_notifier_dtor(php_stream_notifier *notifier)
874{
875	if (notifier && Z_TYPE(notifier->ptr) != IS_UNDEF) {
876		zval_ptr_dtor(&notifier->ptr);
877		ZVAL_UNDEF(&notifier->ptr);
878	}
879}
880
881static int parse_context_options(php_stream_context *context, zval *options)
882{
883	zval *wval, *oval;
884	zend_string *wkey, *okey;
885	int ret = SUCCESS;
886
887	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), wkey, wval) {
888		ZVAL_DEREF(wval);
889		if (wkey && Z_TYPE_P(wval) == IS_ARRAY) {
890			ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(wval), okey, oval) {
891				if (okey) {
892					php_stream_context_set_option(context, ZSTR_VAL(wkey), ZSTR_VAL(okey), oval);
893				}
894			} ZEND_HASH_FOREACH_END();
895		} else {
896			php_error_docref(NULL, E_WARNING, "options should have the form [\"wrappername\"][\"optionname\"] = $value");
897		}
898	} ZEND_HASH_FOREACH_END();
899
900	return ret;
901}
902
903static int parse_context_params(php_stream_context *context, zval *params)
904{
905	int ret = SUCCESS;
906	zval *tmp;
907
908	if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(params), "notification", sizeof("notification")-1))) {
909
910		if (context->notifier) {
911			php_stream_notification_free(context->notifier);
912			context->notifier = NULL;
913		}
914
915		context->notifier = php_stream_notification_alloc();
916		context->notifier->func = user_space_stream_notifier;
917		ZVAL_COPY(&context->notifier->ptr, tmp);
918		context->notifier->dtor = user_space_stream_notifier_dtor;
919	}
920	if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(params), "options", sizeof("options")-1))) {
921		if (Z_TYPE_P(tmp) == IS_ARRAY) {
922			parse_context_options(context, tmp);
923		} else {
924			php_error_docref(NULL, E_WARNING, "Invalid stream/context parameter");
925		}
926	}
927
928	return ret;
929}
930
931/* given a zval which is either a stream or a context, return the underlying
932 * stream_context.  If it is a stream that does not have a context assigned, it
933 * will create and assign a context and return that.  */
934static php_stream_context *decode_context_param(zval *contextresource)
935{
936	php_stream_context *context = NULL;
937
938	context = zend_fetch_resource_ex(contextresource, NULL, php_le_stream_context());
939	if (context == NULL) {
940		php_stream *stream;
941
942		stream = zend_fetch_resource2_ex(contextresource, NULL, php_file_le_stream(), php_file_le_pstream());
943
944		if (stream) {
945			context = PHP_STREAM_CONTEXT(stream);
946			if (context == NULL) {
947				/* Only way this happens is if file is opened with NO_DEFAULT_CONTEXT
948				   param, but then something is called which requires a context.
949				   Don't give them the default one though since they already said they
950	 			   didn't want it. */
951				context = php_stream_context_alloc();
952				stream->ctx = context->res;
953			}
954		}
955	}
956
957	return context;
958}
959/* }}} */
960
961/* {{{ proto array stream_context_get_options(resource context|resource stream)
962   Retrieve options for a stream/wrapper/context */
963PHP_FUNCTION(stream_context_get_options)
964{
965	zval *zcontext;
966	php_stream_context *context;
967
968#ifndef FAST_ZPP
969	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zcontext) == FAILURE) {
970		RETURN_FALSE;
971	}
972#else
973	ZEND_PARSE_PARAMETERS_START(1, 1)
974		Z_PARAM_RESOURCE(zcontext)
975	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
976#endif
977
978	context = decode_context_param(zcontext);
979	if (!context) {
980		php_error_docref(NULL, E_WARNING, "Invalid stream/context parameter");
981		RETURN_FALSE;
982	}
983
984	ZVAL_COPY(return_value, &context->options);
985}
986/* }}} */
987
988/* {{{ proto bool stream_context_set_option(resource context|resource stream, string wrappername, string optionname, mixed value)
989   Set an option for a wrapper */
990PHP_FUNCTION(stream_context_set_option)
991{
992	zval *zcontext = NULL;
993	php_stream_context *context;
994
995	if (ZEND_NUM_ARGS() == 2) {
996		zval *options;
997
998#ifndef FAST_ZPP
999		if (zend_parse_parameters(, ZEND_NUM_ARGS(), "ra", &zcontext, &options) == FAILURE) {
1000			RETURN_FALSE;
1001		}
1002#else
1003		ZEND_PARSE_PARAMETERS_START(2, 2)
1004			Z_PARAM_RESOURCE(zcontext)
1005			Z_PARAM_ARRAY(options)
1006		ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1007#endif
1008
1009		/* figure out where the context is coming from exactly */
1010		if (!(context = decode_context_param(zcontext))) {
1011			php_error_docref(NULL, E_WARNING, "Invalid stream/context parameter");
1012			RETURN_FALSE;
1013		}
1014
1015		RETURN_BOOL(parse_context_options(context, options) == SUCCESS);
1016	} else {
1017		zval *zvalue;
1018		char *wrappername, *optionname;
1019		size_t wrapperlen, optionlen;
1020
1021#ifndef FAST_ZPP
1022		if (zend_parse_parameters(ZEND_NUM_ARGS(), "rssz", &zcontext, &wrappername, &wrapperlen, &optionname, &optionlen, &zvalue) == FAILURE) {
1023			RETURN_FALSE;
1024		}
1025#else
1026		ZEND_PARSE_PARAMETERS_START(4, 4)
1027			Z_PARAM_RESOURCE(zcontext)
1028			Z_PARAM_STRING(wrappername, wrapperlen)
1029			Z_PARAM_STRING(optionname, optionlen)
1030			Z_PARAM_ZVAL(zvalue)
1031		ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1032#endif
1033
1034		/* figure out where the context is coming from exactly */
1035		if (!(context = decode_context_param(zcontext))) {
1036			php_error_docref(NULL, E_WARNING, "Invalid stream/context parameter");
1037			RETURN_FALSE;
1038		}
1039
1040		RETURN_BOOL(php_stream_context_set_option(context, wrappername, optionname, zvalue) == SUCCESS);
1041	}
1042}
1043/* }}} */
1044
1045/* {{{ proto bool stream_context_set_params(resource context|resource stream, array options)
1046   Set parameters for a file context */
1047PHP_FUNCTION(stream_context_set_params)
1048{
1049	zval *params, *zcontext;
1050	php_stream_context *context;
1051
1052#ifndef FAST_ZPP
1053	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &zcontext, &params) == FAILURE) {
1054		RETURN_FALSE;
1055	}
1056#else
1057	ZEND_PARSE_PARAMETERS_START(2, 2)
1058		Z_PARAM_RESOURCE(zcontext)
1059		Z_PARAM_ARRAY(params)
1060	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1061#endif
1062
1063	context = decode_context_param(zcontext);
1064	if (!context) {
1065		php_error_docref(NULL, E_WARNING, "Invalid stream/context parameter");
1066		RETURN_FALSE;
1067	}
1068
1069	RETVAL_BOOL(parse_context_params(context, params) == SUCCESS);
1070}
1071/* }}} */
1072
1073/* {{{ proto array stream_context_get_params(resource context|resource stream)
1074   Get parameters of a file context */
1075PHP_FUNCTION(stream_context_get_params)
1076{
1077	zval *zcontext;
1078	php_stream_context *context;
1079
1080#ifndef FAST_ZPP
1081	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zcontext) == FAILURE) {
1082		RETURN_FALSE;
1083	}
1084#else
1085	ZEND_PARSE_PARAMETERS_START(1, 1)
1086		Z_PARAM_RESOURCE(zcontext)
1087	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1088#endif
1089
1090	context = decode_context_param(zcontext);
1091	if (!context) {
1092		php_error_docref(NULL, E_WARNING, "Invalid stream/context parameter");
1093		RETURN_FALSE;
1094	}
1095
1096	array_init(return_value);
1097	if (context->notifier && Z_TYPE(context->notifier->ptr) != IS_UNDEF && context->notifier->func == user_space_stream_notifier) {
1098		add_assoc_zval_ex(return_value, "notification", sizeof("notification")-1, &context->notifier->ptr);
1099		if (Z_REFCOUNTED(context->notifier->ptr)) Z_ADDREF(context->notifier->ptr);
1100	}
1101	if (Z_REFCOUNTED(context->options)) Z_ADDREF(context->options);
1102	add_assoc_zval_ex(return_value, "options", sizeof("options")-1, &context->options);
1103}
1104/* }}} */
1105
1106/* {{{ proto resource stream_context_get_default([array options])
1107   Get a handle on the default file/stream context and optionally set parameters */
1108PHP_FUNCTION(stream_context_get_default)
1109{
1110	zval *params = NULL;
1111	php_stream_context *context;
1112
1113	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a", &params) == FAILURE) {
1114		RETURN_FALSE;
1115	}
1116
1117	if (FG(default_context) == NULL) {
1118		FG(default_context) = php_stream_context_alloc();
1119	}
1120	context = FG(default_context);
1121
1122	if (params) {
1123		parse_context_options(context, params);
1124	}
1125
1126	php_stream_context_to_zval(context, return_value);
1127}
1128/* }}} */
1129
1130/* {{{ proto resource stream_context_set_default(array options)
1131   Set default file/stream context, returns the context as a resource */
1132PHP_FUNCTION(stream_context_set_default)
1133{
1134	zval *options = NULL;
1135	php_stream_context *context;
1136
1137	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &options) == FAILURE) {
1138		return;
1139	}
1140
1141	if (FG(default_context) == NULL) {
1142		FG(default_context) = php_stream_context_alloc();
1143	}
1144	context = FG(default_context);
1145
1146	parse_context_options(context, options);
1147
1148	php_stream_context_to_zval(context, return_value);
1149}
1150/* }}} */
1151
1152/* {{{ proto resource stream_context_create([array options[, array params]])
1153   Create a file context and optionally set parameters */
1154PHP_FUNCTION(stream_context_create)
1155{
1156	zval *options = NULL, *params = NULL;
1157	php_stream_context *context;
1158
1159	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!a!", &options, &params) == FAILURE) {
1160		RETURN_FALSE;
1161	}
1162
1163	context = php_stream_context_alloc();
1164
1165	if (options) {
1166		parse_context_options(context, options);
1167	}
1168
1169	if (params) {
1170		parse_context_params(context, params);
1171	}
1172
1173	RETURN_RES(context->res);
1174}
1175/* }}} */
1176
1177/* {{{ streams filter functions */
1178static void apply_filter_to_stream(int append, INTERNAL_FUNCTION_PARAMETERS)
1179{
1180	zval *zstream;
1181	php_stream *stream;
1182	char *filtername;
1183	size_t filternamelen;
1184	zend_long read_write = 0;
1185	zval *filterparams = NULL;
1186	php_stream_filter *filter = NULL;
1187	int ret;
1188
1189	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|lz", &zstream,
1190				&filtername, &filternamelen, &read_write, &filterparams) == FAILURE) {
1191		RETURN_FALSE;
1192	}
1193
1194	php_stream_from_zval(stream, zstream);
1195
1196	if ((read_write & PHP_STREAM_FILTER_ALL) == 0) {
1197		/* Chain not specified.
1198		 * Examine stream->mode to determine which filters are needed
1199		 * There's no harm in attaching a filter to an unused chain,
1200		 * but why waste the memory and clock cycles?
1201		 */
1202		if (strchr(stream->mode, 'r') || strchr(stream->mode, '+')) {
1203			read_write |= PHP_STREAM_FILTER_READ;
1204		}
1205		if (strchr(stream->mode, 'w') || strchr(stream->mode, '+') || strchr(stream->mode, 'a')) {
1206			read_write |= PHP_STREAM_FILTER_WRITE;
1207		}
1208	}
1209
1210	if (read_write & PHP_STREAM_FILTER_READ) {
1211		filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream));
1212		if (filter == NULL) {
1213			RETURN_FALSE;
1214		}
1215
1216		if (append) {
1217			ret = php_stream_filter_append_ex(&stream->readfilters, filter);
1218		} else {
1219			ret = php_stream_filter_prepend_ex(&stream->readfilters, filter);
1220		}
1221		if (ret != SUCCESS) {
1222			php_stream_filter_remove(filter, 1);
1223			RETURN_FALSE;
1224		}
1225	}
1226
1227	if (read_write & PHP_STREAM_FILTER_WRITE) {
1228		filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream));
1229		if (filter == NULL) {
1230			RETURN_FALSE;
1231		}
1232
1233		if (append) {
1234			ret = php_stream_filter_append_ex(&stream->writefilters, filter);
1235		} else {
1236			ret = php_stream_filter_prepend_ex(&stream->writefilters, filter);
1237		}
1238		if (ret != SUCCESS) {
1239			php_stream_filter_remove(filter, 1);
1240			RETURN_FALSE;
1241		}
1242	}
1243
1244	if (filter) {
1245		filter->res = zend_register_resource(filter, php_file_le_stream_filter());
1246		GC_REFCOUNT(filter->res)++;
1247		RETURN_RES(filter->res);
1248	} else {
1249		RETURN_FALSE;
1250	}
1251}
1252/* }}} */
1253
1254/* {{{ proto resource stream_filter_prepend(resource stream, string filtername[, int read_write[, string filterparams]])
1255   Prepend a filter to a stream */
1256PHP_FUNCTION(stream_filter_prepend)
1257{
1258	apply_filter_to_stream(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1259}
1260/* }}} */
1261
1262/* {{{ proto resource stream_filter_append(resource stream, string filtername[, int read_write[, string filterparams]])
1263   Append a filter to a stream */
1264PHP_FUNCTION(stream_filter_append)
1265{
1266	apply_filter_to_stream(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1267}
1268/* }}} */
1269
1270/* {{{ proto bool stream_filter_remove(resource stream_filter)
1271	Flushes any data in the filter's internal buffer, removes it from the chain, and frees the resource */
1272PHP_FUNCTION(stream_filter_remove)
1273{
1274	zval *zfilter;
1275	php_stream_filter *filter;
1276
1277	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zfilter) == FAILURE) {
1278		RETURN_FALSE;
1279	}
1280
1281	filter = zend_fetch_resource(Z_RES_P(zfilter), NULL, php_file_le_stream_filter());
1282	if (!filter) {
1283		php_error_docref(NULL, E_WARNING, "Invalid resource given, not a stream filter");
1284		RETURN_FALSE;
1285	}
1286
1287	if (php_stream_filter_flush(filter, 1) == FAILURE) {
1288		php_error_docref(NULL, E_WARNING, "Unable to flush filter, not removing");
1289		RETURN_FALSE;
1290	}
1291
1292	if (zend_list_close(Z_RES_P(zfilter)) == FAILURE) {
1293		php_error_docref(NULL, E_WARNING, "Could not invalidate filter, not removing");
1294		RETURN_FALSE;
1295	} else {
1296		php_stream_filter_remove(filter, 1);
1297		RETURN_TRUE;
1298	}
1299}
1300/* }}} */
1301
1302/* {{{ proto string stream_get_line(resource stream, int maxlen [, string ending])
1303   Read up to maxlen bytes from a stream or until the ending string is found */
1304PHP_FUNCTION(stream_get_line)
1305{
1306	char *str = NULL;
1307	size_t str_len = 0;
1308	zend_long max_length;
1309	zval *zstream;
1310	zend_string *buf;
1311	php_stream *stream;
1312
1313	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|s", &zstream, &max_length, &str, &str_len) == FAILURE) {
1314		RETURN_FALSE;
1315	}
1316
1317	if (max_length < 0) {
1318		php_error_docref(NULL, E_WARNING, "The maximum allowed length must be greater than or equal to zero");
1319		RETURN_FALSE;
1320	}
1321	if (!max_length) {
1322		max_length = PHP_SOCK_CHUNK_SIZE;
1323	}
1324
1325	php_stream_from_zval(stream, zstream);
1326
1327	if ((buf = php_stream_get_record(stream, max_length, str, str_len))) {
1328		RETURN_STR(buf);
1329	} else {
1330		RETURN_FALSE;
1331	}
1332}
1333
1334/* }}} */
1335
1336/* {{{ proto bool stream_set_blocking(resource socket, bool mode)
1337   Set blocking/non-blocking mode on a socket or stream */
1338PHP_FUNCTION(stream_set_blocking)
1339{
1340	zval *zstream;
1341	zend_bool block;
1342	php_stream *stream;
1343
1344#ifndef FAST_ZPP
1345	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rb", &zstream, &block) == FAILURE) {
1346		return;
1347	}
1348#else
1349	ZEND_PARSE_PARAMETERS_START(2, 2)
1350		Z_PARAM_RESOURCE(zstream)
1351		Z_PARAM_BOOL(block)
1352	ZEND_PARSE_PARAMETERS_END();
1353#endif
1354
1355	php_stream_from_zval(stream, zstream);
1356
1357	if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL) == -1) {
1358		RETURN_FALSE;
1359	}
1360
1361	RETURN_TRUE;
1362}
1363
1364/* }}} */
1365
1366/* {{{ proto bool stream_set_timeout(resource stream, int seconds [, int microseconds])
1367   Set timeout on stream read to seconds + microseonds */
1368#if HAVE_SYS_TIME_H || defined(PHP_WIN32)
1369PHP_FUNCTION(stream_set_timeout)
1370{
1371	zval *socket;
1372	zend_long seconds, microseconds = 0;
1373	struct timeval t;
1374	php_stream *stream;
1375	int argc = ZEND_NUM_ARGS();
1376
1377	if (zend_parse_parameters(argc, "rl|l", &socket, &seconds, &microseconds) == FAILURE) {
1378		return;
1379	}
1380
1381	php_stream_from_zval(stream, socket);
1382
1383#ifdef PHP_WIN32
1384	t.tv_sec = (long)seconds;
1385
1386	if (argc == 3) {
1387		t.tv_usec = (long)(microseconds % 1000000);
1388		t.tv_sec +=(long)(microseconds / 1000000);
1389	} else {
1390		t.tv_usec = 0;
1391	}
1392#else
1393	t.tv_sec = seconds;
1394
1395	if (argc == 3) {
1396		t.tv_usec = microseconds % 1000000;
1397		t.tv_sec += microseconds / 1000000;
1398	} else {
1399		t.tv_usec = 0;
1400	}
1401#endif
1402
1403	if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)) {
1404		RETURN_TRUE;
1405	}
1406
1407	RETURN_FALSE;
1408}
1409#endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */
1410/* }}} */
1411
1412/* {{{ proto int stream_set_write_buffer(resource fp, int buffer)
1413   Set file write buffer */
1414PHP_FUNCTION(stream_set_write_buffer)
1415{
1416	zval *arg1;
1417	int ret;
1418	zend_long arg2;
1419	size_t buff;
1420	php_stream *stream;
1421
1422	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &arg1, &arg2) == FAILURE) {
1423		RETURN_FALSE;
1424	}
1425
1426	php_stream_from_zval(stream, arg1);
1427
1428	buff = arg2;
1429
1430	/* if buff is 0 then set to non-buffered */
1431	if (buff == 0) {
1432		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1433	} else {
1434		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1435	}
1436
1437	RETURN_LONG(ret == 0 ? 0 : EOF);
1438}
1439/* }}} */
1440
1441/* {{{ proto int stream_set_chunk_size(resource fp, int chunk_size)
1442   Set the stream chunk size */
1443PHP_FUNCTION(stream_set_chunk_size)
1444{
1445	int			ret;
1446	zend_long		csize;
1447	zval		*zstream;
1448	php_stream	*stream;
1449
1450	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &zstream, &csize) == FAILURE) {
1451		RETURN_FALSE;
1452	}
1453
1454	if (csize <= 0) {
1455		php_error_docref(NULL, E_WARNING, "The chunk size must be a positive integer, given " ZEND_LONG_FMT, csize);
1456		RETURN_FALSE;
1457	}
1458	/* stream.chunk_size is actually a size_t, but php_stream_set_option
1459	 * can only use an int to accept the new value and return the old one.
1460	 * In any case, values larger than INT_MAX for a chunk size make no sense.
1461	 */
1462	if (csize > INT_MAX) {
1463		php_error_docref(NULL, E_WARNING, "The chunk size cannot be larger than %d", INT_MAX);
1464		RETURN_FALSE;
1465	}
1466
1467	php_stream_from_zval(stream, zstream);
1468
1469	ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL);
1470
1471	RETURN_LONG(ret > 0 ? (zend_long)ret : (zend_long)EOF);
1472}
1473/* }}} */
1474
1475/* {{{ proto int stream_set_read_buffer(resource fp, int buffer)
1476   Set file read buffer */
1477PHP_FUNCTION(stream_set_read_buffer)
1478{
1479	zval *arg1;
1480	int ret;
1481	zend_long arg2;
1482	size_t buff;
1483	php_stream *stream;
1484
1485	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &arg1, &arg2) == FAILURE) {
1486		RETURN_FALSE;
1487	}
1488
1489	php_stream_from_zval(stream, arg1);
1490
1491	buff = arg2;
1492
1493	/* if buff is 0 then set to non-buffered */
1494	if (buff == 0) {
1495		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1496	} else {
1497		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1498	}
1499
1500	RETURN_LONG(ret == 0 ? 0 : EOF);
1501}
1502/* }}} */
1503
1504/* {{{ proto int stream_socket_enable_crypto(resource stream, bool enable [, int cryptokind [, resource sessionstream]])
1505   Enable or disable a specific kind of crypto on the stream */
1506PHP_FUNCTION(stream_socket_enable_crypto)
1507{
1508	zend_long cryptokind = 0;
1509	zval *zstream, *zsessstream = NULL;
1510	php_stream *stream, *sessstream = NULL;
1511	zend_bool enable, cryptokindnull;
1512	int ret;
1513
1514#ifdef FAST_ZPP
1515	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rb|l!r", &zstream, &enable, &cryptokind, &cryptokindnull, &zsessstream) == FAILURE) {
1516		RETURN_FALSE;
1517	}
1518#else
1519	ZEND_PARSE_PARAMETERS_START(2, 4)
1520		Z_PARAM_RESOURCE(zstream)
1521		Z_PARAM_BOOL(enable)
1522		Z_PARAM_OPTIONAL
1523		Z_PARAM_LONG_EX(cryptokind, cryptokindnull, 1, 0)
1524		Z_PARAM_RESOURCE(zsessstream)
1525	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1526#endif
1527
1528	php_stream_from_zval(stream, zstream);
1529
1530	if (enable) {
1531		if (ZEND_NUM_ARGS() < 3 || cryptokindnull) {
1532			zval *val;
1533
1534			if (!GET_CTX_OPT(stream, "ssl", "crypto_method", val)) {
1535				php_error_docref(NULL, E_WARNING, "When enabling encryption you must specify the crypto type");
1536				RETURN_FALSE;
1537			}
1538
1539			cryptokind = Z_LVAL_P(val);
1540		}
1541
1542		if (zsessstream) {
1543			php_stream_from_zval(sessstream, zsessstream);
1544		}
1545
1546		if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream) < 0) {
1547			RETURN_FALSE;
1548		}
1549	}
1550
1551	ret = php_stream_xport_crypto_enable(stream, enable);
1552	switch (ret) {
1553		case -1:
1554			RETURN_FALSE;
1555
1556		case 0:
1557			RETURN_LONG(0);
1558
1559		default:
1560			RETURN_TRUE;
1561	}
1562}
1563/* }}} */
1564
1565/* {{{ proto string stream_resolve_include_path(string filename)
1566Determine what file will be opened by calls to fopen() with a relative path */
1567PHP_FUNCTION(stream_resolve_include_path)
1568{
1569	char *filename;
1570	size_t filename_len;
1571	zend_string *resolved_path;
1572
1573	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
1574		return;
1575	}
1576
1577	resolved_path = zend_resolve_path(filename, (int)filename_len);
1578
1579	if (resolved_path) {
1580		RETURN_STR(resolved_path);
1581	}
1582	RETURN_FALSE;
1583}
1584/* }}} */
1585
1586/* {{{ proto bool stream_is_local(resource stream|string url) U
1587*/
1588PHP_FUNCTION(stream_is_local)
1589{
1590	zval *zstream;
1591	php_stream *stream = NULL;
1592	php_stream_wrapper *wrapper = NULL;
1593
1594	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zstream) == FAILURE) {
1595		RETURN_FALSE;
1596	}
1597
1598	if (Z_TYPE_P(zstream) == IS_RESOURCE) {
1599		php_stream_from_zval(stream, zstream);
1600		if (stream == NULL) {
1601			RETURN_FALSE;
1602		}
1603		wrapper = stream->wrapper;
1604	} else {
1605		convert_to_string_ex(zstream);
1606
1607		wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0);
1608	}
1609
1610	if (!wrapper) {
1611		RETURN_FALSE;
1612	}
1613
1614	RETURN_BOOL(wrapper->is_url==0);
1615}
1616/* }}} */
1617
1618/* {{{ proto bool stream_supports_lock(resource stream)
1619   Tells whether the stream supports locking through flock(). */
1620PHP_FUNCTION(stream_supports_lock)
1621{
1622	php_stream *stream;
1623	zval *zsrc;
1624
1625	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsrc) == FAILURE) {
1626		RETURN_FALSE;
1627	}
1628
1629	php_stream_from_zval(stream, zsrc);
1630
1631	if (!php_stream_supports_lock(stream)) {
1632		RETURN_FALSE;
1633	}
1634
1635	RETURN_TRUE;
1636}
1637
1638#ifdef HAVE_SHUTDOWN
1639/* {{{ proto int stream_socket_shutdown(resource stream, int how)
1640	causes all or part of a full-duplex connection on the socket associated
1641	with stream to be shut down.  If how is SHUT_RD,  further receptions will
1642	be disallowed. If how is SHUT_WR, further transmissions will be disallowed.
1643	If how is SHUT_RDWR,  further  receptions and transmissions will be
1644	disallowed. */
1645PHP_FUNCTION(stream_socket_shutdown)
1646{
1647	zend_long how;
1648	zval *zstream;
1649	php_stream *stream;
1650
1651#ifndef FAST_ZPP
1652	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &zstream, &how) == FAILURE) {
1653		RETURN_FALSE;
1654	}
1655#else
1656	ZEND_PARSE_PARAMETERS_START(2, 2)
1657		Z_PARAM_RESOURCE(zstream)
1658		Z_PARAM_LONG(how)
1659	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1660#endif
1661
1662	if (how != STREAM_SHUT_RD &&
1663	    how != STREAM_SHUT_WR &&
1664	    how != STREAM_SHUT_RDWR) {
1665		php_error_docref(NULL, E_WARNING, "Second parameter $how needs to be one of STREAM_SHUT_RD, STREAM_SHUT_WR or STREAM_SHUT_RDWR");
1666		RETURN_FALSE;
1667	}
1668
1669	php_stream_from_zval(stream, zstream);
1670
1671	RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0);
1672}
1673/* }}} */
1674#endif
1675
1676/*
1677 * Local variables:
1678 * tab-width: 4
1679 * c-basic-offset: 4
1680 * End:
1681 * vim600: noet sw=4 ts=4 fdm=marker
1682 * vim<600: noet sw=4 ts=4
1683 */
1684
1685