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)
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, 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 */
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, 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 */
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 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. */
PHP_FUNCTION(stream_get_contents)410 PHP_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. */
PHP_FUNCTION(stream_copy_to_stream)462 PHP_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 */
PHP_FUNCTION(stream_get_meta_data)493 PHP_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 */
PHP_FUNCTION(stream_get_transports)552 PHP_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 */
PHP_FUNCTION(stream_get_wrappers)581 PHP_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 */
stream_array_to_fd_set(zval * stream_array,fd_set * fds,php_socket_t * max_fd TSRMLS_DC)610 static 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
stream_array_from_fd_set(zval * stream_array,fd_set * fds TSRMLS_DC)652 static 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
stream_array_emulate_read_fd_set(zval * stream_array TSRMLS_DC)708 static 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 */
PHP_FUNCTION(stream_select)763 PHP_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 */
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)867 static 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
user_space_stream_notifier_dtor(php_stream_notifier * notifier)906 static 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
parse_context_options(php_stream_context * context,zval * options TSRMLS_DC)914 static 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
parse_context_params(php_stream_context * context,zval * params TSRMLS_DC)946 static 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. */
decode_context_param(zval * contextresource TSRMLS_DC)978 static 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 */
PHP_FUNCTION(stream_context_get_options)1006 PHP_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 */
PHP_FUNCTION(stream_context_set_option)1026 PHP_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 */
PHP_FUNCTION(stream_context_set_params)1062 PHP_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, ¶ms) == 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 */
PHP_FUNCTION(stream_context_get_params)1083 PHP_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 */
PHP_FUNCTION(stream_context_get_default)1111 PHP_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", ¶ms) == 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 */
PHP_FUNCTION(stream_context_set_default)1135 PHP_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 */
PHP_FUNCTION(stream_context_create)1157 PHP_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, ¶ms) == 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 */
apply_filter_to_stream(int append,INTERNAL_FUNCTION_PARAMETERS)1181 static 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 */
PHP_FUNCTION(stream_filter_prepend)1257 PHP_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 */
PHP_FUNCTION(stream_filter_append)1265 PHP_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 */
PHP_FUNCTION(stream_filter_remove)1273 PHP_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 */
PHP_FUNCTION(stream_get_line)1305 PHP_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 */
PHP_FUNCTION(stream_set_blocking)1340 PHP_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)
PHP_FUNCTION(stream_set_timeout)1367 PHP_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, µseconds) == 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 */
PHP_FUNCTION(stream_set_write_buffer)1401 PHP_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 */
PHP_FUNCTION(stream_set_read_buffer)1430 PHP_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 */
PHP_FUNCTION(stream_socket_enable_crypto)1459 PHP_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)
1501 Determine what file will be opened by calls to fopen() with a relative path */
PHP_FUNCTION(stream_resolve_include_path)1502 PHP_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 */
PHP_FUNCTION(stream_is_local)1522 PHP_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(). */
PHP_FUNCTION(stream_supports_lock)1554 PHP_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. */
PHP_FUNCTION(stream_socket_shutdown)1579 PHP_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