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 | | Author: Sterling Hughes <sterling@php.net> | |
16 | +----------------------------------------------------------------------+ |
17 | */ |
18 | |
19 | /* $Id$ */ |
20 | |
21 | #define |
22 | |
23 | #ifdef HAVE_CONFIG_H |
24 | #include "config.h" |
25 | #endif |
26 | |
27 | #include "php.h" |
28 | |
29 | #if HAVE_CURL |
30 | |
31 | #include "php_curl.h" |
32 | |
33 | #include <curl/curl.h> |
34 | #include <curl/multi.h> |
35 | |
36 | #ifdef HAVE_SYS_SELECT_H |
37 | #include <sys/select.h> |
38 | #endif |
39 | |
40 | #ifdef HAVE_SYS_TIME_H |
41 | #include <sys/time.h> |
42 | #endif |
43 | |
44 | #ifdef HAVE_SYS_TYPES_H |
45 | #include <sys/types.h> |
46 | #endif |
47 | |
48 | #ifdef HAVE_UNISTD_H |
49 | #include <unistd.h> |
50 | #endif |
51 | |
52 | /* {{{ proto resource curl_multi_init(void) |
53 | Returns a new cURL multi handle */ |
54 | PHP_FUNCTION(curl_multi_init) |
55 | { |
56 | php_curlm *mh; |
57 | |
58 | if (zend_parse_parameters_none() == FAILURE) { |
59 | return; |
60 | } |
61 | |
62 | mh = ecalloc(1, sizeof(php_curlm)); |
63 | mh->multi = curl_multi_init(); |
64 | |
65 | zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0); |
66 | |
67 | ZEND_REGISTER_RESOURCE(return_value, mh, le_curl_multi_handle); |
68 | } |
69 | /* }}} */ |
70 | |
71 | /* {{{ proto int curl_multi_add_handle(resource mh, resource ch) |
72 | Add a normal cURL handle to a cURL multi handle */ |
73 | PHP_FUNCTION(curl_multi_add_handle) |
74 | { |
75 | zval *z_mh; |
76 | zval *z_ch; |
77 | php_curlm *mh; |
78 | php_curl *ch; |
79 | zval tmp_val; |
80 | |
81 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr" , &z_mh, &z_ch) == FAILURE) { |
82 | return; |
83 | } |
84 | |
85 | ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle); |
86 | ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl); |
87 | |
88 | _php_curl_cleanup_handle(ch); |
89 | |
90 | /* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */ |
91 | tmp_val = *z_ch; |
92 | zval_copy_ctor(&tmp_val); |
93 | |
94 | zend_llist_add_element(&mh->easyh, &tmp_val); |
95 | |
96 | RETURN_LONG((long) curl_multi_add_handle(mh->multi, ch->cp)); |
97 | } |
98 | /* }}} */ |
99 | |
100 | void _php_curl_multi_cleanup_list(void *data) /* {{{ */ |
101 | { |
102 | zval *z_ch = (zval *)data; |
103 | php_curl *ch; |
104 | TSRMLS_FETCH(); |
105 | |
106 | if (!z_ch) { |
107 | return; |
108 | } |
109 | |
110 | ch = (php_curl *) zend_fetch_resource(&z_ch TSRMLS_CC, -1, le_curl_name, NULL, 1, le_curl); |
111 | if (!ch) { |
112 | return; |
113 | } |
114 | |
115 | zend_list_delete(Z_LVAL_P(z_ch)); |
116 | } |
117 | /* }}} */ |
118 | |
119 | /* Used internally as comparison routine passed to zend_list_del_element */ |
120 | static int curl_compare_resources( zval *z1, zval **z2 ) /* {{{ */ |
121 | { |
122 | return (Z_TYPE_P( z1 ) == Z_TYPE_PP( z2 ) && |
123 | Z_TYPE_P( z1 ) == IS_RESOURCE && |
124 | Z_LVAL_P( z1 ) == Z_LVAL_PP( z2 ) ); |
125 | } |
126 | /* }}} */ |
127 | |
128 | /* {{{ proto int curl_multi_remove_handle(resource mh, resource ch) |
129 | Remove a multi handle from a set of cURL handles */ |
130 | PHP_FUNCTION(curl_multi_remove_handle) |
131 | { |
132 | zval *z_mh; |
133 | zval *z_ch; |
134 | php_curlm *mh; |
135 | php_curl *ch; |
136 | |
137 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr" , &z_mh, &z_ch) == FAILURE) { |
138 | return; |
139 | } |
140 | |
141 | ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle); |
142 | ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl); |
143 | |
144 | |
145 | |
146 | RETVAL_LONG((long) curl_multi_remove_handle(mh->multi, ch->cp)); |
147 | zend_llist_del_element( &mh->easyh, &z_ch, |
148 | (int (*)(void *, void *)) curl_compare_resources ); |
149 | |
150 | } |
151 | /* }}} */ |
152 | |
153 | static void _make_timeval_struct(struct timeval *to, double timeout) /* {{{ */ |
154 | { |
155 | unsigned long conv; |
156 | |
157 | conv = (unsigned long) (timeout * 1000000.0); |
158 | to->tv_sec = conv / 1000000; |
159 | to->tv_usec = conv % 1000000; |
160 | } |
161 | /* }}} */ |
162 | |
163 | /* {{{ proto int curl_multi_select(resource mh[, double timeout]) |
164 | Get all the sockets associated with the cURL extension, which can then be "selected" */ |
165 | PHP_FUNCTION(curl_multi_select) |
166 | { |
167 | zval *z_mh; |
168 | php_curlm *mh; |
169 | fd_set readfds; |
170 | fd_set writefds; |
171 | fd_set exceptfds; |
172 | int maxfd; |
173 | double timeout = 1.0; |
174 | struct timeval to; |
175 | |
176 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|d" , &z_mh, &timeout) == FAILURE) { |
177 | return; |
178 | } |
179 | |
180 | ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle); |
181 | |
182 | _make_timeval_struct(&to, timeout); |
183 | |
184 | FD_ZERO(&readfds); |
185 | FD_ZERO(&writefds); |
186 | FD_ZERO(&exceptfds); |
187 | |
188 | curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd); |
189 | if (maxfd == -1) { |
190 | RETURN_LONG(-1); |
191 | } |
192 | RETURN_LONG(select(maxfd + 1, &readfds, &writefds, &exceptfds, &to)); |
193 | } |
194 | /* }}} */ |
195 | |
196 | /* {{{ proto int curl_multi_exec(resource mh, int &still_running) |
197 | Run the sub-connections of the current cURL handle */ |
198 | PHP_FUNCTION(curl_multi_exec) |
199 | { |
200 | zval *z_mh; |
201 | zval *z_still_running; |
202 | php_curlm *mh; |
203 | int still_running; |
204 | int result; |
205 | |
206 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz" , &z_mh, &z_still_running) == FAILURE) { |
207 | return; |
208 | } |
209 | |
210 | ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle); |
211 | |
212 | { |
213 | zend_llist_position pos; |
214 | php_curl *ch; |
215 | zval *pz_ch; |
216 | |
217 | for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; |
218 | pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) { |
219 | |
220 | ZEND_FETCH_RESOURCE(ch, php_curl *, &pz_ch, -1, le_curl_name, le_curl); |
221 | _php_curl_verify_handlers(ch, 1 TSRMLS_CC); |
222 | } |
223 | } |
224 | |
225 | convert_to_long_ex(&z_still_running); |
226 | still_running = Z_LVAL_P(z_still_running); |
227 | result = curl_multi_perform(mh->multi, &still_running); |
228 | ZVAL_LONG(z_still_running, still_running); |
229 | |
230 | RETURN_LONG(result); |
231 | } |
232 | /* }}} */ |
233 | |
234 | /* {{{ proto string curl_multi_getcontent(resource ch) |
235 | Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */ |
236 | PHP_FUNCTION(curl_multi_getcontent) |
237 | { |
238 | zval *z_ch; |
239 | php_curl *ch; |
240 | |
241 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r" , &z_ch) == FAILURE) { |
242 | return; |
243 | } |
244 | |
245 | ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl); |
246 | |
247 | if (ch->handlers->write->method == PHP_CURL_RETURN) { |
248 | if (ch->handlers->write->buf.len == 0) { |
249 | RETURN_EMPTY_STRING(); |
250 | } |
251 | smart_str_0(&ch->handlers->write->buf); |
252 | RETURN_STRINGL(ch->handlers->write->buf.c, ch->handlers->write->buf.len, 1); |
253 | } |
254 | |
255 | RETURN_NULL(); |
256 | } |
257 | /* }}} */ |
258 | |
259 | /* {{{ proto array curl_multi_info_read(resource mh [, long msgs_in_queue]) |
260 | Get information about the current transfers */ |
261 | PHP_FUNCTION(curl_multi_info_read) |
262 | { |
263 | zval *z_mh; |
264 | php_curlm *mh; |
265 | CURLMsg *tmp_msg; |
266 | int queued_msgs; |
267 | zval *zmsgs_in_queue = NULL; |
268 | |
269 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z" , &z_mh, &zmsgs_in_queue) == FAILURE) { |
270 | return; |
271 | } |
272 | |
273 | ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle); |
274 | |
275 | tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs); |
276 | if (tmp_msg == NULL) { |
277 | RETURN_FALSE; |
278 | } |
279 | if (zmsgs_in_queue) { |
280 | zval_dtor(zmsgs_in_queue); |
281 | ZVAL_LONG(zmsgs_in_queue, queued_msgs); |
282 | } |
283 | |
284 | array_init(return_value); |
285 | add_assoc_long(return_value, "msg" , tmp_msg->msg); |
286 | add_assoc_long(return_value, "result" , tmp_msg->data.result); |
287 | |
288 | /* find the original easy curl handle */ |
289 | { |
290 | zend_llist_position pos; |
291 | php_curl *ch; |
292 | zval *pz_ch; |
293 | |
294 | /* search the list of easy handles hanging off the multi-handle */ |
295 | for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; |
296 | pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) { |
297 | ZEND_FETCH_RESOURCE(ch, php_curl *, &pz_ch, -1, le_curl_name, le_curl); |
298 | if (ch->cp == tmp_msg->easy_handle) { |
299 | |
300 | /* we are adding a reference to the underlying php_curl |
301 | resource, so we need to add one to the resource's refcount |
302 | in order to ensure it doesn't get destroyed when the |
303 | underlying curl easy handle goes out of scope. |
304 | Normally you would call zval_copy_ctor( pz_ch ), or |
305 | SEPARATE_ZVAL, but those create new zvals, which is already |
306 | being done in add_assoc_resource */ |
307 | |
308 | zend_list_addref( Z_RESVAL_P( pz_ch ) ); |
309 | |
310 | /* add_assoc_resource automatically creates a new zval to |
311 | wrap the "resource" represented by the current pz_ch */ |
312 | |
313 | add_assoc_resource(return_value, "handle" , Z_RESVAL_P(pz_ch)); |
314 | |
315 | break; |
316 | } |
317 | } |
318 | } |
319 | } |
320 | /* }}} */ |
321 | |
322 | /* {{{ proto void curl_multi_close(resource mh) |
323 | Close a set of cURL handles */ |
324 | PHP_FUNCTION(curl_multi_close) |
325 | { |
326 | zval *z_mh; |
327 | php_curlm *mh; |
328 | |
329 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r" , &z_mh) == FAILURE) { |
330 | return; |
331 | } |
332 | |
333 | ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle); |
334 | |
335 | zend_list_delete(Z_LVAL_P(z_mh)); |
336 | } |
337 | /* }}} */ |
338 | |
339 | void _php_curl_multi_close(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ |
340 | { |
341 | php_curlm *mh = (php_curlm *) rsrc->ptr; |
342 | if (mh) { |
343 | zend_llist_position pos; |
344 | php_curl *ch; |
345 | zval *pz_ch; |
346 | |
347 | for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; |
348 | pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) { |
349 | |
350 | ch = (php_curl *) zend_fetch_resource(&pz_ch TSRMLS_CC, -1, le_curl_name, NULL, 1, le_curl); |
351 | _php_curl_verify_handlers(ch, 0 TSRMLS_CC); |
352 | } |
353 | |
354 | curl_multi_cleanup(mh->multi); |
355 | zend_llist_clean(&mh->easyh); |
356 | efree(mh); |
357 | rsrc->ptr = NULL; |
358 | } |
359 | } |
360 | /* }}} */ |
361 | |
362 | #if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */ |
363 | /* {{{ proto bool curl_multi_strerror(int code) |
364 | return string describing error code */ |
365 | PHP_FUNCTION(curl_multi_strerror) |
366 | { |
367 | long code; |
368 | const char *str; |
369 | |
370 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l" , &code) == FAILURE) { |
371 | return; |
372 | } |
373 | |
374 | str = curl_multi_strerror(code); |
375 | if (str) { |
376 | RETURN_STRING(str, 1); |
377 | } else { |
378 | RETURN_NULL(); |
379 | } |
380 | } |
381 | /* }}} */ |
382 | #endif |
383 | |
384 | #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ |
385 | static int _php_curl_multi_setopt(php_curlm *mh, long option, zval **zvalue, zval *return_value TSRMLS_DC) /* {{{ */ |
386 | { |
387 | CURLMcode error = CURLM_OK; |
388 | |
389 | switch (option) { |
390 | #if LIBCURL_VERSION_NUM >= 0x071000 /* 7.16.0 */ |
391 | case CURLMOPT_PIPELINING: |
392 | #endif |
393 | #if LIBCURL_VERSION_NUM >= 0x071003 /* 7.16.3 */ |
394 | case CURLMOPT_MAXCONNECTS: |
395 | #endif |
396 | convert_to_long_ex(zvalue); |
397 | error = curl_multi_setopt(mh->multi, option, Z_LVAL_PP(zvalue)); |
398 | break; |
399 | |
400 | default: |
401 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid curl multi configuration option" ); |
402 | error = CURLM_UNKNOWN_OPTION; |
403 | break; |
404 | } |
405 | |
406 | if (error != CURLM_OK) { |
407 | return 1; |
408 | } else { |
409 | return 0; |
410 | } |
411 | } |
412 | /* }}} */ |
413 | |
414 | /* {{{ proto int curl_multi_setopt(resource mh, int option, mixed value) |
415 | Set an option for the curl multi handle */ |
416 | PHP_FUNCTION(curl_multi_setopt) |
417 | { |
418 | zval *z_mh, **zvalue; |
419 | long options; |
420 | php_curlm *mh; |
421 | |
422 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlZ" , &z_mh, &options, &zvalue) == FAILURE) { |
423 | return; |
424 | } |
425 | |
426 | ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle); |
427 | |
428 | if (!_php_curl_multi_setopt(mh, options, zvalue, return_value TSRMLS_CC)) { |
429 | RETURN_TRUE; |
430 | } else { |
431 | RETURN_FALSE; |
432 | } |
433 | } |
434 | /* }}} */ |
435 | #endif |
436 | |
437 | #endif |
438 | |
439 | /* |
440 | * Local variables: |
441 | * tab-width: 4 |
442 | * c-basic-offset: 4 |
443 | * End: |
444 | * vim600: noet sw=4 ts=4 fdm=marker |
445 | * vim<600: noet sw=4 ts=4 |
446 | */ |
447 | |