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