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