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