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 ZEND_INCLUDE_FULL_WINDOWS_HEADERS
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 */
54PHP_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 */
73PHP_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
100void _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 */
120static 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 */
130PHP_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
153static 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" */
165PHP_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 */
198PHP_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 */
236PHP_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 */
261PHP_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 */
324PHP_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
339void _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 */
365PHP_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 */
385static 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 */
416PHP_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