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