1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
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 TSRMLS_DC);
46
47/* Streams based network functions */
48
49#if HAVE_SOCKETPAIR
50/* {{{ proto array stream_socket_pair(int domain, int type, int protocol)
51   Creates a pair of connected, indistinguishable socket streams */
52PHP_FUNCTION(stream_socket_pair)
53{
54    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() TSRMLS_CC, "lll",
59            &domain, &type, &protocol)) {
60        RETURN_FALSE;
61    }
62
63    if (0 != socketpair(domain, type, protocol, pair)) {
64        char errbuf[256];
65        php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create sockets: [%d]: %s",
66            php_socket_errno(), php_socket_strerror(php_socket_errno(), errbuf, sizeof(errbuf)));
67        RETURN_FALSE;
68    }
69
70    array_init(return_value);
71
72    s1 = php_stream_sock_open_from_socket(pair[0], 0);
73    s2 = php_stream_sock_open_from_socket(pair[1], 0);
74
75    /* set the __exposed flag.
76     * php_stream_to_zval() does, add_next_index_resource() does not */
77    php_stream_auto_cleanup(s1);
78    php_stream_auto_cleanup(s2);
79
80    add_next_index_resource(return_value, 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 = 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() TSRMLS_CC, "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 TSRMLS_CC);
142
143        php_error_docref(NULL TSRMLS_CC, 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() TSRMLS_CC, "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 | flags,
210            NULL, NULL, context, &errstr, &err);
211
212    if (stream == NULL) {
213        php_error_docref(NULL TSRMLS_CC, 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 = 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() TSRMLS_CC, "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                TSRMLS_CC) && 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 TSRMLS_CC, 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() TSRMLS_CC, "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                TSRMLS_CC)) {
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() TSRMLS_CC, "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 TSRMLS_CC)) {
338            php_error_docref(NULL TSRMLS_CC, 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, flags, target_addr ? &sa : NULL, sl TSRMLS_CC));
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() TSRMLS_CC, "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 TSRMLS_CC, 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, flags, NULL, NULL,
378            zremote ? &remote_addr : NULL
379            TSRMLS_CC);
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() TSRMLS_CC, "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        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 TSRMLS_CC, 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 TSRMLS_CC, 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() TSRMLS_CC, "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 TSRMLS_CC, 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() TSRMLS_CC, "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 TSRMLS_DC)
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 TSRMLS_DC)
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 TSRMLS_DC)
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() TSRMLS_CC, "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 TSRMLS_CC);
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 TSRMLS_CC);
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 TSRMLS_CC);
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 TSRMLS_CC, 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 TSRMLS_CC, E_WARNING, "The seconds parameter must be greater than 0");
770            RETURN_FALSE;
771        } else if (usec < 0) {
772            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The microseconds parameter must be greater than 0");
773            RETURN_FALSE;
774        }
775
776        /* Solaris + BSD do not like microsecond values which are >= 1 sec */
777        if (usec > 999999) {
778            tv.tv_sec = Z_LVAL_P(sec) + (usec / 1000000);
779            tv.tv_usec = usec % 1000000;
780        } else {
781            tv.tv_sec = Z_LVAL_P(sec);
782            tv.tv_usec = usec;
783        }
784
785        tv_p = &tv;
786    }
787
788    /* slight hack to support buffered data; if there is data sitting in the
789     * read buffer of any of the streams in the read array, let's pretend
790     * that we selected, but return only the readable sockets */
791    if (r_array != NULL) {
792
793        retval = stream_array_emulate_read_fd_set(r_array TSRMLS_CC);
794        if (retval > 0) {
795            if (w_array != NULL) {
796                zend_hash_clean(Z_ARRVAL_P(w_array));
797            }
798            if (e_array != NULL) {
799                zend_hash_clean(Z_ARRVAL_P(e_array));
800            }
801            RETURN_LONG(retval);
802        }
803    }
804
805    retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p);
806
807    if (retval == -1) {
808        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
809                errno, strerror(errno), max_fd);
810        RETURN_FALSE;
811    }
812
813    if (r_array != NULL) stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
814    if (w_array != NULL) stream_array_from_fd_set(w_array, &wfds TSRMLS_CC);
815    if (e_array != NULL) stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
816
817    RETURN_LONG(retval);
818}
819/* }}} */
820
821/* {{{ stream_context related functions */
822static void user_space_stream_notifier(php_stream_context *context, int notifycode, int severity,
823        char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC)
824{
825    zval *callback = &context->notifier->ptr;
826    zval retval;
827    zval zvs[6];
828    int i;
829
830    ZVAL_LONG(&zvs[0], notifycode);
831    ZVAL_LONG(&zvs[1], severity);
832    if (xmsg) {
833        ZVAL_STRING(&zvs[2], xmsg);
834    } else {
835        ZVAL_NULL(&zvs[2]);
836    }
837    ZVAL_LONG(&zvs[3], xcode);
838    ZVAL_LONG(&zvs[4], bytes_sofar);
839    ZVAL_LONG(&zvs[5], bytes_max);
840
841    if (FAILURE == call_user_function_ex(EG(function_table), NULL, callback, &retval, 6, zvs, 0, NULL TSRMLS_CC)) {
842        php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call user notifier");
843    }
844    for (i = 0; i < 6; i++) {
845        zval_ptr_dtor(&zvs[i]);
846    }
847    zval_ptr_dtor(&retval);
848}
849
850static void user_space_stream_notifier_dtor(php_stream_notifier *notifier)
851{
852    if (notifier && Z_TYPE(notifier->ptr) != IS_UNDEF) {
853        zval_ptr_dtor(&notifier->ptr);
854        ZVAL_UNDEF(&notifier->ptr);
855    }
856}
857
858static int parse_context_options(php_stream_context *context, zval *options TSRMLS_DC)
859{
860    zval *wval, *oval;
861    zend_string *wkey, *okey;
862    int ret = SUCCESS;
863
864    ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), wkey, wval) {
865        if (wkey && Z_TYPE_P(wval) == IS_ARRAY) {
866
867            ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(wval), okey, oval) {
868                if (okey) {
869                    php_stream_context_set_option(context, wkey->val, okey->val, oval);
870                }
871            } ZEND_HASH_FOREACH_END();
872
873        } else {
874            php_error_docref(NULL TSRMLS_CC, E_WARNING, "options should have the form [\"wrappername\"][\"optionname\"] = $value");
875        }
876    } ZEND_HASH_FOREACH_END();
877
878    return ret;
879}
880
881static int parse_context_params(php_stream_context *context, zval *params TSRMLS_DC)
882{
883    int ret = SUCCESS;
884    zval *tmp;
885
886    if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(params), "notification", sizeof("notification")-1))) {
887
888        if (context->notifier) {
889            php_stream_notification_free(context->notifier);
890            context->notifier = NULL;
891        }
892
893        context->notifier = php_stream_notification_alloc();
894        context->notifier->func = user_space_stream_notifier;
895        ZVAL_COPY(&context->notifier->ptr, tmp);
896        context->notifier->dtor = user_space_stream_notifier_dtor;
897    }
898    if (NULL != (tmp = zend_hash_str_find(Z_ARRVAL_P(params), "options", sizeof("options")-1))) {
899        if (Z_TYPE_P(tmp) == IS_ARRAY) {
900            parse_context_options(context, tmp TSRMLS_CC);
901        } else {
902            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
903        }
904    }
905
906    return ret;
907}
908
909/* given a zval which is either a stream or a context, return the underlying
910 * stream_context.  If it is a stream that does not have a context assigned, it
911 * will create and assign a context and return that.  */
912static php_stream_context *decode_context_param(zval *contextresource TSRMLS_DC)
913{
914    php_stream_context *context = NULL;
915
916    context = zend_fetch_resource(contextresource TSRMLS_CC, -1, NULL, NULL, 1, php_le_stream_context(TSRMLS_C));
917    if (context == NULL) {
918        php_stream *stream;
919
920        stream = zend_fetch_resource(contextresource TSRMLS_CC, -1, NULL, NULL, 2, php_file_le_stream(), php_file_le_pstream);
921
922        if (stream) {
923            context = PHP_STREAM_CONTEXT(stream);
924            if (context == NULL) {
925                /* Only way this happens is if file is opened with NO_DEFAULT_CONTEXT
926                   param, but then something is called which requires a context.
927                   Don't give them the default one though since they already said they
928                   didn't want it. */
929                context = php_stream_context_alloc(TSRMLS_C);
930                stream->ctx = context->res;
931            }
932        }
933    }
934
935    return context;
936}
937/* }}} */
938
939/* {{{ proto array stream_context_get_options(resource context|resource stream)
940   Retrieve options for a stream/wrapper/context */
941PHP_FUNCTION(stream_context_get_options)
942{
943    zval *zcontext;
944    php_stream_context *context;
945
946    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zcontext) == FAILURE) {
947        RETURN_FALSE;
948    }
949    context = decode_context_param(zcontext TSRMLS_CC);
950    if (!context) {
951        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
952        RETURN_FALSE;
953    }
954
955    RETURN_ZVAL(&context->options, 1, 0);
956}
957/* }}} */
958
959/* {{{ proto bool stream_context_set_option(resource context|resource stream, string wrappername, string optionname, mixed value)
960   Set an option for a wrapper */
961PHP_FUNCTION(stream_context_set_option)
962{
963    zval *options = NULL, *zcontext = NULL, *zvalue = NULL;
964    php_stream_context *context;
965    char *wrappername, *optionname;
966    size_t wrapperlen, optionlen;
967
968    if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
969                "rssz", &zcontext, &wrappername, &wrapperlen,
970                &optionname, &optionlen, &zvalue) == FAILURE) {
971        if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
972                    "ra", &zcontext, &options) == FAILURE) {
973            php_error_docref(NULL TSRMLS_CC, E_WARNING, "called with wrong number or type of parameters; please RTM");
974            RETURN_FALSE;
975        }
976    }
977
978    /* figure out where the context is coming from exactly */
979    context = decode_context_param(zcontext TSRMLS_CC);
980    if (!context) {
981        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
982        RETURN_FALSE;
983    }
984
985    if (options) {
986        /* handle the array syntax */
987        RETVAL_BOOL(parse_context_options(context, options TSRMLS_CC) == SUCCESS);
988    } else {
989        php_stream_context_set_option(context, wrappername, optionname, zvalue);
990        RETVAL_TRUE;
991    }
992}
993/* }}} */
994
995/* {{{ proto bool stream_context_set_params(resource context|resource stream, array options)
996   Set parameters for a file context */
997PHP_FUNCTION(stream_context_set_params)
998{
999    zval *params, *zcontext;
1000    php_stream_context *context;
1001
1002    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &zcontext, &params) == FAILURE) {
1003        RETURN_FALSE;
1004    }
1005
1006    context = decode_context_param(zcontext TSRMLS_CC);
1007    if (!context) {
1008        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1009        RETURN_FALSE;
1010    }
1011
1012    RETVAL_BOOL(parse_context_params(context, params TSRMLS_CC) == SUCCESS);
1013}
1014/* }}} */
1015
1016/* {{{ proto array stream_context_get_params(resource context|resource stream)
1017   Get parameters of a file context */
1018PHP_FUNCTION(stream_context_get_params)
1019{
1020    zval *zcontext, options;
1021    php_stream_context *context;
1022
1023    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zcontext) == FAILURE) {
1024        RETURN_FALSE;
1025    }
1026
1027    context = decode_context_param(zcontext TSRMLS_CC);
1028    if (!context) {
1029        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1030        RETURN_FALSE;
1031    }
1032
1033    array_init(return_value);
1034    if (context->notifier && Z_TYPE(context->notifier->ptr) != IS_UNDEF && context->notifier->func == user_space_stream_notifier) {
1035        add_assoc_zval_ex(return_value, "notification", sizeof("notification")-1, &context->notifier->ptr);
1036        if (Z_REFCOUNTED(context->notifier->ptr)) Z_ADDREF(context->notifier->ptr);
1037    }
1038    ZVAL_ZVAL(&options, &context->options, 1, 0);
1039    add_assoc_zval_ex(return_value, "options", sizeof("options")-1, &options);
1040}
1041/* }}} */
1042
1043/* {{{ proto resource stream_context_get_default([array options])
1044   Get a handle on the default file/stream context and optionally set parameters */
1045PHP_FUNCTION(stream_context_get_default)
1046{
1047    zval *params = NULL;
1048    php_stream_context *context;
1049
1050    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a", &params) == FAILURE) {
1051        RETURN_FALSE;
1052    }
1053
1054    if (FG(default_context) == NULL) {
1055        FG(default_context) = php_stream_context_alloc(TSRMLS_C);
1056    }
1057    context = FG(default_context);
1058
1059    if (params) {
1060        parse_context_options(context, params TSRMLS_CC);
1061    }
1062
1063    php_stream_context_to_zval(context, return_value);
1064}
1065/* }}} */
1066
1067/* {{{ proto resource stream_context_set_default(array options)
1068   Set default file/stream context, returns the context as a resource */
1069PHP_FUNCTION(stream_context_set_default)
1070{
1071    zval *options = NULL;
1072    php_stream_context *context;
1073
1074    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &options) == FAILURE) {
1075        return;
1076    }
1077
1078    if (FG(default_context) == NULL) {
1079        FG(default_context) = php_stream_context_alloc(TSRMLS_C);
1080    }
1081    context = FG(default_context);
1082
1083    parse_context_options(context, options TSRMLS_CC);
1084
1085    php_stream_context_to_zval(context, return_value);
1086}
1087/* }}} */
1088
1089/* {{{ proto resource stream_context_create([array options[, array params]])
1090   Create a file context and optionally set parameters */
1091PHP_FUNCTION(stream_context_create)
1092{
1093    zval *options = NULL, *params = NULL;
1094    php_stream_context *context;
1095
1096    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!a!", &options, &params) == FAILURE) {
1097        RETURN_FALSE;
1098    }
1099
1100    context = php_stream_context_alloc(TSRMLS_C);
1101
1102    if (options) {
1103        parse_context_options(context, options TSRMLS_CC);
1104    }
1105
1106    if (params) {
1107        parse_context_params(context, params TSRMLS_CC);
1108    }
1109
1110    RETURN_RES(context->res);
1111}
1112/* }}} */
1113
1114/* {{{ streams filter functions */
1115static void apply_filter_to_stream(int append, INTERNAL_FUNCTION_PARAMETERS)
1116{
1117    zval *zstream;
1118    php_stream *stream;
1119    char *filtername;
1120    size_t filternamelen;
1121    zend_long read_write = 0;
1122    zval *filterparams = NULL;
1123    php_stream_filter *filter = NULL;
1124    int ret;
1125
1126    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lz", &zstream,
1127                &filtername, &filternamelen, &read_write, &filterparams) == FAILURE) {
1128        RETURN_FALSE;
1129    }
1130
1131    php_stream_from_zval(stream, zstream);
1132
1133    if ((read_write & PHP_STREAM_FILTER_ALL) == 0) {
1134        /* Chain not specified.
1135         * Examine stream->mode to determine which filters are needed
1136         * There's no harm in attaching a filter to an unused chain,
1137         * but why waste the memory and clock cycles?
1138         */
1139        if (strchr(stream->mode, 'r') || strchr(stream->mode, '+')) {
1140            read_write |= PHP_STREAM_FILTER_READ;
1141        }
1142        if (strchr(stream->mode, 'w') || strchr(stream->mode, '+') || strchr(stream->mode, 'a')) {
1143            read_write |= PHP_STREAM_FILTER_WRITE;
1144        }
1145    }
1146
1147    if (read_write & PHP_STREAM_FILTER_READ) {
1148        filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream) TSRMLS_CC);
1149        if (filter == NULL) {
1150            RETURN_FALSE;
1151        }
1152
1153        if (append) {
1154            ret = php_stream_filter_append_ex(&stream->readfilters, filter TSRMLS_CC);
1155        } else {
1156            ret = php_stream_filter_prepend_ex(&stream->readfilters, filter TSRMLS_CC);
1157        }
1158        if (ret != SUCCESS) {
1159            php_stream_filter_remove(filter, 1 TSRMLS_CC);
1160            RETURN_FALSE;
1161        }
1162    }
1163
1164    if (read_write & PHP_STREAM_FILTER_WRITE) {
1165        filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream) TSRMLS_CC);
1166        if (filter == NULL) {
1167            RETURN_FALSE;
1168        }
1169
1170        if (append) {
1171            ret = php_stream_filter_append_ex(&stream->writefilters, filter TSRMLS_CC);
1172        } else {
1173            ret = php_stream_filter_prepend_ex(&stream->writefilters, filter TSRMLS_CC);
1174        }
1175        if (ret != SUCCESS) {
1176            php_stream_filter_remove(filter, 1 TSRMLS_CC);
1177            RETURN_FALSE;
1178        }
1179    }
1180
1181    if (filter) {
1182        filter->res = ZEND_REGISTER_RESOURCE(NULL, filter, php_file_le_stream_filter());
1183        GC_REFCOUNT(filter->res)++;
1184        RETURN_RES(filter->res);
1185    } else {
1186        RETURN_FALSE;
1187    }
1188}
1189/* }}} */
1190
1191/* {{{ proto resource stream_filter_prepend(resource stream, string filtername[, int read_write[, string filterparams]])
1192   Prepend a filter to a stream */
1193PHP_FUNCTION(stream_filter_prepend)
1194{
1195    apply_filter_to_stream(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1196}
1197/* }}} */
1198
1199/* {{{ proto resource stream_filter_append(resource stream, string filtername[, int read_write[, string filterparams]])
1200   Append a filter to a stream */
1201PHP_FUNCTION(stream_filter_append)
1202{
1203    apply_filter_to_stream(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1204}
1205/* }}} */
1206
1207/* {{{ proto bool stream_filter_remove(resource stream_filter)
1208    Flushes any data in the filter's internal buffer, removes it from the chain, and frees the resource */
1209PHP_FUNCTION(stream_filter_remove)
1210{
1211    zval *zfilter;
1212    php_stream_filter *filter;
1213
1214    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zfilter) == FAILURE) {
1215        RETURN_FALSE;
1216    }
1217
1218    filter = zend_fetch_resource(zfilter TSRMLS_CC, -1, NULL, NULL, 1, php_file_le_stream_filter());
1219    if (!filter) {
1220        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid resource given, not a stream filter");
1221        RETURN_FALSE;
1222    }
1223
1224    if (php_stream_filter_flush(filter, 1) == FAILURE) {
1225        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to flush filter, not removing");
1226        RETURN_FALSE;
1227    }
1228
1229    if (zend_list_close(Z_RES_P(zfilter)) == FAILURE) {
1230        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not invalidate filter, not removing");
1231        RETURN_FALSE;
1232    } else {
1233        php_stream_filter_remove(filter, 1 TSRMLS_CC);
1234        RETURN_TRUE;
1235    }
1236}
1237/* }}} */
1238
1239/* {{{ proto string stream_get_line(resource stream, int maxlen [, string ending])
1240   Read up to maxlen bytes from a stream or until the ending string is found */
1241PHP_FUNCTION(stream_get_line)
1242{
1243    char *str = NULL;
1244    size_t str_len = 0;
1245    zend_long max_length;
1246    zval *zstream;
1247    zend_string *buf;
1248    php_stream *stream;
1249
1250    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|s", &zstream, &max_length, &str, &str_len) == FAILURE) {
1251        RETURN_FALSE;
1252    }
1253
1254    if (max_length < 0) {
1255        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The maximum allowed length must be greater than or equal to zero");
1256        RETURN_FALSE;
1257    }
1258    if (!max_length) {
1259        max_length = PHP_SOCK_CHUNK_SIZE;
1260    }
1261
1262    php_stream_from_zval(stream, zstream);
1263
1264    if ((buf = php_stream_get_record(stream, max_length, str, str_len TSRMLS_CC))) {
1265        RETURN_STR(buf);
1266    } else {
1267        RETURN_FALSE;
1268    }
1269}
1270
1271/* }}} */
1272
1273/* {{{ proto bool stream_set_blocking(resource socket, int mode)
1274   Set blocking/non-blocking mode on a socket or stream */
1275PHP_FUNCTION(stream_set_blocking)
1276{
1277    zval *arg1;
1278    int block;
1279    zend_long arg2;
1280    php_stream *stream;
1281
1282    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1283        return;
1284    }
1285
1286    php_stream_from_zval(stream, arg1);
1287
1288    block = arg2;
1289
1290    if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block == 0 ? 0 : 1, 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 TSRMLS_CC, "rl|l", &socket, &seconds, &microseconds) == FAILURE) {
1311        return;
1312    }
1313
1314    php_stream_from_zval(stream, socket);
1315
1316    t.tv_sec = seconds;
1317
1318    if (argc == 3) {
1319        t.tv_usec = microseconds % 1000000;
1320        t.tv_sec += microseconds / 1000000;
1321    } else {
1322        t.tv_usec = 0;
1323    }
1324
1325    if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)) {
1326        RETURN_TRUE;
1327    }
1328
1329    RETURN_FALSE;
1330}
1331#endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */
1332/* }}} */
1333
1334/* {{{ proto int stream_set_write_buffer(resource fp, int buffer)
1335   Set file write buffer */
1336PHP_FUNCTION(stream_set_write_buffer)
1337{
1338    zval *arg1;
1339    int ret;
1340    zend_long arg2;
1341    size_t buff;
1342    php_stream *stream;
1343
1344    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1345        RETURN_FALSE;
1346    }
1347
1348    php_stream_from_zval(stream, arg1);
1349
1350    buff = arg2;
1351
1352    /* if buff is 0 then set to non-buffered */
1353    if (buff == 0) {
1354        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1355    } else {
1356        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1357    }
1358
1359    RETURN_LONG(ret == 0 ? 0 : EOF);
1360}
1361/* }}} */
1362
1363/* {{{ proto int stream_set_chunk_size(resource fp, int chunk_size)
1364   Set the stream chunk size */
1365PHP_FUNCTION(stream_set_chunk_size)
1366{
1367    int         ret;
1368    zend_long       csize;
1369    zval        *zstream;
1370    php_stream  *stream;
1371
1372    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zstream, &csize) == FAILURE) {
1373        RETURN_FALSE;
1374    }
1375
1376    if (csize <= 0) {
1377        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The chunk size must be a positive integer, given " ZEND_LONG_FMT, csize);
1378        RETURN_FALSE;
1379    }
1380    /* stream.chunk_size is actually a size_t, but php_stream_set_option
1381     * can only use an int to accept the new value and return the old one.
1382     * In any case, values larger than INT_MAX for a chunk size make no sense.
1383     */
1384    if (csize > INT_MAX) {
1385        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The chunk size cannot be larger than %d", INT_MAX);
1386        RETURN_FALSE;
1387    }
1388
1389    php_stream_from_zval(stream, zstream);
1390
1391    ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL);
1392
1393    RETURN_LONG(ret > 0 ? (zend_long)ret : (zend_long)EOF);
1394}
1395/* }}} */
1396
1397/* {{{ proto int stream_set_read_buffer(resource fp, int buffer)
1398   Set file read buffer */
1399PHP_FUNCTION(stream_set_read_buffer)
1400{
1401    zval *arg1;
1402    int ret;
1403    zend_long arg2;
1404    size_t buff;
1405    php_stream *stream;
1406
1407    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1408        RETURN_FALSE;
1409    }
1410
1411    php_stream_from_zval(stream, arg1);
1412
1413    buff = arg2;
1414
1415    /* if buff is 0 then set to non-buffered */
1416    if (buff == 0) {
1417        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1418    } else {
1419        ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1420    }
1421
1422    RETURN_LONG(ret == 0 ? 0 : EOF);
1423}
1424/* }}} */
1425
1426/* {{{ proto int stream_socket_enable_crypto(resource stream, bool enable [, int cryptokind [, resource sessionstream]])
1427   Enable or disable a specific kind of crypto on the stream */
1428PHP_FUNCTION(stream_socket_enable_crypto)
1429{
1430    zend_long cryptokind = 0;
1431    zval *zstream, *zsessstream = NULL;
1432    php_stream *stream, *sessstream = NULL;
1433    zend_bool enable, cryptokindnull;
1434    int ret;
1435
1436    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rb|l!r", &zstream, &enable, &cryptokind, &cryptokindnull, &zsessstream) == FAILURE) {
1437        RETURN_FALSE;
1438    }
1439
1440    php_stream_from_zval(stream, zstream);
1441
1442    if (enable) {
1443        if (ZEND_NUM_ARGS() < 3 || cryptokindnull) {
1444            zval *val;
1445
1446            if (!GET_CTX_OPT(stream, "ssl", "crypto_method", val)) {
1447                php_error_docref(NULL TSRMLS_CC, E_WARNING, "When enabling encryption you must specify the crypto type");
1448                RETURN_FALSE;
1449            }
1450
1451            cryptokind = Z_LVAL_P(val);
1452        }
1453
1454        if (zsessstream) {
1455            php_stream_from_zval(sessstream, zsessstream);
1456        }
1457
1458        if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream TSRMLS_CC) < 0) {
1459            RETURN_FALSE;
1460        }
1461    }
1462
1463    ret = php_stream_xport_crypto_enable(stream, enable TSRMLS_CC);
1464    switch (ret) {
1465        case -1:
1466            RETURN_FALSE;
1467
1468        case 0:
1469            RETURN_LONG(0);
1470
1471        default:
1472            RETURN_TRUE;
1473    }
1474}
1475/* }}} */
1476
1477/* {{{ proto string stream_resolve_include_path(string filename)
1478Determine what file will be opened by calls to fopen() with a relative path */
1479PHP_FUNCTION(stream_resolve_include_path)
1480{
1481    char *filename, *resolved_path;
1482    size_t filename_len;
1483
1484    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
1485        return;
1486    }
1487
1488    resolved_path = zend_resolve_path(filename, filename_len TSRMLS_CC);
1489
1490    if (resolved_path) {
1491        // TODO: avoid reallocation ???
1492        RETVAL_STRING(resolved_path);
1493        efree(resolved_path);
1494        return;
1495    }
1496    RETURN_FALSE;
1497}
1498/* }}} */
1499
1500/* {{{ proto bool stream_is_local(resource stream|string url) U
1501*/
1502PHP_FUNCTION(stream_is_local)
1503{
1504    zval *zstream;
1505    php_stream *stream = NULL;
1506    php_stream_wrapper *wrapper = NULL;
1507
1508    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zstream) == FAILURE) {
1509        RETURN_FALSE;
1510    }
1511
1512    if (Z_TYPE_P(zstream) == IS_RESOURCE) {
1513        php_stream_from_zval(stream, zstream);
1514        if (stream == NULL) {
1515            RETURN_FALSE;
1516        }
1517        wrapper = stream->wrapper;
1518    } else {
1519        convert_to_string_ex(zstream);
1520
1521        wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0 TSRMLS_CC);
1522    }
1523
1524    if (!wrapper) {
1525        RETURN_FALSE;
1526    }
1527
1528    RETURN_BOOL(wrapper->is_url==0);
1529}
1530/* }}} */
1531
1532/* {{{ proto bool stream_supports_lock(resource stream)
1533   Tells whether the stream supports locking through flock(). */
1534PHP_FUNCTION(stream_supports_lock)
1535{
1536    php_stream *stream;
1537    zval *zsrc;
1538
1539    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsrc) == FAILURE) {
1540        RETURN_FALSE;
1541    }
1542
1543    php_stream_from_zval(stream, zsrc);
1544
1545    if (!php_stream_supports_lock(stream)) {
1546        RETURN_FALSE;
1547    }
1548
1549    RETURN_TRUE;
1550}
1551
1552#ifdef HAVE_SHUTDOWN
1553/* {{{ proto int stream_socket_shutdown(resource stream, int how)
1554    causes all or part of a full-duplex connection on the socket associated
1555    with stream to be shut down.  If how is SHUT_RD,  further receptions will
1556    be disallowed. If how is SHUT_WR, further transmissions will be disallowed.
1557    If how is SHUT_RDWR,  further  receptions and transmissions will be
1558    disallowed. */
1559PHP_FUNCTION(stream_socket_shutdown)
1560{
1561    zend_long how;
1562    zval *zstream;
1563    php_stream *stream;
1564
1565    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zstream, &how) == FAILURE) {
1566        RETURN_FALSE;
1567    }
1568
1569    if (how != STREAM_SHUT_RD &&
1570        how != STREAM_SHUT_WR &&
1571        how != STREAM_SHUT_RDWR) {
1572        RETURN_FALSE;
1573    }
1574
1575    php_stream_from_zval(stream, zstream);
1576
1577    RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how TSRMLS_CC) == 0);
1578}
1579/* }}} */
1580#endif
1581
1582/*
1583 * Local variables:
1584 * tab-width: 4
1585 * c-basic-offset: 4
1586 * End:
1587 * vim600: noet sw=4 ts=4 fdm=marker
1588 * vim<600: noet sw=4 ts=4
1589 */
1590
1591