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)
36 typedef unsigned long long php_timeout_ull;
37 #else
38 #include "win32/select.h"
39 #include "win32/sockets.h"
40 typedef unsigned __int64 php_timeout_ull;
41 #endif
42
43 static 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 */
PHP_FUNCTION(stream_socket_pair)50 PHP_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 */
PHP_FUNCTION(stream_socket_client)86 PHP_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, 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 */
PHP_FUNCTION(stream_socket_server)175 PHP_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, 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 */
PHP_FUNCTION(stream_socket_accept)240 PHP_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 */
PHP_FUNCTION(stream_socket_get_name)297 PHP_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 */
PHP_FUNCTION(stream_socket_sendto)325 PHP_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 */
PHP_FUNCTION(stream_socket_recvfrom)354 PHP_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 RETURN_STRINGL(read_buf, recvd, 0);
395 }
396
397 efree(read_buf);
398 RETURN_FALSE;
399 }
400 /* }}} */
401
402 /* {{{ proto string stream_get_contents(resource source [, long maxlen [, long offset]])
403 Reads all remaining bytes (or up to maxlen bytes) from a stream and returns them as a string. */
PHP_FUNCTION(stream_get_contents)404 PHP_FUNCTION(stream_get_contents)
405 {
406 php_stream *stream;
407 zval *zsrc;
408 long maxlen = PHP_STREAM_COPY_ALL,
409 desiredpos = -1L;
410 long len;
411 char *contents = NULL;
412
413 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ll", &zsrc, &maxlen, &desiredpos) == FAILURE) {
414 RETURN_FALSE;
415 }
416
417 php_stream_from_zval(stream, &zsrc);
418
419 if (desiredpos >= 0) {
420 int seek_res = 0;
421 off_t position;
422
423 position = php_stream_tell(stream);
424 if (position >= 0 && desiredpos > position) {
425 /* use SEEK_CUR to allow emulation in streams that don't support seeking */
426 seek_res = php_stream_seek(stream, desiredpos - position, SEEK_CUR);
427 } else if (desiredpos < position) {
428 /* desired position before position or error on tell */
429 seek_res = php_stream_seek(stream, desiredpos, SEEK_SET);
430 }
431
432 if (seek_res != 0) {
433 php_error_docref(NULL TSRMLS_CC, E_WARNING,
434 "Failed to seek to position %ld in the stream", desiredpos);
435 RETURN_FALSE;
436 }
437 }
438
439 len = php_stream_copy_to_mem(stream, &contents, maxlen, 0);
440
441 if (contents) {
442 if (len > INT_MAX) {
443 php_error_docref(NULL TSRMLS_CC, E_WARNING, "content truncated from %ld to %d bytes", len, INT_MAX);
444 len = INT_MAX;
445 }
446 RETVAL_STRINGL(contents, len, 0);
447 } else {
448 RETVAL_EMPTY_STRING();
449 }
450 }
451 /* }}} */
452
453 /* {{{ proto long stream_copy_to_stream(resource source, resource dest [, long maxlen [, long pos]])
454 Reads up to maxlen bytes from source stream and writes them to dest stream. */
PHP_FUNCTION(stream_copy_to_stream)455 PHP_FUNCTION(stream_copy_to_stream)
456 {
457 php_stream *src, *dest;
458 zval *zsrc, *zdest;
459 long maxlen = PHP_STREAM_COPY_ALL, pos = 0;
460 size_t len;
461 int ret;
462
463 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|ll", &zsrc, &zdest, &maxlen, &pos) == FAILURE) {
464 RETURN_FALSE;
465 }
466
467 php_stream_from_zval(src, &zsrc);
468 php_stream_from_zval(dest, &zdest);
469
470 if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) {
471 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek to position %ld in the stream", pos);
472 RETURN_FALSE;
473 }
474
475 ret = php_stream_copy_to_stream_ex(src, dest, maxlen, &len);
476
477 if (ret != SUCCESS) {
478 RETURN_FALSE;
479 }
480 RETURN_LONG(len);
481 }
482 /* }}} */
483
484 /* {{{ proto array stream_get_meta_data(resource fp)
485 Retrieves header/meta data from streams/file pointers */
PHP_FUNCTION(stream_get_meta_data)486 PHP_FUNCTION(stream_get_meta_data)
487 {
488 zval *arg1;
489 php_stream *stream;
490 zval *newval;
491
492 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FAILURE) {
493 return;
494 }
495 php_stream_from_zval(stream, &arg1);
496
497 array_init(return_value);
498
499 if (stream->wrapperdata) {
500 MAKE_STD_ZVAL(newval);
501 MAKE_COPY_ZVAL(&stream->wrapperdata, newval);
502
503 add_assoc_zval(return_value, "wrapper_data", newval);
504 }
505 if (stream->wrapper) {
506 add_assoc_string(return_value, "wrapper_type", (char *)stream->wrapper->wops->label, 1);
507 }
508 add_assoc_string(return_value, "stream_type", (char *)stream->ops->label, 1);
509
510 add_assoc_string(return_value, "mode", stream->mode, 1);
511
512 #if 0 /* TODO: needs updating for new filter API */
513 if (stream->filterhead) {
514 php_stream_filter *filter;
515
516 MAKE_STD_ZVAL(newval);
517 array_init(newval);
518
519 for (filter = stream->filterhead; filter != NULL; filter = filter->next) {
520 add_next_index_string(newval, (char *)filter->fops->label, 1);
521 }
522
523 add_assoc_zval(return_value, "filters", newval);
524 }
525 #endif
526
527 add_assoc_long(return_value, "unread_bytes", stream->writepos - stream->readpos);
528
529 add_assoc_bool(return_value, "seekable", (stream->ops->seek) && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0);
530 if (stream->orig_path) {
531 add_assoc_string(return_value, "uri", stream->orig_path, 1);
532 }
533
534 if (!php_stream_populate_meta_data(stream, return_value)) {
535 add_assoc_bool(return_value, "timed_out", 0);
536 add_assoc_bool(return_value, "blocked", 1);
537 add_assoc_bool(return_value, "eof", php_stream_eof(stream));
538 }
539
540 }
541 /* }}} */
542
543 /* {{{ proto array stream_get_transports()
544 Retrieves list of registered socket transports */
PHP_FUNCTION(stream_get_transports)545 PHP_FUNCTION(stream_get_transports)
546 {
547 HashTable *stream_xport_hash;
548 char *stream_xport;
549 uint stream_xport_len;
550 ulong num_key;
551
552 if (zend_parse_parameters_none() == FAILURE) {
553 return;
554 }
555
556 if ((stream_xport_hash = php_stream_xport_get_hash())) {
557 HashPosition pos;
558 array_init(return_value);
559 zend_hash_internal_pointer_reset_ex(stream_xport_hash, &pos);
560 while (zend_hash_get_current_key_ex(stream_xport_hash,
561 &stream_xport, &stream_xport_len,
562 &num_key, 0, &pos) == HASH_KEY_IS_STRING) {
563 add_next_index_stringl(return_value, stream_xport, stream_xport_len - 1, 1);
564 zend_hash_move_forward_ex(stream_xport_hash, &pos);
565 }
566 } else {
567 RETURN_FALSE;
568 }
569 }
570 /* }}} */
571
572 /* {{{ proto array stream_get_wrappers()
573 Retrieves list of registered stream wrappers */
PHP_FUNCTION(stream_get_wrappers)574 PHP_FUNCTION(stream_get_wrappers)
575 {
576 HashTable *url_stream_wrappers_hash;
577 char *stream_protocol;
578 int key_flags;
579 uint stream_protocol_len = 0;
580 ulong num_key;
581
582 if (zend_parse_parameters_none() == FAILURE) {
583 return;
584 }
585
586 if ((url_stream_wrappers_hash = php_stream_get_url_stream_wrappers_hash())) {
587 HashPosition pos;
588 array_init(return_value);
589 for (zend_hash_internal_pointer_reset_ex(url_stream_wrappers_hash, &pos);
590 (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;
591 zend_hash_move_forward_ex(url_stream_wrappers_hash, &pos)) {
592 if (key_flags == HASH_KEY_IS_STRING) {
593 add_next_index_stringl(return_value, stream_protocol, stream_protocol_len - 1, 1);
594 }
595 }
596 } else {
597 RETURN_FALSE;
598 }
599
600 }
601 /* }}} */
602
603 /* {{{ stream_select related functions */
stream_array_to_fd_set(zval * stream_array,fd_set * fds,php_socket_t * max_fd TSRMLS_DC)604 static int stream_array_to_fd_set(zval *stream_array, fd_set *fds, php_socket_t *max_fd TSRMLS_DC)
605 {
606 zval **elem;
607 php_stream *stream;
608 int cnt = 0;
609
610 if (Z_TYPE_P(stream_array) != IS_ARRAY) {
611 return 0;
612 }
613 for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream_array));
614 zend_hash_get_current_data(Z_ARRVAL_P(stream_array), (void **) &elem) == SUCCESS;
615 zend_hash_move_forward(Z_ARRVAL_P(stream_array))) {
616
617 /* Temporary int fd is needed for the STREAM data type on windows, passing this_fd directly to php_stream_cast()
618 would eventually bring a wrong result on x64. php_stream_cast() casts to int internally, and this will leave
619 the higher bits of a SOCKET variable uninitialized on systems with little endian. */
620 int tmp_fd;
621
622 php_stream_from_zval_no_verify(stream, elem);
623 if (stream == NULL) {
624 continue;
625 }
626 /* get the fd.
627 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
628 * when casting. It is only used here so that the buffered data warning
629 * is not displayed.
630 * */
631 if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&tmp_fd, 1) && tmp_fd != -1) {
632
633 php_socket_t this_fd = (php_socket_t)tmp_fd;
634
635 PHP_SAFE_FD_SET(this_fd, fds);
636
637 if (this_fd > *max_fd) {
638 *max_fd = this_fd;
639 }
640 cnt++;
641 }
642 }
643 return cnt ? 1 : 0;
644 }
645
stream_array_from_fd_set(zval * stream_array,fd_set * fds TSRMLS_DC)646 static int stream_array_from_fd_set(zval *stream_array, fd_set *fds TSRMLS_DC)
647 {
648 zval **elem, **dest_elem;
649 php_stream *stream;
650 HashTable *new_hash;
651 int ret = 0;
652
653 if (Z_TYPE_P(stream_array) != IS_ARRAY) {
654 return 0;
655 }
656 ALLOC_HASHTABLE(new_hash);
657 zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(stream_array)), NULL, ZVAL_PTR_DTOR, 0);
658
659 for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream_array));
660 zend_hash_has_more_elements(Z_ARRVAL_P(stream_array)) == SUCCESS;
661 zend_hash_move_forward(Z_ARRVAL_P(stream_array))) {
662
663 int type;
664 char *key;
665 uint key_len;
666 ulong num_ind;
667 /* Temporary int fd is needed for the STREAM data type on windows, passing this_fd directly to php_stream_cast()
668 would eventually bring a wrong result on x64. php_stream_cast() casts to int internally, and this will leave
669 the higher bits of a SOCKET variable uninitialized on systems with little endian. */
670 int tmp_fd;
671
672
673 type = zend_hash_get_current_key_ex(Z_ARRVAL_P(stream_array),
674 &key, &key_len, &num_ind, 0, NULL);
675 if (type == HASH_KEY_NON_EXISTANT ||
676 zend_hash_get_current_data(Z_ARRVAL_P(stream_array), (void **) &elem) == FAILURE) {
677 continue; /* should not happen */
678 }
679
680 php_stream_from_zval_no_verify(stream, elem);
681 if (stream == NULL) {
682 continue;
683 }
684 /* get the fd
685 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
686 * when casting. It is only used here so that the buffered data warning
687 * is not displayed.
688 */
689 if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&tmp_fd, 1) && tmp_fd != -1) {
690
691 php_socket_t this_fd = (php_socket_t)tmp_fd;
692
693 if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
694 if (type == HASH_KEY_IS_LONG) {
695 zend_hash_index_update(new_hash, num_ind, (void *)elem, sizeof(zval *), (void **)&dest_elem);
696 } else { /* HASH_KEY_IS_STRING */
697 zend_hash_update(new_hash, key, key_len, (void *)elem, sizeof(zval *), (void **)&dest_elem);
698 }
699
700 if (dest_elem) {
701 zval_add_ref(dest_elem);
702 }
703 ret++;
704 continue;
705 }
706 }
707 }
708
709 /* destroy old array and add new one */
710 zend_hash_destroy(Z_ARRVAL_P(stream_array));
711 efree(Z_ARRVAL_P(stream_array));
712
713 zend_hash_internal_pointer_reset(new_hash);
714 Z_ARRVAL_P(stream_array) = new_hash;
715
716 return ret;
717 }
718
stream_array_emulate_read_fd_set(zval * stream_array TSRMLS_DC)719 static int stream_array_emulate_read_fd_set(zval *stream_array TSRMLS_DC)
720 {
721 zval **elem, **dest_elem;
722 php_stream *stream;
723 HashTable *new_hash;
724 int ret = 0;
725
726 if (Z_TYPE_P(stream_array) != IS_ARRAY) {
727 return 0;
728 }
729 ALLOC_HASHTABLE(new_hash);
730 zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(stream_array)), NULL, ZVAL_PTR_DTOR, 0);
731
732 for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream_array));
733 zend_hash_get_current_data(Z_ARRVAL_P(stream_array), (void **) &elem) == SUCCESS;
734 zend_hash_move_forward(Z_ARRVAL_P(stream_array))) {
735
736 php_stream_from_zval_no_verify(stream, elem);
737 if (stream == NULL) {
738 continue;
739 }
740 if ((stream->writepos - stream->readpos) > 0) {
741 /* allow readable non-descriptor based streams to participate in stream_select.
742 * Non-descriptor streams will only "work" if they have previously buffered the
743 * data. Not ideal, but better than nothing.
744 * This branch of code also allows blocking streams with buffered data to
745 * operate correctly in stream_select.
746 * */
747 zend_hash_next_index_insert(new_hash, (void *)elem, sizeof(zval *), (void **)&dest_elem);
748 if (dest_elem) {
749 zval_add_ref(dest_elem);
750 }
751 ret++;
752 continue;
753 }
754 }
755
756 if (ret > 0) {
757 /* destroy old array and add new one */
758 zend_hash_destroy(Z_ARRVAL_P(stream_array));
759 efree(Z_ARRVAL_P(stream_array));
760
761 zend_hash_internal_pointer_reset(new_hash);
762 Z_ARRVAL_P(stream_array) = new_hash;
763 } else {
764 zend_hash_destroy(new_hash);
765 FREE_HASHTABLE(new_hash);
766 }
767
768 return ret;
769 }
770 /* }}} */
771
772 /* {{{ proto int stream_select(array &read_streams, array &write_streams, array &except_streams, int tv_sec[, int tv_usec])
773 Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */
PHP_FUNCTION(stream_select)774 PHP_FUNCTION(stream_select)
775 {
776 zval *r_array, *w_array, *e_array, **sec = NULL;
777 struct timeval tv;
778 struct timeval *tv_p = NULL;
779 fd_set rfds, wfds, efds;
780 php_socket_t max_fd = 0;
781 int retval, sets = 0;
782 long usec = 0;
783 int set_count, max_set_count = 0;
784
785 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!a!Z!|l", &r_array, &w_array, &e_array, &sec, &usec) == FAILURE)
786 return;
787
788 FD_ZERO(&rfds);
789 FD_ZERO(&wfds);
790 FD_ZERO(&efds);
791
792 if (r_array != NULL) {
793 set_count = stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
794 if (set_count > max_set_count)
795 max_set_count = set_count;
796 sets += set_count;
797 }
798
799 if (w_array != NULL) {
800 set_count = stream_array_to_fd_set(w_array, &wfds, &max_fd TSRMLS_CC);
801 if (set_count > max_set_count)
802 max_set_count = set_count;
803 sets += set_count;
804 }
805
806 if (e_array != NULL) {
807 set_count = stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
808 if (set_count > max_set_count)
809 max_set_count = set_count;
810 sets += set_count;
811 }
812
813 if (!sets) {
814 php_error_docref(NULL TSRMLS_CC, E_WARNING, "No stream arrays were passed");
815 RETURN_FALSE;
816 }
817
818 PHP_SAFE_MAX_FD(max_fd, max_set_count);
819
820 /* If seconds is not set to null, build the timeval, else we wait indefinitely */
821 if (sec != NULL) {
822 convert_to_long_ex(sec);
823
824 if (Z_LVAL_PP(sec) < 0) {
825 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The seconds parameter must be greater than 0");
826 RETURN_FALSE;
827 } else if (usec < 0) {
828 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The microseconds parameter must be greater than 0");
829 RETURN_FALSE;
830 }
831
832 /* Solaris + BSD do not like microsecond values which are >= 1 sec */
833 if (usec > 999999) {
834 tv.tv_sec = Z_LVAL_PP(sec) + (usec / 1000000);
835 tv.tv_usec = usec % 1000000;
836 } else {
837 tv.tv_sec = Z_LVAL_PP(sec);
838 tv.tv_usec = usec;
839 }
840
841 tv_p = &tv;
842 }
843
844 /* slight hack to support buffered data; if there is data sitting in the
845 * read buffer of any of the streams in the read array, let's pretend
846 * that we selected, but return only the readable sockets */
847 if (r_array != NULL) {
848
849 retval = stream_array_emulate_read_fd_set(r_array TSRMLS_CC);
850 if (retval > 0) {
851 if (w_array != NULL) {
852 zend_hash_clean(Z_ARRVAL_P(w_array));
853 }
854 if (e_array != NULL) {
855 zend_hash_clean(Z_ARRVAL_P(e_array));
856 }
857 RETURN_LONG(retval);
858 }
859 }
860
861 retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p);
862
863 if (retval == -1) {
864 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
865 errno, strerror(errno), max_fd);
866 RETURN_FALSE;
867 }
868
869 if (r_array != NULL) stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
870 if (w_array != NULL) stream_array_from_fd_set(w_array, &wfds TSRMLS_CC);
871 if (e_array != NULL) stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
872
873 RETURN_LONG(retval);
874 }
875 /* }}} */
876
877 /* {{{ stream_context related functions */
user_space_stream_notifier(php_stream_context * context,int notifycode,int severity,char * xmsg,int xcode,size_t bytes_sofar,size_t bytes_max,void * ptr TSRMLS_DC)878 static void user_space_stream_notifier(php_stream_context *context, int notifycode, int severity,
879 char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC)
880 {
881 zval *callback = (zval*)context->notifier->ptr;
882 zval *retval = NULL;
883 zval zvs[6];
884 zval *ps[6];
885 zval **ptps[6];
886 int i;
887
888 for (i = 0; i < 6; i++) {
889 INIT_ZVAL(zvs[i]);
890 ps[i] = &zvs[i];
891 ptps[i] = &ps[i];
892 MAKE_STD_ZVAL(ps[i]);
893 }
894
895 ZVAL_LONG(ps[0], notifycode);
896 ZVAL_LONG(ps[1], severity);
897 if (xmsg) {
898 ZVAL_STRING(ps[2], xmsg, 1);
899 } else {
900 ZVAL_NULL(ps[2]);
901 }
902 ZVAL_LONG(ps[3], xcode);
903 ZVAL_LONG(ps[4], bytes_sofar);
904 ZVAL_LONG(ps[5], bytes_max);
905
906 if (FAILURE == call_user_function_ex(EG(function_table), NULL, callback, &retval, 6, ptps, 0, NULL TSRMLS_CC)) {
907 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call user notifier");
908 }
909 for (i = 0; i < 6; i++) {
910 zval_ptr_dtor(&ps[i]);
911 }
912 if (retval) {
913 zval_ptr_dtor(&retval);
914 }
915 }
916
user_space_stream_notifier_dtor(php_stream_notifier * notifier)917 static void user_space_stream_notifier_dtor(php_stream_notifier *notifier)
918 {
919 if (notifier && notifier->ptr) {
920 zval_ptr_dtor((zval **)&(notifier->ptr));
921 notifier->ptr = NULL;
922 }
923 }
924
parse_context_options(php_stream_context * context,zval * options TSRMLS_DC)925 static int parse_context_options(php_stream_context *context, zval *options TSRMLS_DC)
926 {
927 HashPosition pos, opos;
928 zval **wval, **oval;
929 char *wkey, *okey;
930 uint wkey_len, okey_len;
931 int ret = SUCCESS;
932 ulong num_key;
933
934 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(options), &pos);
935 while (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_P(options), (void**)&wval, &pos)) {
936 if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_P(options), &wkey, &wkey_len, &num_key, 0, &pos)
937 && Z_TYPE_PP(wval) == IS_ARRAY) {
938
939 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(wval), &opos);
940 while (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(wval), (void**)&oval, &opos)) {
941
942 if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(wval), &okey, &okey_len, &num_key, 0, &opos)) {
943 php_stream_context_set_option(context, wkey, okey, *oval);
944 }
945 zend_hash_move_forward_ex(Z_ARRVAL_PP(wval), &opos);
946 }
947
948 } else {
949 php_error_docref(NULL TSRMLS_CC, E_WARNING, "options should have the form [\"wrappername\"][\"optionname\"] = $value");
950 }
951 zend_hash_move_forward_ex(Z_ARRVAL_P(options), &pos);
952 }
953
954 return ret;
955 }
956
parse_context_params(php_stream_context * context,zval * params TSRMLS_DC)957 static int parse_context_params(php_stream_context *context, zval *params TSRMLS_DC)
958 {
959 int ret = SUCCESS;
960 zval **tmp;
961
962 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(params), "notification", sizeof("notification"), (void**)&tmp)) {
963
964 if (context->notifier) {
965 php_stream_notification_free(context->notifier);
966 context->notifier = NULL;
967 }
968
969 context->notifier = php_stream_notification_alloc();
970 context->notifier->func = user_space_stream_notifier;
971 context->notifier->ptr = *tmp;
972 Z_ADDREF_P(*tmp);
973 context->notifier->dtor = user_space_stream_notifier_dtor;
974 }
975 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(params), "options", sizeof("options"), (void**)&tmp)) {
976 if (Z_TYPE_PP(tmp) == IS_ARRAY) {
977 parse_context_options(context, *tmp TSRMLS_CC);
978 } else {
979 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
980 }
981 }
982
983 return ret;
984 }
985
986 /* given a zval which is either a stream or a context, return the underlying
987 * stream_context. If it is a stream that does not have a context assigned, it
988 * will create and assign a context and return that. */
decode_context_param(zval * contextresource TSRMLS_DC)989 static php_stream_context *decode_context_param(zval *contextresource TSRMLS_DC)
990 {
991 php_stream_context *context = NULL;
992
993 context = zend_fetch_resource(&contextresource TSRMLS_CC, -1, NULL, NULL, 1, php_le_stream_context(TSRMLS_C));
994 if (context == NULL) {
995 php_stream *stream;
996
997 stream = zend_fetch_resource(&contextresource TSRMLS_CC, -1, NULL, NULL, 2, php_file_le_stream(), php_file_le_pstream);
998
999 if (stream) {
1000 context = stream->context;
1001 if (context == NULL) {
1002 /* Only way this happens is if file is opened with NO_DEFAULT_CONTEXT
1003 param, but then something is called which requires a context.
1004 Don't give them the default one though since they already said they
1005 didn't want it. */
1006 context = stream->context = php_stream_context_alloc(TSRMLS_C);
1007 }
1008 }
1009 }
1010
1011 return context;
1012 }
1013 /* }}} */
1014
1015 /* {{{ proto array stream_context_get_options(resource context|resource stream)
1016 Retrieve options for a stream/wrapper/context */
PHP_FUNCTION(stream_context_get_options)1017 PHP_FUNCTION(stream_context_get_options)
1018 {
1019 zval *zcontext;
1020 php_stream_context *context;
1021
1022 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zcontext) == FAILURE) {
1023 RETURN_FALSE;
1024 }
1025 context = decode_context_param(zcontext TSRMLS_CC);
1026 if (!context) {
1027 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1028 RETURN_FALSE;
1029 }
1030
1031 RETURN_ZVAL(context->options, 1, 0);
1032 }
1033 /* }}} */
1034
1035 /* {{{ proto bool stream_context_set_option(resource context|resource stream, string wrappername, string optionname, mixed value)
1036 Set an option for a wrapper */
PHP_FUNCTION(stream_context_set_option)1037 PHP_FUNCTION(stream_context_set_option)
1038 {
1039 zval *options = NULL, *zcontext = NULL, *zvalue = NULL;
1040 php_stream_context *context;
1041 char *wrappername, *optionname;
1042 int wrapperlen, optionlen;
1043
1044 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1045 "rssz", &zcontext, &wrappername, &wrapperlen,
1046 &optionname, &optionlen, &zvalue) == FAILURE) {
1047 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
1048 "ra", &zcontext, &options) == FAILURE) {
1049 php_error_docref(NULL TSRMLS_CC, E_WARNING, "called with wrong number or type of parameters; please RTM");
1050 RETURN_FALSE;
1051 }
1052 }
1053
1054 /* figure out where the context is coming from exactly */
1055 context = decode_context_param(zcontext TSRMLS_CC);
1056 if (!context) {
1057 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1058 RETURN_FALSE;
1059 }
1060
1061 if (options) {
1062 /* handle the array syntax */
1063 RETVAL_BOOL(parse_context_options(context, options TSRMLS_CC) == SUCCESS);
1064 } else {
1065 php_stream_context_set_option(context, wrappername, optionname, zvalue);
1066 RETVAL_TRUE;
1067 }
1068 }
1069 /* }}} */
1070
1071 /* {{{ proto bool stream_context_set_params(resource context|resource stream, array options)
1072 Set parameters for a file context */
PHP_FUNCTION(stream_context_set_params)1073 PHP_FUNCTION(stream_context_set_params)
1074 {
1075 zval *params, *zcontext;
1076 php_stream_context *context;
1077
1078 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &zcontext, ¶ms) == FAILURE) {
1079 RETURN_FALSE;
1080 }
1081
1082 context = decode_context_param(zcontext TSRMLS_CC);
1083 if (!context) {
1084 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1085 RETURN_FALSE;
1086 }
1087
1088 RETVAL_BOOL(parse_context_params(context, params TSRMLS_CC) == SUCCESS);
1089 }
1090 /* }}} */
1091
1092 /* {{{ proto array stream_context_get_params(resource context|resource stream)
1093 Get parameters of a file context */
PHP_FUNCTION(stream_context_get_params)1094 PHP_FUNCTION(stream_context_get_params)
1095 {
1096 zval *zcontext, *options;
1097 php_stream_context *context;
1098
1099 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zcontext) == FAILURE) {
1100 RETURN_FALSE;
1101 }
1102
1103 context = decode_context_param(zcontext TSRMLS_CC);
1104 if (!context) {
1105 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid stream/context parameter");
1106 RETURN_FALSE;
1107 }
1108
1109 array_init(return_value);
1110 if (context->notifier && context->notifier->ptr && context->notifier->func == user_space_stream_notifier) {
1111 add_assoc_zval_ex(return_value, ZEND_STRS("notification"), context->notifier->ptr);
1112 Z_ADDREF_P(context->notifier->ptr);
1113 }
1114 ALLOC_INIT_ZVAL(options);
1115 ZVAL_ZVAL(options, context->options, 1, 0);
1116 add_assoc_zval_ex(return_value, ZEND_STRS("options"), options);
1117 }
1118 /* }}} */
1119
1120 /* {{{ proto resource stream_context_get_default([array options])
1121 Get a handle on the default file/stream context and optionally set parameters */
PHP_FUNCTION(stream_context_get_default)1122 PHP_FUNCTION(stream_context_get_default)
1123 {
1124 zval *params = NULL;
1125 php_stream_context *context;
1126
1127 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a", ¶ms) == FAILURE) {
1128 RETURN_FALSE;
1129 }
1130
1131 if (FG(default_context) == NULL) {
1132 FG(default_context) = php_stream_context_alloc(TSRMLS_C);
1133 }
1134 context = FG(default_context);
1135
1136 if (params) {
1137 parse_context_options(context, params TSRMLS_CC);
1138 }
1139
1140 php_stream_context_to_zval(context, return_value);
1141 }
1142 /* }}} */
1143
1144 /* {{{ proto resource stream_context_set_default(array options)
1145 Set default file/stream context, returns the context as a resource */
PHP_FUNCTION(stream_context_set_default)1146 PHP_FUNCTION(stream_context_set_default)
1147 {
1148 zval *options = NULL;
1149 php_stream_context *context;
1150
1151 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &options) == FAILURE) {
1152 return;
1153 }
1154
1155 if (FG(default_context) == NULL) {
1156 FG(default_context) = php_stream_context_alloc(TSRMLS_C);
1157 }
1158 context = FG(default_context);
1159
1160 parse_context_options(context, options TSRMLS_CC);
1161
1162 php_stream_context_to_zval(context, return_value);
1163 }
1164 /* }}} */
1165
1166 /* {{{ proto resource stream_context_create([array options[, array params]])
1167 Create a file context and optionally set parameters */
PHP_FUNCTION(stream_context_create)1168 PHP_FUNCTION(stream_context_create)
1169 {
1170 zval *options = NULL, *params = NULL;
1171 php_stream_context *context;
1172
1173 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!a!", &options, ¶ms) == FAILURE) {
1174 RETURN_FALSE;
1175 }
1176
1177 context = php_stream_context_alloc(TSRMLS_C);
1178
1179 if (options) {
1180 parse_context_options(context, options TSRMLS_CC);
1181 }
1182
1183 if (params) {
1184 parse_context_params(context, params TSRMLS_CC);
1185 }
1186
1187 RETURN_RESOURCE(context->rsrc_id);
1188 }
1189 /* }}} */
1190
1191 /* {{{ streams filter functions */
apply_filter_to_stream(int append,INTERNAL_FUNCTION_PARAMETERS)1192 static void apply_filter_to_stream(int append, INTERNAL_FUNCTION_PARAMETERS)
1193 {
1194 zval *zstream;
1195 php_stream *stream;
1196 char *filtername;
1197 int filternamelen;
1198 long read_write = 0;
1199 zval *filterparams = NULL;
1200 php_stream_filter *filter = NULL;
1201 int ret;
1202
1203 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lz", &zstream,
1204 &filtername, &filternamelen, &read_write, &filterparams) == FAILURE) {
1205 RETURN_FALSE;
1206 }
1207
1208 php_stream_from_zval(stream, &zstream);
1209
1210 if ((read_write & PHP_STREAM_FILTER_ALL) == 0) {
1211 /* Chain not specified.
1212 * Examine stream->mode to determine which filters are needed
1213 * There's no harm in attaching a filter to an unused chain,
1214 * but why waste the memory and clock cycles?
1215 */
1216 if (strchr(stream->mode, 'r') || strchr(stream->mode, '+')) {
1217 read_write |= PHP_STREAM_FILTER_READ;
1218 }
1219 if (strchr(stream->mode, 'w') || strchr(stream->mode, '+') || strchr(stream->mode, 'a')) {
1220 read_write |= PHP_STREAM_FILTER_WRITE;
1221 }
1222 }
1223
1224 if (read_write & PHP_STREAM_FILTER_READ) {
1225 filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream) TSRMLS_CC);
1226 if (filter == NULL) {
1227 RETURN_FALSE;
1228 }
1229
1230 if (append) {
1231 ret = php_stream_filter_append_ex(&stream->readfilters, filter TSRMLS_CC);
1232 } else {
1233 ret = php_stream_filter_prepend_ex(&stream->readfilters, filter TSRMLS_CC);
1234 }
1235 if (ret != SUCCESS) {
1236 php_stream_filter_remove(filter, 1 TSRMLS_CC);
1237 RETURN_FALSE;
1238 }
1239 }
1240
1241 if (read_write & PHP_STREAM_FILTER_WRITE) {
1242 filter = php_stream_filter_create(filtername, filterparams, php_stream_is_persistent(stream) TSRMLS_CC);
1243 if (filter == NULL) {
1244 RETURN_FALSE;
1245 }
1246
1247 if (append) {
1248 ret = php_stream_filter_append_ex(&stream->writefilters, filter TSRMLS_CC);
1249 } else {
1250 ret = php_stream_filter_prepend_ex(&stream->writefilters, filter TSRMLS_CC);
1251 }
1252 if (ret != SUCCESS) {
1253 php_stream_filter_remove(filter, 1 TSRMLS_CC);
1254 RETURN_FALSE;
1255 }
1256 }
1257
1258 if (filter) {
1259 RETURN_RESOURCE(filter->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, filter, php_file_le_stream_filter()));
1260 } else {
1261 RETURN_FALSE;
1262 }
1263 }
1264 /* }}} */
1265
1266 /* {{{ proto resource stream_filter_prepend(resource stream, string filtername[, int read_write[, string filterparams]])
1267 Prepend a filter to a stream */
PHP_FUNCTION(stream_filter_prepend)1268 PHP_FUNCTION(stream_filter_prepend)
1269 {
1270 apply_filter_to_stream(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1271 }
1272 /* }}} */
1273
1274 /* {{{ proto resource stream_filter_append(resource stream, string filtername[, int read_write[, string filterparams]])
1275 Append a filter to a stream */
PHP_FUNCTION(stream_filter_append)1276 PHP_FUNCTION(stream_filter_append)
1277 {
1278 apply_filter_to_stream(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
1279 }
1280 /* }}} */
1281
1282 /* {{{ proto bool stream_filter_remove(resource stream_filter)
1283 Flushes any data in the filter's internal buffer, removes it from the chain, and frees the resource */
PHP_FUNCTION(stream_filter_remove)1284 PHP_FUNCTION(stream_filter_remove)
1285 {
1286 zval *zfilter;
1287 php_stream_filter *filter;
1288
1289 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zfilter) == FAILURE) {
1290 RETURN_FALSE;
1291 }
1292
1293 filter = zend_fetch_resource(&zfilter TSRMLS_CC, -1, NULL, NULL, 1, php_file_le_stream_filter());
1294 if (!filter) {
1295 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid resource given, not a stream filter");
1296 RETURN_FALSE;
1297 }
1298
1299 if (php_stream_filter_flush(filter, 1) == FAILURE) {
1300 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to flush filter, not removing");
1301 RETURN_FALSE;
1302 }
1303
1304 if (zend_list_delete(Z_LVAL_P(zfilter)) == FAILURE) {
1305 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not invalidate filter, not removing");
1306 RETURN_FALSE;
1307 } else {
1308 php_stream_filter_remove(filter, 1 TSRMLS_CC);
1309 RETURN_TRUE;
1310 }
1311 }
1312 /* }}} */
1313
1314 /* {{{ proto string stream_get_line(resource stream, int maxlen [, string ending])
1315 Read up to maxlen bytes from a stream or until the ending string is found */
PHP_FUNCTION(stream_get_line)1316 PHP_FUNCTION(stream_get_line)
1317 {
1318 char *str = NULL;
1319 int str_len = 0;
1320 long max_length;
1321 zval *zstream;
1322 char *buf;
1323 size_t buf_size;
1324 php_stream *stream;
1325
1326 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|s", &zstream, &max_length, &str, &str_len) == FAILURE) {
1327 RETURN_FALSE;
1328 }
1329
1330 if (max_length < 0) {
1331 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The maximum allowed length must be greater than or equal to zero");
1332 RETURN_FALSE;
1333 }
1334 if (!max_length) {
1335 max_length = PHP_SOCK_CHUNK_SIZE;
1336 }
1337
1338 php_stream_from_zval(stream, &zstream);
1339
1340 if ((buf = php_stream_get_record(stream, max_length, &buf_size, str, str_len TSRMLS_CC))) {
1341 RETURN_STRINGL(buf, buf_size, 0);
1342 } else {
1343 RETURN_FALSE;
1344 }
1345 }
1346
1347 /* }}} */
1348
1349 /* {{{ proto bool stream_set_blocking(resource socket, int mode)
1350 Set blocking/non-blocking mode on a socket or stream */
PHP_FUNCTION(stream_set_blocking)1351 PHP_FUNCTION(stream_set_blocking)
1352 {
1353 zval *arg1;
1354 int block;
1355 long arg2;
1356 php_stream *stream;
1357
1358 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1359 return;
1360 }
1361
1362 php_stream_from_zval(stream, &arg1);
1363
1364 block = arg2;
1365
1366 if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block == 0 ? 0 : 1, NULL) == -1) {
1367 RETURN_FALSE;
1368 }
1369
1370 RETURN_TRUE;
1371 }
1372
1373 /* }}} */
1374
1375 /* {{{ proto bool stream_set_timeout(resource stream, int seconds [, int microseconds])
1376 Set timeout on stream read to seconds + microseonds */
1377 #if HAVE_SYS_TIME_H || defined(PHP_WIN32)
PHP_FUNCTION(stream_set_timeout)1378 PHP_FUNCTION(stream_set_timeout)
1379 {
1380 zval *socket;
1381 long seconds, microseconds = 0;
1382 struct timeval t;
1383 php_stream *stream;
1384 int argc = ZEND_NUM_ARGS();
1385
1386 if (zend_parse_parameters(argc TSRMLS_CC, "rl|l", &socket, &seconds, µseconds) == FAILURE) {
1387 return;
1388 }
1389
1390 php_stream_from_zval(stream, &socket);
1391
1392 t.tv_sec = seconds;
1393
1394 if (argc == 3) {
1395 t.tv_usec = microseconds % 1000000;
1396 t.tv_sec += microseconds / 1000000;
1397 } else {
1398 t.tv_usec = 0;
1399 }
1400
1401 if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)) {
1402 RETURN_TRUE;
1403 }
1404
1405 RETURN_FALSE;
1406 }
1407 #endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */
1408 /* }}} */
1409
1410 /* {{{ proto int stream_set_write_buffer(resource fp, int buffer)
1411 Set file write buffer */
PHP_FUNCTION(stream_set_write_buffer)1412 PHP_FUNCTION(stream_set_write_buffer)
1413 {
1414 zval *arg1;
1415 int ret;
1416 long arg2;
1417 size_t buff;
1418 php_stream *stream;
1419
1420 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1421 RETURN_FALSE;
1422 }
1423
1424 php_stream_from_zval(stream, &arg1);
1425
1426 buff = arg2;
1427
1428 /* if buff is 0 then set to non-buffered */
1429 if (buff == 0) {
1430 ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1431 } else {
1432 ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1433 }
1434
1435 RETURN_LONG(ret == 0 ? 0 : EOF);
1436 }
1437 /* }}} */
1438
1439 /* {{{ proto int stream_set_chunk_size(resource fp, int chunk_size)
1440 Set the stream chunk size */
PHP_FUNCTION(stream_set_chunk_size)1441 PHP_FUNCTION(stream_set_chunk_size)
1442 {
1443 int ret;
1444 long csize;
1445 zval *zstream;
1446 php_stream *stream;
1447
1448 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zstream, &csize) == FAILURE) {
1449 RETURN_FALSE;
1450 }
1451
1452 if (csize <= 0) {
1453 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The chunk size must be a positive integer, given %ld", csize);
1454 RETURN_FALSE;
1455 }
1456 /* stream.chunk_size is actually a size_t, but php_stream_set_option
1457 * can only use an int to accept the new value and return the old one.
1458 * In any case, values larger than INT_MAX for a chunk size make no sense.
1459 */
1460 if (csize > INT_MAX) {
1461 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The chunk size cannot be larger than %d", INT_MAX);
1462 RETURN_FALSE;
1463 }
1464
1465 php_stream_from_zval(stream, &zstream);
1466
1467 ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL);
1468
1469 RETURN_LONG(ret > 0 ? (long)ret : (long)EOF);
1470 }
1471 /* }}} */
1472
1473 /* {{{ proto int stream_set_read_buffer(resource fp, int buffer)
1474 Set file read buffer */
PHP_FUNCTION(stream_set_read_buffer)1475 PHP_FUNCTION(stream_set_read_buffer)
1476 {
1477 zval *arg1;
1478 int ret;
1479 long arg2;
1480 size_t buff;
1481 php_stream *stream;
1482
1483 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &arg2) == FAILURE) {
1484 RETURN_FALSE;
1485 }
1486
1487 php_stream_from_zval(stream, &arg1);
1488
1489 buff = arg2;
1490
1491 /* if buff is 0 then set to non-buffered */
1492 if (buff == 0) {
1493 ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL);
1494 } else {
1495 ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff);
1496 }
1497
1498 RETURN_LONG(ret == 0 ? 0 : EOF);
1499 }
1500 /* }}} */
1501
1502 /* {{{ proto int stream_socket_enable_crypto(resource stream, bool enable [, int cryptokind [, resource sessionstream]])
1503 Enable or disable a specific kind of crypto on the stream */
PHP_FUNCTION(stream_socket_enable_crypto)1504 PHP_FUNCTION(stream_socket_enable_crypto)
1505 {
1506 long cryptokind = 0;
1507 zval *zstream, *zsessstream = NULL;
1508 php_stream *stream, *sessstream = NULL;
1509 zend_bool enable;
1510 int ret;
1511
1512 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rb|lr", &zstream, &enable, &cryptokind, &zsessstream) == FAILURE) {
1513 RETURN_FALSE;
1514 }
1515
1516 php_stream_from_zval(stream, &zstream);
1517
1518 if (ZEND_NUM_ARGS() >= 3) {
1519 if (zsessstream) {
1520 php_stream_from_zval(sessstream, &zsessstream);
1521 }
1522
1523 if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream TSRMLS_CC) < 0) {
1524 RETURN_FALSE;
1525 }
1526 } else if (enable) {
1527 php_error_docref(NULL TSRMLS_CC, E_WARNING, "When enabling encryption you must specify the crypto type");
1528 RETURN_FALSE;
1529 }
1530
1531 ret = php_stream_xport_crypto_enable(stream, enable TSRMLS_CC);
1532 switch (ret) {
1533 case -1:
1534 RETURN_FALSE;
1535
1536 case 0:
1537 RETURN_LONG(0);
1538
1539 default:
1540 RETURN_TRUE;
1541 }
1542 }
1543 /* }}} */
1544
1545 /* {{{ proto string stream_resolve_include_path(string filename)
1546 Determine what file will be opened by calls to fopen() with a relative path */
PHP_FUNCTION(stream_resolve_include_path)1547 PHP_FUNCTION(stream_resolve_include_path)
1548 {
1549 char *filename, *resolved_path;
1550 int filename_len;
1551
1552 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
1553 return;
1554 }
1555
1556 resolved_path = zend_resolve_path(filename, filename_len TSRMLS_CC);
1557
1558 if (resolved_path) {
1559 RETURN_STRING(resolved_path, 0);
1560 }
1561 RETURN_FALSE;
1562 }
1563 /* }}} */
1564
1565 /* {{{ proto bool stream_is_local(resource stream|string url) U
1566 */
PHP_FUNCTION(stream_is_local)1567 PHP_FUNCTION(stream_is_local)
1568 {
1569 zval **zstream;
1570 php_stream *stream = NULL;
1571 php_stream_wrapper *wrapper = NULL;
1572
1573 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &zstream) == FAILURE) {
1574 RETURN_FALSE;
1575 }
1576
1577 if (Z_TYPE_PP(zstream) == IS_RESOURCE) {
1578 php_stream_from_zval(stream, zstream);
1579 if (stream == NULL) {
1580 RETURN_FALSE;
1581 }
1582 wrapper = stream->wrapper;
1583 } else {
1584 convert_to_string_ex(zstream);
1585
1586 wrapper = php_stream_locate_url_wrapper(Z_STRVAL_PP(zstream), NULL, 0 TSRMLS_CC);
1587 }
1588
1589 if (!wrapper) {
1590 RETURN_FALSE;
1591 }
1592
1593 RETURN_BOOL(wrapper->is_url==0);
1594 }
1595 /* }}} */
1596
1597 /* {{{ proto bool stream_supports_lock(resource stream)
1598 Tells whether the stream supports locking through flock(). */
PHP_FUNCTION(stream_supports_lock)1599 PHP_FUNCTION(stream_supports_lock)
1600 {
1601 php_stream *stream;
1602 zval *zsrc;
1603
1604 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zsrc) == FAILURE) {
1605 RETURN_FALSE;
1606 }
1607
1608 php_stream_from_zval(stream, &zsrc);
1609
1610 if (!php_stream_supports_lock(stream)) {
1611 RETURN_FALSE;
1612 }
1613
1614 RETURN_TRUE;
1615 }
1616
1617 #ifdef HAVE_SHUTDOWN
1618 /* {{{ proto int stream_socket_shutdown(resource stream, int how)
1619 causes all or part of a full-duplex connection on the socket associated
1620 with stream to be shut down. If how is SHUT_RD, further receptions will
1621 be disallowed. If how is SHUT_WR, further transmissions will be disallowed.
1622 If how is SHUT_RDWR, further receptions and transmissions will be
1623 disallowed. */
PHP_FUNCTION(stream_socket_shutdown)1624 PHP_FUNCTION(stream_socket_shutdown)
1625 {
1626 long how;
1627 zval *zstream;
1628 php_stream *stream;
1629
1630 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zstream, &how) == FAILURE) {
1631 RETURN_FALSE;
1632 }
1633
1634 if (how != STREAM_SHUT_RD &&
1635 how != STREAM_SHUT_WR &&
1636 how != STREAM_SHUT_RDWR) {
1637 RETURN_FALSE;
1638 }
1639
1640 php_stream_from_zval(stream, &zstream);
1641
1642 RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how TSRMLS_CC) == 0);
1643 }
1644 /* }}} */
1645 #endif
1646
1647 /*
1648 * Local variables:
1649 * tab-width: 4
1650 * c-basic-offset: 4
1651 * End:
1652 * vim600: noet sw=4 ts=4 fdm=marker
1653 * vim<600: noet sw=4 ts=4
1654 */
1655
1656