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