1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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 */
PHP_FUNCTION(curl_multi_init)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 RETURN_RES(zend_register_resource(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 */
PHP_FUNCTION(curl_multi_add_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(), "rr", &z_mh, &z_ch) == FAILURE) {
82 return;
83 }
84
85 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
86 RETURN_FALSE;
87 }
88
89 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
90 RETURN_FALSE;
91 }
92
93 _php_curl_cleanup_handle(ch);
94
95 /* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */
96 ZVAL_DUP(&tmp_val, z_ch);
97
98 zend_llist_add_element(&mh->easyh, &tmp_val);
99
100 RETURN_LONG((zend_long)curl_multi_add_handle(mh->multi, ch->cp));
101 }
102 /* }}} */
103
_php_curl_multi_cleanup_list(void * data)104 void _php_curl_multi_cleanup_list(void *data) /* {{{ */
105 {
106 zval *z_ch = (zval *)data;
107 php_curl *ch;
108
109 if (!z_ch) {
110 return;
111 }
112 if (!Z_RES_P(z_ch)->ptr) {
113 return;
114 }
115 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
116 return;
117 }
118
119 zend_list_delete(Z_RES_P(z_ch));
120 }
121 /* }}} */
122
123 /* Used internally as comparison routine passed to zend_list_del_element */
curl_compare_resources(zval * z1,zval * z2)124 static int curl_compare_resources( zval *z1, zval *z2 ) /* {{{ */
125 {
126 return (Z_TYPE_P(z1) == Z_TYPE_P(z2) &&
127 Z_TYPE_P(z1) == IS_RESOURCE &&
128 Z_RES_P(z1) == Z_RES_P(z2));
129 }
130 /* }}} */
131
132 /* {{{ proto int curl_multi_remove_handle(resource mh, resource ch)
133 Remove a multi handle from a set of cURL handles */
PHP_FUNCTION(curl_multi_remove_handle)134 PHP_FUNCTION(curl_multi_remove_handle)
135 {
136 zval *z_mh;
137 zval *z_ch;
138 php_curlm *mh;
139 php_curl *ch;
140
141 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &z_mh, &z_ch) == FAILURE) {
142 return;
143 }
144
145 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
146 RETURN_FALSE;
147 }
148
149 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
150 RETURN_FALSE;
151 }
152
153 RETVAL_LONG((zend_long)curl_multi_remove_handle(mh->multi, ch->cp));
154 zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *))curl_compare_resources);
155
156 }
157 /* }}} */
158
_make_timeval_struct(struct timeval * to,double timeout)159 static void _make_timeval_struct(struct timeval *to, double timeout) /* {{{ */
160 {
161 unsigned long conv;
162
163 conv = (unsigned long) (timeout * 1000000.0);
164 to->tv_sec = conv / 1000000;
165 to->tv_usec = conv % 1000000;
166 }
167 /* }}} */
168
169 /* {{{ proto int curl_multi_select(resource mh[, double timeout])
170 Get all the sockets associated with the cURL extension, which can then be "selected" */
PHP_FUNCTION(curl_multi_select)171 PHP_FUNCTION(curl_multi_select)
172 {
173 zval *z_mh;
174 php_curlm *mh;
175 fd_set readfds;
176 fd_set writefds;
177 fd_set exceptfds;
178 int maxfd;
179 double timeout = 1.0;
180 struct timeval to;
181
182 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|d", &z_mh, &timeout) == FAILURE) {
183 return;
184 }
185
186 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
187 RETURN_FALSE;
188 }
189
190 _make_timeval_struct(&to, timeout);
191
192 FD_ZERO(&readfds);
193 FD_ZERO(&writefds);
194 FD_ZERO(&exceptfds);
195
196 curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd);
197 if (maxfd == -1) {
198 RETURN_LONG(-1);
199 }
200 RETURN_LONG(select(maxfd + 1, &readfds, &writefds, &exceptfds, &to));
201 }
202 /* }}} */
203
204 /* {{{ proto int curl_multi_exec(resource mh, int &still_running)
205 Run the sub-connections of the current cURL handle */
PHP_FUNCTION(curl_multi_exec)206 PHP_FUNCTION(curl_multi_exec)
207 {
208 zval *z_mh;
209 zval *z_still_running;
210 php_curlm *mh;
211 int still_running;
212 int result;
213
214 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz/", &z_mh, &z_still_running) == FAILURE) {
215 return;
216 }
217
218 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
219 RETURN_FALSE;
220 }
221
222 {
223 zend_llist_position pos;
224 php_curl *ch;
225 zval *pz_ch;
226
227 for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
228 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
229
230 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl)) == NULL) {
231 RETURN_FALSE;
232 }
233
234 _php_curl_verify_handlers(ch, 1);
235 }
236 }
237
238 convert_to_long(z_still_running);
239 still_running = Z_LVAL_P(z_still_running);
240 result = curl_multi_perform(mh->multi, &still_running);
241 ZVAL_LONG(z_still_running, still_running);
242
243 RETURN_LONG(result);
244 }
245 /* }}} */
246
247 /* {{{ proto string curl_multi_getcontent(resource ch)
248 Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */
PHP_FUNCTION(curl_multi_getcontent)249 PHP_FUNCTION(curl_multi_getcontent)
250 {
251 zval *z_ch;
252 php_curl *ch;
253
254 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_ch) == FAILURE) {
255 return;
256 }
257
258 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
259 RETURN_FALSE;
260 }
261
262 if (ch->handlers->write->method == PHP_CURL_RETURN) {
263 if (!ch->handlers->write->buf.s) {
264 RETURN_EMPTY_STRING();
265 }
266 smart_str_0(&ch->handlers->write->buf);
267 RETURN_STR_COPY(ch->handlers->write->buf.s);
268 }
269
270 RETURN_NULL();
271 }
272 /* }}} */
273
274 /* {{{ proto array curl_multi_info_read(resource mh [, long msgs_in_queue])
275 Get information about the current transfers */
PHP_FUNCTION(curl_multi_info_read)276 PHP_FUNCTION(curl_multi_info_read)
277 {
278 zval *z_mh;
279 php_curlm *mh;
280 CURLMsg *tmp_msg;
281 int queued_msgs;
282 zval *zmsgs_in_queue = NULL;
283
284 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z/", &z_mh, &zmsgs_in_queue) == FAILURE) {
285 return;
286 }
287
288 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
289 RETURN_FALSE;
290 }
291
292 tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs);
293 if (tmp_msg == NULL) {
294 RETURN_FALSE;
295 }
296 if (zmsgs_in_queue) {
297 zval_dtor(zmsgs_in_queue);
298 ZVAL_LONG(zmsgs_in_queue, queued_msgs);
299 }
300
301 array_init(return_value);
302 add_assoc_long(return_value, "msg", tmp_msg->msg);
303 add_assoc_long(return_value, "result", tmp_msg->data.result);
304
305 /* find the original easy curl handle */
306 {
307 zend_llist_position pos;
308 php_curl *ch;
309 zval *pz_ch;
310
311 /* search the list of easy handles hanging off the multi-handle */
312 for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
313 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
314
315 if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl)) == NULL) {
316 RETURN_FALSE;
317 }
318 if (ch->cp == tmp_msg->easy_handle) {
319
320 /* we are adding a reference to the underlying php_curl
321 resource, so we need to add one to the resource's refcount
322 in order to ensure it doesn't get destroyed when the
323 underlying curl easy handle goes out of scope.
324 Normally you would call zval_copy_ctor( pz_ch ), or
325 SEPARATE_ZVAL, but those create new zvals, which is already
326 being done in add_assoc_resource */
327 Z_ADDREF_P(pz_ch);
328
329 /* add_assoc_resource automatically creates a new zval to
330 wrap the "resource" represented by the current pz_ch */
331
332 add_assoc_zval(return_value, "handle", pz_ch);
333
334 break;
335 }
336 }
337 }
338 }
339 /* }}} */
340
341 /* {{{ proto void curl_multi_close(resource mh)
342 Close a set of cURL handles */
PHP_FUNCTION(curl_multi_close)343 PHP_FUNCTION(curl_multi_close)
344 {
345 zval *z_mh;
346 php_curlm *mh;
347
348 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_mh) == FAILURE) {
349 return;
350 }
351
352 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
353 RETURN_FALSE;
354 }
355
356 zend_list_close(Z_RES_P(z_mh));
357 }
358 /* }}} */
359
_php_curl_multi_close(zend_resource * rsrc)360 void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */
361 {
362 php_curlm *mh = (php_curlm *)rsrc->ptr;
363 if (mh) {
364 zend_llist_position pos;
365 php_curl *ch;
366 zval *pz_ch;
367
368 for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
369 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
370 /* ptr is NULL means it already be freed */
371 if (Z_RES_P(pz_ch)->ptr) {
372 if ((ch = (php_curl *) zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl))) {
373 _php_curl_verify_handlers(ch, 0);
374 }
375 }
376 }
377
378 curl_multi_cleanup(mh->multi);
379 zend_llist_clean(&mh->easyh);
380 efree(mh);
381 rsrc->ptr = NULL;
382 }
383 }
384 /* }}} */
385
386 #if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */
387 /* {{{ proto bool curl_multi_strerror(int code)
388 return string describing error code */
PHP_FUNCTION(curl_multi_strerror)389 PHP_FUNCTION(curl_multi_strerror)
390 {
391 zend_long code;
392 const char *str;
393
394 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &code) == FAILURE) {
395 return;
396 }
397
398 str = curl_multi_strerror(code);
399 if (str) {
400 RETURN_STRING(str);
401 } else {
402 RETURN_NULL();
403 }
404 }
405 /* }}} */
406 #endif
407
408 #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
_php_curl_multi_setopt(php_curlm * mh,zend_long option,zval * zvalue,zval * return_value)409 static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */
410 {
411 CURLMcode error = CURLM_OK;
412
413 switch (option) {
414 #if LIBCURL_VERSION_NUM >= 0x071000 /* 7.16.0 */
415 case CURLMOPT_PIPELINING:
416 #endif
417 #if LIBCURL_VERSION_NUM >= 0x071003 /* 7.16.3 */
418 case CURLMOPT_MAXCONNECTS:
419 #endif
420 #if LIBCURL_VERSION_NUM >= 0x071e00 /* 7.30.0 */
421 case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
422 case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
423 case CURLMOPT_MAX_HOST_CONNECTIONS:
424 case CURLMOPT_MAX_PIPELINE_LENGTH:
425 case CURLMOPT_MAX_TOTAL_CONNECTIONS:
426 #endif
427 error = curl_multi_setopt(mh->multi, option, zval_get_long(zvalue));
428 break;
429
430 default:
431 php_error_docref(NULL, E_WARNING, "Invalid curl multi configuration option");
432 error = CURLM_UNKNOWN_OPTION;
433 break;
434 }
435
436 if (error != CURLM_OK) {
437 return 1;
438 } else {
439 return 0;
440 }
441 }
442 /* }}} */
443
444 /* {{{ proto int curl_multi_setopt(resource mh, int option, mixed value)
445 Set an option for the curl multi handle */
PHP_FUNCTION(curl_multi_setopt)446 PHP_FUNCTION(curl_multi_setopt)
447 {
448 zval *z_mh, *zvalue;
449 zend_long options;
450 php_curlm *mh;
451
452 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &z_mh, &options, &zvalue) == FAILURE) {
453 return;
454 }
455
456 if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
457 RETURN_FALSE;
458 }
459
460 if (!_php_curl_multi_setopt(mh, options, zvalue, return_value)) {
461 RETURN_TRUE;
462 } else {
463 RETURN_FALSE;
464 }
465 }
466 /* }}} */
467 #endif
468
469 #endif
470
471 /*
472 * Local variables:
473 * tab-width: 4
474 * c-basic-offset: 4
475 * End:
476 * vim600: noet sw=4 ts=4 fdm=marker
477 * vim<600: noet sw=4 ts=4
478 */
479