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