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