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