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