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