1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | 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 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 */
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() 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 ch->uses++;
90
91 /* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */
92 tmp_val = *z_ch;
93 zval_copy_ctor(&tmp_val);
94
95 zend_llist_add_element(&mh->easyh, &tmp_val);
96
97 RETURN_LONG((long) curl_multi_add_handle(mh->multi, ch->cp));
98 }
99 /* }}} */
100
_php_curl_multi_cleanup_list(void * data)101 void _php_curl_multi_cleanup_list(void *data) /* {{{ */
102 {
103 zval *z_ch = (zval *)data;
104 php_curl *ch;
105 TSRMLS_FETCH();
106
107 if (!z_ch) {
108 return;
109 }
110
111 ch = (php_curl *) zend_fetch_resource(&z_ch TSRMLS_CC, -1, le_curl_name, NULL, 1, le_curl);
112 if (!ch) {
113 return;
114 }
115
116 if (ch->uses) {
117 ch->uses--;
118 } else {
119 zend_list_delete(Z_LVAL_P(z_ch));
120 }
121 }
122 /* }}} */
123
124 /* Used internally as comparison routine passed to zend_list_del_element */
curl_compare_resources(zval * z1,zval ** z2)125 static int curl_compare_resources( zval *z1, zval **z2 ) /* {{{ */
126 {
127 return (Z_TYPE_P( z1 ) == Z_TYPE_PP( z2 ) &&
128 Z_TYPE_P( z1 ) == IS_RESOURCE &&
129 Z_LVAL_P( z1 ) == Z_LVAL_PP( z2 ) );
130 }
131 /* }}} */
132
133 /* {{{ proto int curl_multi_remove_handle(resource mh, resource ch)
134 Remove a multi handle from a set of cURL handles */
PHP_FUNCTION(curl_multi_remove_handle)135 PHP_FUNCTION(curl_multi_remove_handle)
136 {
137 zval *z_mh;
138 zval *z_ch;
139 php_curlm *mh;
140 php_curl *ch;
141
142 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &z_mh, &z_ch) == FAILURE) {
143 return;
144 }
145
146 ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
147 ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl);
148
149 --ch->uses;
150
151 zend_llist_del_element( &mh->easyh, &z_ch,
152 (int (*)(void *, void *)) curl_compare_resources );
153
154 RETURN_LONG((long) curl_multi_remove_handle(mh->multi, ch->cp));
155 }
156 /* }}} */
157
_make_timeval_struct(struct timeval * to,double timeout)158 static void _make_timeval_struct(struct timeval *to, double timeout) /* {{{ */
159 {
160 unsigned long conv;
161
162 conv = (unsigned long) (timeout * 1000000.0);
163 to->tv_sec = conv / 1000000;
164 to->tv_usec = conv % 1000000;
165 }
166 /* }}} */
167
168 /* {{{ proto int curl_multi_select(resource mh[, double timeout])
169 Get all the sockets associated with the cURL extension, which can then be "selected" */
PHP_FUNCTION(curl_multi_select)170 PHP_FUNCTION(curl_multi_select)
171 {
172 zval *z_mh;
173 php_curlm *mh;
174 fd_set readfds;
175 fd_set writefds;
176 fd_set exceptfds;
177 int maxfd;
178 double timeout = 1.0;
179 struct timeval to;
180
181 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|d", &z_mh, &timeout) == FAILURE) {
182 return;
183 }
184
185 ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
186
187 _make_timeval_struct(&to, timeout);
188
189 FD_ZERO(&readfds);
190 FD_ZERO(&writefds);
191 FD_ZERO(&exceptfds);
192
193 curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd);
194 if (maxfd == -1) {
195 RETURN_LONG(-1);
196 }
197 RETURN_LONG(select(maxfd + 1, &readfds, &writefds, &exceptfds, &to));
198 }
199 /* }}} */
200
201 /* {{{ proto int curl_multi_exec(resource mh, int &still_running)
202 Run the sub-connections of the current cURL handle */
PHP_FUNCTION(curl_multi_exec)203 PHP_FUNCTION(curl_multi_exec)
204 {
205 zval *z_mh;
206 zval *z_still_running;
207 php_curlm *mh;
208 int still_running;
209 int result;
210
211 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &z_mh, &z_still_running) == FAILURE) {
212 return;
213 }
214
215 ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
216
217 {
218 zend_llist_position pos;
219 php_curl *ch;
220 zval *pz_ch;
221
222 for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
223 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
224
225 ZEND_FETCH_RESOURCE(ch, php_curl *, &pz_ch, -1, le_curl_name, le_curl);
226 _php_curl_verify_handlers(ch, 1 TSRMLS_CC);
227 }
228 }
229
230 convert_to_long_ex(&z_still_running);
231 still_running = Z_LVAL_P(z_still_running);
232 result = curl_multi_perform(mh->multi, &still_running);
233 ZVAL_LONG(z_still_running, still_running);
234
235 RETURN_LONG(result);
236 }
237 /* }}} */
238
239 /* {{{ proto string curl_multi_getcontent(resource ch)
240 Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */
PHP_FUNCTION(curl_multi_getcontent)241 PHP_FUNCTION(curl_multi_getcontent)
242 {
243 zval *z_ch;
244 php_curl *ch;
245
246 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_ch) == FAILURE) {
247 return;
248 }
249
250 ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl);
251
252 if (ch->handlers->write->method == PHP_CURL_RETURN && ch->handlers->write->buf.len > 0) {
253 smart_str_0(&ch->handlers->write->buf);
254 RETURN_STRINGL(ch->handlers->write->buf.c, ch->handlers->write->buf.len, 1);
255 }
256 }
257 /* }}} */
258
259 /* {{{ proto array curl_multi_info_read(resource mh [, long msgs_in_queue])
260 Get information about the current transfers */
PHP_FUNCTION(curl_multi_info_read)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 */
PHP_FUNCTION(curl_multi_close)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
_php_curl_multi_close(zend_rsrc_list_entry * rsrc TSRMLS_DC)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 #endif
363
364 /*
365 * Local variables:
366 * tab-width: 4
367 * c-basic-offset: 4
368 * End:
369 * vim600: noet sw=4 ts=4 fdm=marker
370 * vim<600: noet sw=4 ts=4
371 */
372