xref: /PHP-5.3/ext/curl/multi.c (revision a2045ff3)
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