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