xref: /PHP-5.5/ext/standard/streamsfuncs.c (revision 6297a117)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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, 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, 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 		RETURN_STRINGL(read_buf, recvd, 0);
395 	}
396 
397 	efree(read_buf);
398 	RETURN_FALSE;
399 }
400 /* }}} */
401 
402 /* {{{ proto string stream_get_contents(resource source [, long maxlen [, long offset]])
403    Reads all remaining bytes (or up to maxlen bytes) from a stream and returns them as a string. */
PHP_FUNCTION(stream_get_contents)404 PHP_FUNCTION(stream_get_contents)
405 {
406 	php_stream	*stream;
407 	zval		*zsrc;
408 	long		maxlen		= PHP_STREAM_COPY_ALL,
409 				desiredpos	= -1L;
410 	long		len;
411 	char		*contents	= NULL;
412 
413 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ll", &zsrc, &maxlen, &desiredpos) == FAILURE) {
414 		RETURN_FALSE;
415 	}
416 
417 	php_stream_from_zval(stream, &zsrc);
418 
419 	if (desiredpos >= 0) {
420 		int		seek_res = 0;
421 		off_t	position;
422 
423 		position = php_stream_tell(stream);
424 		if (position >= 0 && desiredpos > position) {
425 			/* use SEEK_CUR to allow emulation in streams that don't support seeking */
426 			seek_res = php_stream_seek(stream, desiredpos - position, SEEK_CUR);
427 		} else if (desiredpos < position)  {
428 			/* desired position before position or error on tell */
429 			seek_res = php_stream_seek(stream, desiredpos, SEEK_SET);
430 		}
431 
432 		if (seek_res != 0) {
433 			php_error_docref(NULL TSRMLS_CC, E_WARNING,
434 				"Failed to seek to position %ld in the stream", desiredpos);
435 			RETURN_FALSE;
436 		}
437 	}
438 
439 	len = php_stream_copy_to_mem(stream, &contents, maxlen, 0);
440 
441 	if (contents) {
442 		if (len > INT_MAX) {
443 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "content truncated from %ld to %d bytes", len, INT_MAX);
444 			len = INT_MAX;
445 		}
446 		RETVAL_STRINGL(contents, len, 0);
447 	} else {
448 		RETVAL_EMPTY_STRING();
449 	}
450 }
451 /* }}} */
452 
453 /* {{{ proto long stream_copy_to_stream(resource source, resource dest [, long maxlen [, long pos]])
454    Reads up to maxlen bytes from source stream and writes them to dest stream. */
PHP_FUNCTION(stream_copy_to_stream)455 PHP_FUNCTION(stream_copy_to_stream)
456 {
457 	php_stream *src, *dest;
458 	zval *zsrc, *zdest;
459 	long maxlen = PHP_STREAM_COPY_ALL, pos = 0;
460 	size_t len;
461 	int ret;
462 
463 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|ll", &zsrc, &zdest, &maxlen, &pos) == FAILURE) {
464 		RETURN_FALSE;
465 	}
466 
467 	php_stream_from_zval(src, &zsrc);
468 	php_stream_from_zval(dest, &zdest);
469 
470 	if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) {
471 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek to position %ld in the stream", pos);
472 		RETURN_FALSE;
473 	}
474 
475 	ret = php_stream_copy_to_stream_ex(src, dest, maxlen, &len);
476 
477 	if (ret != SUCCESS) {
478 		RETURN_FALSE;
479 	}
480 	RETURN_LONG(len);
481 }
482 /* }}} */
483 
484 /* {{{ proto array stream_get_meta_data(resource fp)
485     Retrieves header/meta data from streams/file pointers */
PHP_FUNCTION(stream_get_meta_data)486 PHP_FUNCTION(stream_get_meta_data)
487 {
488 	zval *arg1;
489 	php_stream *stream;
490 	zval *newval;
491 
492 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FAILURE) {
493 		return;
494 	}
495 	php_stream_from_zval(stream, &arg1);
496 
497 	array_init(return_value);
498 
499 	if (!php_stream_populate_meta_data(stream, return_value)) {
500 		add_assoc_bool(return_value, "timed_out", 0);
501 		add_assoc_bool(return_value, "blocked", 1);
502 		add_assoc_bool(return_value, "eof", php_stream_eof(stream));
503 	}
504 
505 	if (stream->wrapperdata) {
506 		MAKE_STD_ZVAL(newval);
507 		MAKE_COPY_ZVAL(&stream->wrapperdata, newval);
508 
509 		add_assoc_zval(return_value, "wrapper_data", newval);
510 	}
511 	if (stream->wrapper) {
512 		add_assoc_string(return_value, "wrapper_type", (char *)stream->wrapper->wops->label, 1);
513 	}
514 	add_assoc_string(return_value, "stream_type", (char *)stream->ops->label, 1);
515 
516 	add_assoc_string(return_value, "mode", stream->mode, 1);
517 
518 #if 0	/* TODO: needs updating for new filter API */
519 	if (stream->filterhead) {
520 		php_stream_filter *filter;
521 
522 		MAKE_STD_ZVAL(newval);
523 		array_init(newval);
524 
525 		for (filter = stream->filterhead; filter != NULL; filter = filter->next) {
526 			add_next_index_string(newval, (char *)filter->fops->label, 1);
527 		}
528 
529 		add_assoc_zval(return_value, "filters", newval);
530 	}
531 #endif
532 
533 	add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos);
534 
535 	add_assoc_bool(return_value, "seekable", (stream->ops->seek) && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0);
536 	if (stream->orig_path) {
537 		add_assoc_string(return_value, "uri", stream->orig_path, 1);
538 	}
539 
540 }
541 /* }}} */
542 
543 /* {{{ proto array stream_get_transports()
544    Retrieves list of registered socket transports */
PHP_FUNCTION(stream_get_transports)545 PHP_FUNCTION(stream_get_transports)
546 {
547 	HashTable *stream_xport_hash;
548 	char *stream_xport;
549 	uint stream_xport_len;
550 	ulong num_key;
551 
552 	if (zend_parse_parameters_none() == FAILURE) {
553 		return;
554 	}
555 
556 	if ((stream_xport_hash = php_stream_xport_get_hash())) {
557 		HashPosition pos;
558 		array_init(return_value);
559 		zend_hash_internal_pointer_reset_ex(stream_xport_hash, &pos);
560 		while (zend_hash_get_current_key_ex(stream_xport_hash,
561 					&stream_xport, &stream_xport_len,
562 					&num_key, 0, &pos) == HASH_KEY_IS_STRING) {
563 			add_next_index_stringl(return_value, stream_xport, stream_xport_len - 1, 1);
564 			zend_hash_move_forward_ex(stream_xport_hash, &pos);
565 		}
566 	} else {
567 		RETURN_FALSE;
568 	}
569 }
570 /* }}} */
571 
572 /* {{{ proto array stream_get_wrappers()
573     Retrieves list of registered stream wrappers */
PHP_FUNCTION(stream_get_wrappers)574 PHP_FUNCTION(stream_get_wrappers)
575 {
576 	HashTable *url_stream_wrappers_hash;
577 	char *stream_protocol;
578 	int key_flags;
579 	uint stream_protocol_len = 0;
580 	ulong num_key;
581 
582 	if (zend_parse_parameters_none() == FAILURE) {
583 		return;
584 	}
585 
586 	if ((url_stream_wrappers_hash = php_stream_get_url_stream_wrappers_hash())) {
587 		HashPosition pos;
588 		array_init(return_value);
589 		for (zend_hash_internal_pointer_reset_ex(url_stream_wrappers_hash, &pos);
590 			(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;
591 			zend_hash_move_forward_ex(url_stream_wrappers_hash, &pos)) {
592 				if (key_flags == HASH_KEY_IS_STRING) {
593 					add_next_index_stringl(return_value, stream_protocol, stream_protocol_len - 1, 1);
594 				}
595 		}
596 	} else {
597 		RETURN_FALSE;
598 	}
599 
600 }
601 /* }}} */
602 
603 /* {{{ stream_select related functions */
stream_array_to_fd_set(zval * stream_array,fd_set * fds,php_socket_t * max_fd TSRMLS_DC)604 static int stream_array_to_fd_set(zval *stream_array, fd_set *fds, php_socket_t *max_fd TSRMLS_DC)
605 {
606 	zval **elem;
607 	php_stream *stream;
608 	int cnt = 0;
609 
610 	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
611 		return 0;
612 	}
613 	for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream_array));
614 		 zend_hash_get_current_data(Z_ARRVAL_P(stream_array), (void **) &elem) == SUCCESS;
615 		 zend_hash_move_forward(Z_ARRVAL_P(stream_array))) {
616 
617 		/* Temporary int fd is needed for the STREAM data type on windows, passing this_fd directly to php_stream_cast()
618 			would eventually bring a wrong result on x64. php_stream_cast() casts to int internally, and this will leave
619 			the higher bits of a SOCKET variable uninitialized on systems with little endian. */
620 		int tmp_fd;
621 
622 		php_stream_from_zval_no_verify(stream, elem);
623 		if (stream == NULL) {
624 			continue;
625 		}
626 		/* get the fd.
627 		 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
628 		 * when casting.  It is only used here so that the buffered data warning
629 		 * is not displayed.
630 		 * */
631 		if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&tmp_fd, 1) && tmp_fd != -1) {
632 
633 			php_socket_t this_fd = (php_socket_t)tmp_fd;
634 
635 			PHP_SAFE_FD_SET(this_fd, fds);
636 
637 			if (this_fd > *max_fd) {
638 				*max_fd = this_fd;
639 			}
640 			cnt++;
641 		}
642 	}
643 	return cnt ? 1 : 0;
644 }
645 
stream_array_from_fd_set(zval * stream_array,fd_set * fds TSRMLS_DC)646 static int stream_array_from_fd_set(zval *stream_array, fd_set *fds TSRMLS_DC)
647 {
648 	zval **elem, **dest_elem;
649 	php_stream *stream;
650 	HashTable *new_hash;
651 	int ret = 0;
652 
653 	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
654 		return 0;
655 	}
656 	ALLOC_HASHTABLE(new_hash);
657 	zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(stream_array)), NULL, ZVAL_PTR_DTOR, 0);
658 
659 	for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream_array));
660 		 zend_hash_has_more_elements(Z_ARRVAL_P(stream_array)) == SUCCESS;
661 		 zend_hash_move_forward(Z_ARRVAL_P(stream_array))) {
662 
663 		int type;
664 		char *key;
665 		uint key_len;
666 		ulong num_ind;
667 		/* Temporary int fd is needed for the STREAM data type on windows, passing this_fd directly to php_stream_cast()
668 			would eventually bring a wrong result on x64. php_stream_cast() casts to int internally, and this will leave
669 			the higher bits of a SOCKET variable uninitialized on systems with little endian. */
670 		int tmp_fd;
671 
672 
673 		type = zend_hash_get_current_key_ex(Z_ARRVAL_P(stream_array),
674 				&key, &key_len, &num_ind, 0, NULL);
675 		if (type == HASH_KEY_NON_EXISTENT ||
676 			zend_hash_get_current_data(Z_ARRVAL_P(stream_array), (void **) &elem) == FAILURE) {
677 			continue; /* should not happen */
678 		}
679 
680 		php_stream_from_zval_no_verify(stream, elem);
681 		if (stream == NULL) {
682 			continue;
683 		}
684 		/* get the fd
685 		 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
686 		 * when casting.  It is only used here so that the buffered data warning
687 		 * is not displayed.
688 		 */
689 		if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&tmp_fd, 1) && tmp_fd != -1) {
690 
691 			php_socket_t this_fd = (php_socket_t)tmp_fd;
692 
693 			if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
694 				if (type == HASH_KEY_IS_LONG) {
695 					zend_hash_index_update(new_hash, num_ind, (void *)elem, sizeof(zval *), (void **)&dest_elem);
696 				} else { /* HASH_KEY_IS_STRING */
697 					zend_hash_update(new_hash, key, key_len, (void *)elem, sizeof(zval *), (void **)&dest_elem);
698 				}
699 
700 				if (dest_elem) {
701 					zval_add_ref(dest_elem);
702 				}
703 				ret++;
704 				continue;
705 			}
706 		}
707 	}
708 
709 	/* destroy old array and add new one */
710 	zend_hash_destroy(Z_ARRVAL_P(stream_array));
711 	efree(Z_ARRVAL_P(stream_array));
712 
713 	zend_hash_internal_pointer_reset(new_hash);
714 	Z_ARRVAL_P(stream_array) = new_hash;
715 
716 	return ret;
717 }
718 
stream_array_emulate_read_fd_set(zval * stream_array TSRMLS_DC)719 static int stream_array_emulate_read_fd_set(zval *stream_array TSRMLS_DC)
720 {
721 	zval **elem, **dest_elem;
722 	php_stream *stream;
723 	HashTable *new_hash;
724 	int ret = 0;
725 
726 	if (Z_TYPE_P(stream_array) != IS_ARRAY) {
727 		return 0;
728 	}
729 	ALLOC_HASHTABLE(new_hash);
730 	zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(stream_array)), NULL, ZVAL_PTR_DTOR, 0);
731 
732 	for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream_array));
733 		 zend_hash_get_current_data(Z_ARRVAL_P(stream_array), (void **) &elem) == SUCCESS;
734 		 zend_hash_move_forward(Z_ARRVAL_P(stream_array))) {
735 
736 		php_stream_from_zval_no_verify(stream, elem);
737 		if (stream == NULL) {
738 			continue;
739 		}
740 		if ((stream->writepos - stream->readpos) > 0) {
741 			/* allow readable non-descriptor based streams to participate in stream_select.
742 			 * Non-descriptor streams will only "work" if they have previously buffered the
743 			 * data.  Not ideal, but better than nothing.
744 			 * This branch of code also allows blocking streams with buffered data to
745 			 * operate correctly in stream_select.
746 			 * */
747 			zend_hash_next_index_insert(new_hash, (void *)elem, sizeof(zval *), (void **)&dest_elem);
748 			if (dest_elem) {
749 				zval_add_ref(dest_elem);
750 			}
751 			ret++;
752 			continue;
753 		}
754 	}
755 
756 	if (ret > 0) {
757 		/* destroy old array and add new one */
758 		zend_hash_destroy(Z_ARRVAL_P(stream_array));
759 		efree(Z_ARRVAL_P(stream_array));
760 
761 		zend_hash_internal_pointer_reset(new_hash);
762 		Z_ARRVAL_P(stream_array) = new_hash;
763 	} else {
764 		zend_hash_destroy(new_hash);
765 		FREE_HASHTABLE(new_hash);
766 	}
767 
768 	return ret;
769 }
770 /* }}} */
771 
772 /* {{{ proto int stream_select(array &read_streams, array &write_streams, array &except_streams, int tv_sec[, int tv_usec])
773    Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */
PHP_FUNCTION(stream_select)774 PHP_FUNCTION(stream_select)
775 {
776 	zval			*r_array, *w_array, *e_array, **sec = NULL;
777 	struct timeval	tv;
778 	struct timeval *tv_p = NULL;
779 	fd_set			rfds, wfds, efds;
780 	php_socket_t	max_fd = 0;
781 	int				retval, sets = 0;
782 	long			usec = 0;
783 	int				set_count, max_set_count = 0;
784 
785 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!a!Z!|l", &r_array, &w_array, &e_array, &sec, &usec) == FAILURE)
786 		return;
787 
788 	FD_ZERO(&rfds);
789 	FD_ZERO(&wfds);
790 	FD_ZERO(&efds);
791 
792 	if (r_array != NULL) {
793 		set_count = stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
794 		if (set_count > max_set_count)
795 			max_set_count = set_count;
796 		sets += set_count;
797 	}
798 
799 	if (w_array != NULL) {
800 		set_count = stream_array_to_fd_set(w_array, &wfds, &max_fd TSRMLS_CC);
801 		if (set_count > max_set_count)
802 			max_set_count = set_count;
803 		sets += set_count;
804 	}
805 
806 	if (e_array != NULL) {
807 		set_count = stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
808 		if (set_count > max_set_count)
809 			max_set_count = set_count;
810 		sets += set_count;
811 	}
812 
813 	if (!sets) {
814 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No stream arrays were passed");
815 		RETURN_FALSE;
816 	}
817 
818 	PHP_SAFE_MAX_FD(max_fd, max_set_count);
819 
820 	/* If seconds is not set to null, build the timeval, else we wait indefinitely */
821 	if (sec != NULL) {
822 		convert_to_long_ex(sec);
823 
824 		if (Z_LVAL_PP(sec) < 0) {
825 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "The seconds parameter must be greater than 0");
826 			RETURN_FALSE;
827 		} else if (usec < 0) {
828 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "The microseconds parameter must be greater than 0");
829 			RETURN_FALSE;
830 		}
831 
832 		/* Solaris + BSD do not like microsecond values which are >= 1 sec */
833 		if (usec > 999999) {
834 			tv.tv_sec = Z_LVAL_PP(sec) + (usec / 1000000);
835 			tv.tv_usec = usec % 1000000;
836 		} else {
837 			tv.tv_sec = Z_LVAL_PP(sec);
838 			tv.tv_usec = usec;
839 		}
840 
841 		tv_p = &tv;
842 	}
843 
844 	/* slight hack to support buffered data; if there is data sitting in the
845 	 * read buffer of any of the streams in the read array, let's pretend
846 	 * that we selected, but return only the readable sockets */
847 	if (r_array != NULL) {
848 
849 		retval = stream_array_emulate_read_fd_set(r_array TSRMLS_CC);
850 		if (retval > 0) {
851 			if (w_array != NULL) {
852 				zend_hash_clean(Z_ARRVAL_P(w_array));
853 			}
854 			if (e_array != NULL) {
855 				zend_hash_clean(Z_ARRVAL_P(e_array));
856 			}
857 			RETURN_LONG(retval);
858 		}
859 	}
860 
861 	retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p);
862 
863 	if (retval == -1) {
864 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
865 				errno, strerror(errno), max_fd);
866 		RETURN_FALSE;
867 	}
868 
869 	if (r_array != NULL) stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
870 	if (w_array != NULL) stream_array_from_fd_set(w_array, &wfds TSRMLS_CC);
871 	if (e_array != NULL) stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
872 
873 	RETURN_LONG(retval);
874 }
875 /* }}} */
876 
877 /* {{{ 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)878 static void user_space_stream_notifier(php_stream_context *context, int notifycode, int severity,
879 		char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC)
880 {
881 	zval *callback = (zval*)context->notifier->ptr;
882 	zval *retval = NULL;
883 	zval zvs[6];
884 	zval *ps[6];
885 	zval **ptps[6];
886 	int i;
887 
888 	for (i = 0; i < 6; i++) {
889 		INIT_ZVAL(zvs[i]);
890 		ps[i] = &zvs[i];
891 		ptps[i] = &ps[i];
892 		MAKE_STD_ZVAL(ps[i]);
893 	}
894 
895 	ZVAL_LONG(ps[0], notifycode);
896 	ZVAL_LONG(ps[1], severity);
897 	if (xmsg) {
898 		ZVAL_STRING(ps[2], xmsg, 1);
899 	} else {
900 		ZVAL_NULL(ps[2]);
901 	}
902 	ZVAL_LONG(ps[3], xcode);
903 	ZVAL_LONG(ps[4], bytes_sofar);
904 	ZVAL_LONG(ps[5], bytes_max);
905 
906 	if (FAILURE == call_user_function_ex(EG(function_table), NULL, callback, &retval, 6, ptps, 0, NULL TSRMLS_CC)) {
907 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call user notifier");
908 	}
909 	for (i = 0; i < 6; i++) {
910 		zval_ptr_dtor(&ps[i]);
911 	}
912 	if (retval) {
913 		zval_ptr_dtor(&retval);
914 	}
915 }
916 
user_space_stream_notifier_dtor(php_stream_notifier * notifier)917 static void user_space_stream_notifier_dtor(php_stream_notifier *notifier)
918 {
919 	if (notifier && notifier->ptr) {
920 		zval_ptr_dtor((zval **)&(notifier->ptr));
921 		notifier->ptr = NULL;
922 	}
923 }
924 
parse_context_options(php_stream_context * context,zval * options TSRMLS_DC)925 static int parse_context_options(php_stream_context *context, zval *options TSRMLS_DC)
926 {
927 	HashPosition pos, opos;
928 	zval **wval, **oval;
929 	char *wkey, *okey;
930 	uint wkey_len, okey_len;
931 	int ret = SUCCESS;
932 	ulong num_key;
933 
934 	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(options), &pos);
935 	while (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_P(options), (void**)&wval, &pos)) {
936 		if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_P(options), &wkey, &wkey_len, &num_key, 0, &pos)
937 				&& Z_TYPE_PP(wval) == IS_ARRAY) {
938 
939 			zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(wval), &opos);
940 			while (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(wval), (void**)&oval, &opos)) {
941 
942 				if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(wval), &okey, &okey_len, &num_key, 0, &opos)) {
943 					php_stream_context_set_option(context, wkey, okey, *oval);
944 				}
945 				zend_hash_move_forward_ex(Z_ARRVAL_PP(wval), &opos);
946 			}
947 
948 		} else {
949 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "options should have the form [\"wrappername\"][\"optionname\"] = $value");
950 		}
951 		zend_hash_move_forward_ex(Z_ARRVAL_P(options), &pos);
952 	}
953 
954 	return ret;
955 }
956 
parse_context_params(php_stream_context * context,zval * params TSRMLS_DC)957 static int parse_context_params(php_stream_context *context, zval *params TSRMLS_DC)
958 {
959 	int ret = SUCCESS;
960 	zval **tmp;
961 
962 	if (SUCCESS == zend_hash_find(Z_ARRVAL_P(params), "notification", sizeof("notification"), (void**)&tmp)) {
963 
964 		if (context->notifier) {
965 			php_stream_notification_free(context->notifier);
966 			context->notifier = NULL;
967 		}
968 
969 		context->notifier = php_stream_notification_alloc();
970 		context->notifier->func = user_space_stream_notifier;
971 		context->notifier->ptr = *tmp;
972 		Z_ADDREF_P(*tmp);
973 		context->notifier->dtor = user_space_stream_notifier_dtor;
974 	}
975 	if (SUCCESS == zend_hash_find(Z_ARRVAL_P(params), "options", sizeof("options"), (void**)&tmp)) {
976 		if (Z_TYPE_PP(tmp) == IS_ARRAY) {
977 			parse_context_options(context, *tmp TSRMLS_CC);
978 		} else {
979 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
980 		}
981 	}
982 
983 	return ret;
984 }
985 
986 /* given a zval which is either a stream or a context, return the underlying
987  * stream_context.  If it is a stream that does not have a context assigned, it
988  * will create and assign a context and return that.  */
decode_context_param(zval * contextresource TSRMLS_DC)989 static php_stream_context *decode_context_param(zval *contextresource TSRMLS_DC)
990 {
991 	php_stream_context *context = NULL;
992 
993 	context = zend_fetch_resource(&contextresource TSRMLS_CC, -1, NULL, NULL, 1, php_le_stream_context(TSRMLS_C));
994 	if (context == NULL) {
995 		php_stream *stream;
996 
997 		stream = zend_fetch_resource(&contextresource TSRMLS_CC, -1, NULL, NULL, 2, php_file_le_stream(), php_file_le_pstream);
998 
999 		if (stream) {
1000 			context = stream->context;
1001 			if (context == NULL) {
1002 				/* Only way this happens is if file is opened with NO_DEFAULT_CONTEXT
1003 				   param, but then something is called which requires a context.
1004 				   Don't give them the default one though since they already said they
1005 	 			   didn't want it. */
1006 				context = stream->context = php_stream_context_alloc(TSRMLS_C);
1007 			}
1008 		}
1009 	}
1010 
1011 	return context;
1012 }
1013 /* }}} */
1014 
1015 /* {{{ proto array stream_context_get_options(resource context|resource stream)
1016    Retrieve options for a stream/wrapper/context */
PHP_FUNCTION(stream_context_get_options)1017 PHP_FUNCTION(stream_context_get_options)
1018 {
1019 	zval *zcontext;
1020 	php_stream_context *context;
1021 
1022 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zcontext) == FAILURE) {
1023 		RETURN_FALSE;
1024 	}
1025 	context = decode_context_param(zcontext TSRMLS_CC);
1026 	if (!context) {
1027 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1028 		RETURN_FALSE;
1029 	}
1030 
1031 	RETURN_ZVAL(context->options, 1, 0);
1032 }
1033 /* }}} */
1034 
1035 /* {{{ proto bool stream_context_set_option(resource context|resource stream, string wrappername, string optionname, mixed value)
1036    Set an option for a wrapper */
PHP_FUNCTION(stream_context_set_option)1037 PHP_FUNCTION(stream_context_set_option)
1038 {
1039 	zval *options = NULL, *zcontext = NULL, *zvalue = NULL;
1040 	php_stream_context *context;
1041 	char *wrappername, *optionname;
1042 	int wrapperlen, optionlen;
1043 
1044 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1045 				"rssz", &zcontext, &wrappername, &wrapperlen,
1046 				&optionname, &optionlen, &zvalue) == FAILURE) {
1047 		if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1048 					"ra", &zcontext, &options) == FAILURE) {
1049 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "called with wrong number or type of parameters; please RTM");
1050 			RETURN_FALSE;
1051 		}
1052 	}
1053 
1054 	/* figure out where the context is coming from exactly */
1055 	context = decode_context_param(zcontext TSRMLS_CC);
1056 	if (!context) {
1057 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1058 		RETURN_FALSE;
1059 	}
1060 
1061 	if (options) {
1062 		/* handle the array syntax */
1063 		RETVAL_BOOL(parse_context_options(context, options TSRMLS_CC) == SUCCESS);
1064 	} else {
1065 		php_stream_context_set_option(context, wrappername, optionname, zvalue);
1066 		RETVAL_TRUE;
1067 	}
1068 }
1069 /* }}} */
1070 
1071 /* {{{ proto bool stream_context_set_params(resource context|resource stream, array options)
1072    Set parameters for a file context */
PHP_FUNCTION(stream_context_set_params)1073 PHP_FUNCTION(stream_context_set_params)
1074 {
1075 	zval *params, *zcontext;
1076 	php_stream_context *context;
1077 
1078 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &zcontext, &params) == FAILURE) {
1079 		RETURN_FALSE;
1080 	}
1081 
1082 	context = decode_context_param(zcontext TSRMLS_CC);
1083 	if (!context) {
1084 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1085 		RETURN_FALSE;
1086 	}
1087 
1088 	RETVAL_BOOL(parse_context_params(context, params TSRMLS_CC) == SUCCESS);
1089 }
1090 /* }}} */
1091 
1092 /* {{{ proto array stream_context_get_params(resource context|resource stream)
1093    Get parameters of a file context */
PHP_FUNCTION(stream_context_get_params)1094 PHP_FUNCTION(stream_context_get_params)
1095 {
1096 	zval *zcontext, *options;
1097 	php_stream_context *context;
1098 
1099 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zcontext) == FAILURE) {
1100 		RETURN_FALSE;
1101 	}
1102 
1103 	context = decode_context_param(zcontext TSRMLS_CC);
1104 	if (!context) {
1105 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1106 		RETURN_FALSE;
1107 	}
1108 
1109 	array_init(return_value);
1110 	if (context->notifier && context->notifier->ptr && context->notifier->func == user_space_stream_notifier) {
1111 		add_assoc_zval_ex(return_value, ZEND_STRS("notification"), context->notifier->ptr);
1112 		Z_ADDREF_P(context->notifier->ptr);
1113 	}
1114 	ALLOC_INIT_ZVAL(options);
1115 	ZVAL_ZVAL(options, context->options, 1, 0);
1116 	add_assoc_zval_ex(return_value, ZEND_STRS("options"), options);
1117 }
1118 /* }}} */
1119 
1120 /* {{{ proto resource stream_context_get_default([array options])
1121    Get a handle on the default file/stream context and optionally set parameters */
PHP_FUNCTION(stream_context_get_default)1122 PHP_FUNCTION(stream_context_get_default)
1123 {
1124 	zval *params = NULL;
1125 	php_stream_context *context;
1126 
1127 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a", &params) == FAILURE) {
1128 		RETURN_FALSE;
1129 	}
1130 
1131 	if (FG(default_context) == NULL) {
1132 		FG(default_context) = php_stream_context_alloc(TSRMLS_C);
1133 	}
1134 	context = FG(default_context);
1135 
1136 	if (params) {
1137 		parse_context_options(context, params TSRMLS_CC);
1138 	}
1139 
1140 	php_stream_context_to_zval(context, return_value);
1141 }
1142 /* }}} */
1143 
1144 /* {{{ proto resource stream_context_set_default(array options)
1145    Set default file/stream context, returns the context as a resource */
PHP_FUNCTION(stream_context_set_default)1146 PHP_FUNCTION(stream_context_set_default)
1147 {
1148 	zval *options = NULL;
1149 	php_stream_context *context;
1150 
1151 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &options) == FAILURE) {
1152 		return;
1153 	}
1154 
1155 	if (FG(default_context) == NULL) {
1156 		FG(default_context) = php_stream_context_alloc(TSRMLS_C);
1157 	}
1158 	context = FG(default_context);
1159 
1160 	parse_context_options(context, options TSRMLS_CC);
1161 
1162 	php_stream_context_to_zval(context, return_value);
1163 }
1164 /* }}} */
1165 
1166 /* {{{ proto resource stream_context_create([array options[, array params]])
1167    Create a file context and optionally set parameters */
PHP_FUNCTION(stream_context_create)1168 PHP_FUNCTION(stream_context_create)
1169 {
1170 	zval *options = NULL, *params = NULL;
1171 	php_stream_context *context;
1172 
1173 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!a!", &options, &params) == FAILURE) {
1174 		RETURN_FALSE;
1175 	}
1176 
1177 	context = php_stream_context_alloc(TSRMLS_C);
1178 
1179 	if (options) {
1180 		parse_context_options(context, options TSRMLS_CC);
1181 	}
1182 
1183 	if (params) {
1184 		parse_context_params(context, params TSRMLS_CC);
1185 	}
1186 
1187 	RETURN_RESOURCE(context->rsrc_id);
1188 }
1189 /* }}} */
1190 
1191 /* {{{ streams filter functions */
apply_filter_to_stream(int append,INTERNAL_FUNCTION_PARAMETERS)1192 static void apply_filter_to_stream(int append, INTERNAL_FUNCTION_PARAMETERS)
1193 {
1194 	zval *zstream;
1195 	php_stream *stream;
1196 	char *filtername;
1197 	int filternamelen;
1198 	long read_write = 0;
1199 	zval *filterparams = NULL;
1200 	php_stream_filter *filter = NULL;
1201 	int ret;
1202 
1203 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lz", &zstream,
1204 				&filtername, &filternamelen, &read_write, &filterparams) == FAILURE) {
1205 		RETURN_FALSE;
1206 	}
1207 
1208 	php_stream_from_zval(stream, &zstream);
1209 
1210 	if ((read_write & PHP_STREAM_FILTER_ALL) == 0) {
1211 		/* Chain not specified.
1212 		 * Examine stream->mode to determine which filters are needed
1213 		 * There's no harm in attaching a filter to an unused chain,
1214 		 * but why waste the memory and clock cycles?
1215 		 */
1216 		if (strchr(stream->mode, 'r') || strchr(stream->mode, '+')) {
1217 			read_write |= PHP_STREAM_FILTER_READ;
1218 		}
1219 		if (strchr(stream->mode, 'w') || strchr(stream->mode, '+') || strchr(stream->mode, 'a')) {
1220 			read_write |= PHP_STREAM_FILTER_WRITE;
1221 		}
1222 	}
1223 
1224 	if (read_write & PHP_STREAM_FILTER_READ) {
1225 		filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream) TSRMLS_CC);
1226 		if (filter == NULL) {
1227 			RETURN_FALSE;
1228 		}
1229 
1230 		if (append) {
1231 			ret = php_stream_filter_append_ex(&stream->readfilters, filter TSRMLS_CC);
1232 		} else {
1233 			ret = php_stream_filter_prepend_ex(&stream->readfilters, filter TSRMLS_CC);
1234 		}
1235 		if (ret != SUCCESS) {
1236 			php_stream_filter_remove(filter, 1 TSRMLS_CC);
1237 			RETURN_FALSE;
1238 		}
1239 	}
1240 
1241 	if (read_write & PHP_STREAM_FILTER_WRITE) {
1242 		filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream) TSRMLS_CC);
1243 		if (filter == NULL) {
1244 			RETURN_FALSE;
1245 		}
1246 
1247 		if (append) {
1248 			ret = php_stream_filter_append_ex(&stream->writefilters, filter TSRMLS_CC);
1249 		} else {
1250 			ret = php_stream_filter_prepend_ex(&stream->writefilters, filter TSRMLS_CC);
1251 		}
1252 		if (ret != SUCCESS) {
1253 			php_stream_filter_remove(filter, 1 TSRMLS_CC);
1254 			RETURN_FALSE;
1255 		}
1256 	}
1257 
1258 	if (filter) {
1259 		RETURN_RESOURCE(filter->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, filter, php_file_le_stream_filter()));
1260 	} else {
1261 		RETURN_FALSE;
1262 	}
1263 }
1264 /* }}} */
1265 
1266 /* {{{ proto resource stream_filter_prepend(resource stream, string filtername[, int read_write[, string filterparams]])
1267    Prepend a filter to a stream */
PHP_FUNCTION(stream_filter_prepend)1268 PHP_FUNCTION(stream_filter_prepend)
1269 {
1270 	apply_filter_to_stream(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1271 }
1272 /* }}} */
1273 
1274 /* {{{ proto resource stream_filter_append(resource stream, string filtername[, int read_write[, string filterparams]])
1275    Append a filter to a stream */
PHP_FUNCTION(stream_filter_append)1276 PHP_FUNCTION(stream_filter_append)
1277 {
1278 	apply_filter_to_stream(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1279 }
1280 /* }}} */
1281 
1282 /* {{{ proto bool stream_filter_remove(resource stream_filter)
1283 	Flushes any data in the filter's internal buffer, removes it from the chain, and frees the resource */
PHP_FUNCTION(stream_filter_remove)1284 PHP_FUNCTION(stream_filter_remove)
1285 {
1286 	zval *zfilter;
1287 	php_stream_filter *filter;
1288 
1289 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zfilter) == FAILURE) {
1290 		RETURN_FALSE;
1291 	}
1292 
1293 	filter = zend_fetch_resource(&zfilter TSRMLS_CC, -1, NULL, NULL, 1, php_file_le_stream_filter());
1294 	if (!filter) {
1295 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid resource given, not a stream filter");
1296 		RETURN_FALSE;
1297 	}
1298 
1299 	if (php_stream_filter_flush(filter, 1) == FAILURE) {
1300 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to flush filter, not removing");
1301 		RETURN_FALSE;
1302 	}
1303 
1304 	if (zend_list_delete(Z_LVAL_P(zfilter)) == FAILURE) {
1305 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not invalidate filter, not removing");
1306 		RETURN_FALSE;
1307 	} else {
1308 		php_stream_filter_remove(filter, 1 TSRMLS_CC);
1309 		RETURN_TRUE;
1310 	}
1311 }
1312 /* }}} */
1313 
1314 /* {{{ proto string stream_get_line(resource stream, int maxlen [, string ending])
1315    Read up to maxlen bytes from a stream or until the ending string is found */
PHP_FUNCTION(stream_get_line)1316 PHP_FUNCTION(stream_get_line)
1317 {
1318 	char *str = NULL;
1319 	int str_len = 0;
1320 	long max_length;
1321 	zval *zstream;
1322 	char *buf;
1323 	size_t buf_size;
1324 	php_stream *stream;
1325 
1326 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|s", &zstream, &max_length, &str, &str_len) == FAILURE) {
1327 		RETURN_FALSE;
1328 	}
1329 
1330 	if (max_length < 0) {
1331 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The maximum allowed length must be greater than or equal to zero");
1332 		RETURN_FALSE;
1333 	}
1334 	if (!max_length) {
1335 		max_length = PHP_SOCK_CHUNK_SIZE;
1336 	}
1337 
1338 	php_stream_from_zval(stream, &zstream);
1339 
1340 	if ((buf = php_stream_get_record(stream, max_length, &buf_size, str, str_len TSRMLS_CC))) {
1341 		RETURN_STRINGL(buf, buf_size, 0);
1342 	} else {
1343 		RETURN_FALSE;
1344 	}
1345 }
1346 
1347 /* }}} */
1348 
1349 /* {{{ proto bool stream_set_blocking(resource socket, int mode)
1350    Set blocking/non-blocking mode on a socket or stream */
PHP_FUNCTION(stream_set_blocking)1351 PHP_FUNCTION(stream_set_blocking)
1352 {
1353 	zval *arg1;
1354 	int block;
1355 	long arg2;
1356 	php_stream *stream;
1357 
1358 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1359 		return;
1360 	}
1361 
1362 	php_stream_from_zval(stream, &arg1);
1363 
1364 	block = arg2;
1365 
1366 	if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block == 0 ? 0 : 1, NULL) == -1) {
1367 		RETURN_FALSE;
1368 	}
1369 
1370 	RETURN_TRUE;
1371 }
1372 
1373 /* }}} */
1374 
1375 /* {{{ proto bool stream_set_timeout(resource stream, int seconds [, int microseconds])
1376    Set timeout on stream read to seconds + microseonds */
1377 #if HAVE_SYS_TIME_H || defined(PHP_WIN32)
PHP_FUNCTION(stream_set_timeout)1378 PHP_FUNCTION(stream_set_timeout)
1379 {
1380 	zval *socket;
1381 	long seconds, microseconds = 0;
1382 	struct timeval t;
1383 	php_stream *stream;
1384 	int argc = ZEND_NUM_ARGS();
1385 
1386 	if (zend_parse_parameters(argc TSRMLS_CC, "rl|l", &socket, &seconds, &microseconds) == FAILURE) {
1387 		return;
1388 	}
1389 
1390 	php_stream_from_zval(stream, &socket);
1391 
1392 	t.tv_sec = seconds;
1393 
1394 	if (argc == 3) {
1395 		t.tv_usec = microseconds % 1000000;
1396 		t.tv_sec += microseconds / 1000000;
1397 	} else {
1398 		t.tv_usec = 0;
1399 	}
1400 
1401 	if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)) {
1402 		RETURN_TRUE;
1403 	}
1404 
1405 	RETURN_FALSE;
1406 }
1407 #endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */
1408 /* }}} */
1409 
1410 /* {{{ proto int stream_set_write_buffer(resource fp, int buffer)
1411    Set file write buffer */
PHP_FUNCTION(stream_set_write_buffer)1412 PHP_FUNCTION(stream_set_write_buffer)
1413 {
1414 	zval *arg1;
1415 	int ret;
1416 	long arg2;
1417 	size_t buff;
1418 	php_stream *stream;
1419 
1420 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1421 		RETURN_FALSE;
1422 	}
1423 
1424 	php_stream_from_zval(stream, &arg1);
1425 
1426 	buff = arg2;
1427 
1428 	/* if buff is 0 then set to non-buffered */
1429 	if (buff == 0) {
1430 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1431 	} else {
1432 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1433 	}
1434 
1435 	RETURN_LONG(ret == 0 ? 0 : EOF);
1436 }
1437 /* }}} */
1438 
1439 /* {{{ proto int stream_set_chunk_size(resource fp, int chunk_size)
1440    Set the stream chunk size */
PHP_FUNCTION(stream_set_chunk_size)1441 PHP_FUNCTION(stream_set_chunk_size)
1442 {
1443 	int			ret;
1444 	long		csize;
1445 	zval		*zstream;
1446 	php_stream	*stream;
1447 
1448 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zstream, &csize) == FAILURE) {
1449 		RETURN_FALSE;
1450 	}
1451 
1452 	if (csize <= 0) {
1453 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The chunk size must be a positive integer, given %ld", csize);
1454 		RETURN_FALSE;
1455 	}
1456 	/* stream.chunk_size is actually a size_t, but php_stream_set_option
1457 	 * can only use an int to accept the new value and return the old one.
1458 	 * In any case, values larger than INT_MAX for a chunk size make no sense.
1459 	 */
1460 	if (csize > INT_MAX) {
1461 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "The chunk size cannot be larger than %d", INT_MAX);
1462 		RETURN_FALSE;
1463 	}
1464 
1465 	php_stream_from_zval(stream, &zstream);
1466 
1467 	ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL);
1468 
1469 	RETURN_LONG(ret > 0 ? (long)ret : (long)EOF);
1470 }
1471 /* }}} */
1472 
1473 /* {{{ proto int stream_set_read_buffer(resource fp, int buffer)
1474    Set file read buffer */
PHP_FUNCTION(stream_set_read_buffer)1475 PHP_FUNCTION(stream_set_read_buffer)
1476 {
1477 	zval *arg1;
1478 	int ret;
1479 	long arg2;
1480 	size_t buff;
1481 	php_stream *stream;
1482 
1483 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1484 		RETURN_FALSE;
1485 	}
1486 
1487 	php_stream_from_zval(stream, &arg1);
1488 
1489 	buff = arg2;
1490 
1491 	/* if buff is 0 then set to non-buffered */
1492 	if (buff == 0) {
1493 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1494 	} else {
1495 		ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1496 	}
1497 
1498 	RETURN_LONG(ret == 0 ? 0 : EOF);
1499 }
1500 /* }}} */
1501 
1502 /* {{{ proto int stream_socket_enable_crypto(resource stream, bool enable [, int cryptokind [, resource sessionstream]])
1503    Enable or disable a specific kind of crypto on the stream */
PHP_FUNCTION(stream_socket_enable_crypto)1504 PHP_FUNCTION(stream_socket_enable_crypto)
1505 {
1506 	long cryptokind = 0;
1507 	zval *zstream, *zsessstream = NULL;
1508 	php_stream *stream, *sessstream = NULL;
1509 	zend_bool enable;
1510 	int ret;
1511 
1512 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rb|lr", &zstream, &enable, &cryptokind, &zsessstream) == FAILURE) {
1513 		RETURN_FALSE;
1514 	}
1515 
1516 	php_stream_from_zval(stream, &zstream);
1517 
1518 	if (ZEND_NUM_ARGS() >= 3) {
1519 		if (zsessstream) {
1520 			php_stream_from_zval(sessstream, &zsessstream);
1521 		}
1522 
1523 		if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream TSRMLS_CC) < 0) {
1524 			RETURN_FALSE;
1525 		}
1526 	} else if (enable) {
1527 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "When enabling encryption you must specify the crypto type");
1528 		RETURN_FALSE;
1529 	}
1530 
1531 	ret = php_stream_xport_crypto_enable(stream, enable TSRMLS_CC);
1532 	switch (ret) {
1533 		case -1:
1534 			RETURN_FALSE;
1535 
1536 		case 0:
1537 			RETURN_LONG(0);
1538 
1539 		default:
1540 			RETURN_TRUE;
1541 	}
1542 }
1543 /* }}} */
1544 
1545 /* {{{ proto string stream_resolve_include_path(string filename)
1546 Determine what file will be opened by calls to fopen() with a relative path */
PHP_FUNCTION(stream_resolve_include_path)1547 PHP_FUNCTION(stream_resolve_include_path)
1548 {
1549 	char *filename, *resolved_path;
1550 	int filename_len;
1551 
1552 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
1553 		return;
1554 	}
1555 
1556 	resolved_path = zend_resolve_path(filename, filename_len TSRMLS_CC);
1557 
1558 	if (resolved_path) {
1559 		RETURN_STRING(resolved_path, 0);
1560 	}
1561 	RETURN_FALSE;
1562 }
1563 /* }}} */
1564 
1565 /* {{{ proto bool stream_is_local(resource stream|string url) U
1566 */
PHP_FUNCTION(stream_is_local)1567 PHP_FUNCTION(stream_is_local)
1568 {
1569 	zval **zstream;
1570 	php_stream *stream = NULL;
1571 	php_stream_wrapper *wrapper = NULL;
1572 
1573 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &zstream) == FAILURE) {
1574 		RETURN_FALSE;
1575 	}
1576 
1577 	if (Z_TYPE_PP(zstream) == IS_RESOURCE) {
1578 		php_stream_from_zval(stream, zstream);
1579 		if (stream == NULL) {
1580 			RETURN_FALSE;
1581 		}
1582 		wrapper = stream->wrapper;
1583 	} else {
1584 		convert_to_string_ex(zstream);
1585 
1586 		wrapper = php_stream_locate_url_wrapper(Z_STRVAL_PP(zstream), NULL, 0 TSRMLS_CC);
1587 	}
1588 
1589 	if (!wrapper) {
1590 		RETURN_FALSE;
1591 	}
1592 
1593 	RETURN_BOOL(wrapper->is_url==0);
1594 }
1595 /* }}} */
1596 
1597 /* {{{ proto bool stream_supports_lock(resource stream)
1598    Tells whether the stream supports locking through flock(). */
PHP_FUNCTION(stream_supports_lock)1599 PHP_FUNCTION(stream_supports_lock)
1600 {
1601 	php_stream *stream;
1602 	zval *zsrc;
1603 
1604 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsrc) == FAILURE) {
1605 		RETURN_FALSE;
1606 	}
1607 
1608 	php_stream_from_zval(stream, &zsrc);
1609 
1610 	if (!php_stream_supports_lock(stream)) {
1611 		RETURN_FALSE;
1612 	}
1613 
1614 	RETURN_TRUE;
1615 }
1616 
1617 #ifdef HAVE_SHUTDOWN
1618 /* {{{ proto int stream_socket_shutdown(resource stream, int how)
1619 	causes all or part of a full-duplex connection on the socket associated
1620 	with stream to be shut down.  If how is SHUT_RD,  further receptions will
1621 	be disallowed. If how is SHUT_WR, further transmissions will be disallowed.
1622 	If how is SHUT_RDWR,  further  receptions and transmissions will be
1623 	disallowed. */
PHP_FUNCTION(stream_socket_shutdown)1624 PHP_FUNCTION(stream_socket_shutdown)
1625 {
1626 	long how;
1627 	zval *zstream;
1628 	php_stream *stream;
1629 
1630 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zstream, &how) == FAILURE) {
1631 		RETURN_FALSE;
1632 	}
1633 
1634 	if (how != STREAM_SHUT_RD &&
1635 	    how != STREAM_SHUT_WR &&
1636 	    how != STREAM_SHUT_RDWR) {
1637 		RETURN_FALSE;
1638 	}
1639 
1640 	php_stream_from_zval(stream, &zstream);
1641 
1642 	RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how TSRMLS_CC) == 0);
1643 }
1644 /* }}} */
1645 #endif
1646 
1647 /*
1648  * Local variables:
1649  * tab-width: 4
1650  * c-basic-offset: 4
1651  * End:
1652  * vim600: noet sw=4 ts=4 fdm=marker
1653  * vim<600: noet sw=4 ts=4
1654  */
1655 
1656