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