1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 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 RETURN_EMPTY_STRING();
258 }
259 /* }}} */
260
261 /* {{{ proto array curl_multi_info_read(resource mh [, long msgs_in_queue])
262 Get information about the current transfers */
PHP_FUNCTION(curl_multi_info_read)263 PHP_FUNCTION(curl_multi_info_read)
264 {
265 zval *z_mh;
266 php_curlm *mh;
267 CURLMsg *tmp_msg;
268 int queued_msgs;
269 zval *zmsgs_in_queue = NULL;
270
271 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z", &z_mh, &zmsgs_in_queue) == FAILURE) {
272 return;
273 }
274
275 ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
276
277 tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs);
278 if (tmp_msg == NULL) {
279 RETURN_FALSE;
280 }
281 if (zmsgs_in_queue) {
282 zval_dtor(zmsgs_in_queue);
283 ZVAL_LONG(zmsgs_in_queue, queued_msgs);
284 }
285
286 array_init(return_value);
287 add_assoc_long(return_value, "msg", tmp_msg->msg);
288 add_assoc_long(return_value, "result", tmp_msg->data.result);
289
290 /* find the original easy curl handle */
291 {
292 zend_llist_position pos;
293 php_curl *ch;
294 zval *pz_ch;
295
296 /* search the list of easy handles hanging off the multi-handle */
297 for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
298 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
299 ZEND_FETCH_RESOURCE(ch, php_curl *, &pz_ch, -1, le_curl_name, le_curl);
300 if (ch->cp == tmp_msg->easy_handle) {
301
302 /* we are adding a reference to the underlying php_curl
303 resource, so we need to add one to the resource's refcount
304 in order to ensure it doesn't get destroyed when the
305 underlying curl easy handle goes out of scope.
306 Normally you would call zval_copy_ctor( pz_ch ), or
307 SEPARATE_ZVAL, but those create new zvals, which is already
308 being done in add_assoc_resource */
309
310 zend_list_addref( Z_RESVAL_P( pz_ch ) );
311
312 /* add_assoc_resource automatically creates a new zval to
313 wrap the "resource" represented by the current pz_ch */
314
315 add_assoc_resource(return_value, "handle", Z_RESVAL_P(pz_ch));
316
317 break;
318 }
319 }
320 }
321 }
322 /* }}} */
323
324 /* {{{ proto void curl_multi_close(resource mh)
325 Close a set of cURL handles */
PHP_FUNCTION(curl_multi_close)326 PHP_FUNCTION(curl_multi_close)
327 {
328 zval *z_mh;
329 php_curlm *mh;
330
331 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_mh) == FAILURE) {
332 return;
333 }
334
335 ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
336
337 zend_list_delete(Z_LVAL_P(z_mh));
338 }
339 /* }}} */
340
_php_curl_multi_close(zend_rsrc_list_entry * rsrc TSRMLS_DC)341 void _php_curl_multi_close(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
342 {
343 php_curlm *mh = (php_curlm *) rsrc->ptr;
344 if (mh) {
345 zend_llist_position pos;
346 php_curl *ch;
347 zval *pz_ch;
348
349 for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
350 pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
351
352 ch = (php_curl *) zend_fetch_resource(&pz_ch TSRMLS_CC, -1, le_curl_name, NULL, 1, le_curl);
353 _php_curl_verify_handlers(ch, 0 TSRMLS_CC);
354 }
355
356 curl_multi_cleanup(mh->multi);
357 zend_llist_clean(&mh->easyh);
358 efree(mh);
359 rsrc->ptr = NULL;
360 }
361 }
362 /* }}} */
363
364 #endif
365
366 /*
367 * Local variables:
368 * tab-width: 4
369 * c-basic-offset: 4
370 * End:
371 * vim600: noet sw=4 ts=4 fdm=marker
372 * vim<600: noet sw=4 ts=4
373 */
374