xref: /PHP-7.1/ext/curl/multi.c (revision 3bb218a0)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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 #define SAVE_CURLM_ERROR(__handle, __err) (__handle)->err.no = (int) __err;
53 
54 /* {{{ proto resource curl_multi_init(void)
55    Returns a new cURL multi handle */
PHP_FUNCTION(curl_multi_init)56 PHP_FUNCTION(curl_multi_init)
57 {
58 	php_curlm *mh;
59 
60 	if (zend_parse_parameters_none() == FAILURE) {
61 		return;
62 	}
63 
64 	mh = ecalloc(1, sizeof(php_curlm));
65 	mh->multi = curl_multi_init();
66 	mh->handlers = ecalloc(1, sizeof(php_curlm_handlers));
67 
68 	zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0);
69 
70 	RETURN_RES(zend_register_resource(mh, le_curl_multi_handle));
71 }
72 /* }}} */
73 
74 /* {{{ proto int curl_multi_add_handle(resource mh, resource ch)
75    Add a normal cURL handle to a cURL multi handle */
PHP_FUNCTION(curl_multi_add_handle)76 PHP_FUNCTION(curl_multi_add_handle)
77 {
78 	zval      *z_mh;
79 	zval      *z_ch;
80 	php_curlm *mh;
81 	php_curl  *ch;
82 	zval tmp_val;
83 	CURLMcode error = CURLM_OK;
84 
85 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &z_mh, &z_ch) == FAILURE) {
86 		return;
87 	}
88 
89 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
90 		RETURN_FALSE;
91 	}
92 
93 	if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
94 		RETURN_FALSE;
95 	}
96 
97 	_php_curl_cleanup_handle(ch);
98 
99 	/* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */
100 	ZVAL_DUP(&tmp_val, z_ch);
101 
102 	zend_llist_add_element(&mh->easyh, &tmp_val);
103 
104 	error = curl_multi_add_handle(mh->multi, ch->cp);
105 	SAVE_CURLM_ERROR(mh, error);
106 
107 	RETURN_LONG((zend_long) error);
108 }
109 /* }}} */
110 
_php_curl_multi_cleanup_list(void * data)111 void _php_curl_multi_cleanup_list(void *data) /* {{{ */
112 {
113 	zval *z_ch = (zval *)data;
114 	php_curl *ch;
115 
116 	if (!z_ch) {
117 		return;
118 	}
119 	if (!Z_RES_P(z_ch)->ptr) {
120 		return;
121 	}
122 	if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
123 		return;
124 	}
125 
126 	zend_list_delete(Z_RES_P(z_ch));
127 }
128 /* }}} */
129 
130 /* Used internally as comparison routine passed to zend_list_del_element */
curl_compare_resources(zval * z1,zval * z2)131 static int curl_compare_resources( zval *z1, zval *z2 ) /* {{{ */
132 {
133 	return (Z_TYPE_P(z1) == Z_TYPE_P(z2) &&
134 			Z_TYPE_P(z1) == IS_RESOURCE &&
135 			Z_RES_P(z1) == Z_RES_P(z2));
136 }
137 /* }}} */
138 
139 /* Used to find the php_curl resource for a given curl easy handle */
_php_curl_multi_find_easy_handle(php_curlm * mh,CURL * easy)140 static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */
141 {
142 	php_curl 			*tmp_ch;
143 	zend_llist_position pos;
144 	zval				*pz_ch_temp;
145 
146 	for(pz_ch_temp = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp;
147 		pz_ch_temp = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
148 
149 		if ((tmp_ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch_temp), le_curl_name, le_curl)) == NULL) {
150 			return NULL;
151 		}
152 
153 		if (tmp_ch->cp == easy) {
154 			return pz_ch_temp;
155 		}
156 	}
157 
158 	return NULL;
159 }
160 /* }}} */
161 
162 /* {{{ proto int curl_multi_remove_handle(resource mh, resource ch)
163    Remove a multi handle from a set of cURL handles */
PHP_FUNCTION(curl_multi_remove_handle)164 PHP_FUNCTION(curl_multi_remove_handle)
165 {
166 	zval      *z_mh;
167 	zval      *z_ch;
168 	php_curlm *mh;
169 	php_curl  *ch;
170 	CURLMcode error = CURLM_OK;
171 
172 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &z_mh, &z_ch) == FAILURE) {
173 		return;
174 	}
175 
176 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
177 		RETURN_FALSE;
178 	}
179 
180 	if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
181 		RETURN_FALSE;
182 	}
183 
184 	error = curl_multi_remove_handle(mh->multi, ch->cp);
185 	SAVE_CURLM_ERROR(mh, error);
186 
187 	RETVAL_LONG((zend_long) error);
188 	zend_llist_del_element(&mh->easyh, z_ch, (int (*)(void *, void *))curl_compare_resources);
189 
190 }
191 /* }}} */
192 
193 #if LIBCURL_VERSION_NUM < 0x071c00
_make_timeval_struct(struct timeval * to,double timeout)194 static void _make_timeval_struct(struct timeval *to, double timeout) /* {{{ */
195 {
196 	unsigned long conv;
197 
198 	conv = (unsigned long) (timeout * 1000000.0);
199 	to->tv_sec = conv / 1000000;
200 	to->tv_usec = conv % 1000000;
201 }
202 /* }}} */
203 #endif
204 
205 /* {{{ proto int curl_multi_select(resource mh[, double timeout])
206    Get all the sockets associated with the cURL extension, which can then be "selected" */
PHP_FUNCTION(curl_multi_select)207 PHP_FUNCTION(curl_multi_select)
208 {
209 	zval           *z_mh;
210 	php_curlm      *mh;
211 	double          timeout = 1.0;
212 #if LIBCURL_VERSION_NUM >= 0x071c00 /* Available since 7.28.0 */
213 	int             numfds = 0;
214 #else
215 	fd_set          readfds;
216 	fd_set          writefds;
217 	fd_set          exceptfds;
218 	int             maxfd;
219 	struct timeval  to;
220 #endif
221 	CURLMcode error = CURLM_OK;
222 
223 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|d", &z_mh, &timeout) == FAILURE) {
224 		return;
225 	}
226 
227 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
228 		RETURN_FALSE;
229 	}
230 
231 #if LIBCURL_VERSION_NUM >= 0x071c00 /* Available since 7.28.0 */
232 	error = curl_multi_wait(mh->multi, NULL, 0, (unsigned long) timeout * 1000.0, &numfds);
233 	if (CURLM_OK != error) {
234 		SAVE_CURLM_ERROR(mh, error);
235 		RETURN_LONG(-1);
236 	}
237 
238 	RETURN_LONG(numfds);
239 #else
240 	_make_timeval_struct(&to, timeout);
241 
242 	FD_ZERO(&readfds);
243 	FD_ZERO(&writefds);
244 	FD_ZERO(&exceptfds);
245 
246 	error = curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd);
247 	SAVE_CURLM_ERROR(mh, error);
248 
249 	if (maxfd == -1) {
250 		RETURN_LONG(-1);
251 	}
252 	RETURN_LONG(select(maxfd + 1, &readfds, &writefds, &exceptfds, &to));
253 #endif
254 }
255 /* }}} */
256 
257 /* {{{ proto int curl_multi_exec(resource mh, int &still_running)
258    Run the sub-connections of the current cURL handle */
PHP_FUNCTION(curl_multi_exec)259 PHP_FUNCTION(curl_multi_exec)
260 {
261 	zval      *z_mh;
262 	zval      *z_still_running;
263 	php_curlm *mh;
264 	int        still_running;
265 	CURLMcode error = CURLM_OK;
266 
267 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz/", &z_mh, &z_still_running) == FAILURE) {
268 		return;
269 	}
270 
271 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
272 		RETURN_FALSE;
273 	}
274 
275 	{
276 		zend_llist_position pos;
277 		php_curl *ch;
278 		zval	*pz_ch;
279 
280 		for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
281 			pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
282 
283 			if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl)) == NULL) {
284 				RETURN_FALSE;
285 			}
286 
287 			_php_curl_verify_handlers(ch, 1);
288 		}
289 	}
290 
291 	convert_to_long(z_still_running);
292 	still_running = Z_LVAL_P(z_still_running);
293 	error = curl_multi_perform(mh->multi, &still_running);
294 	ZVAL_LONG(z_still_running, still_running);
295 
296 	SAVE_CURLM_ERROR(mh, error);
297 	RETURN_LONG((zend_long) error);
298 }
299 /* }}} */
300 
301 /* {{{ proto string curl_multi_getcontent(resource ch)
302    Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */
PHP_FUNCTION(curl_multi_getcontent)303 PHP_FUNCTION(curl_multi_getcontent)
304 {
305 	zval     *z_ch;
306 	php_curl *ch;
307 
308 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_ch) == FAILURE) {
309 		return;
310 	}
311 
312 	if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(z_ch), le_curl_name, le_curl)) == NULL) {
313 		RETURN_FALSE;
314 	}
315 
316 	if (ch->handlers->write->method == PHP_CURL_RETURN) {
317 		if (!ch->handlers->write->buf.s) {
318 			RETURN_EMPTY_STRING();
319 		}
320 		smart_str_0(&ch->handlers->write->buf);
321 		RETURN_STR_COPY(ch->handlers->write->buf.s);
322 	}
323 
324 	RETURN_NULL();
325 }
326 /* }}} */
327 
328 /* {{{ proto array curl_multi_info_read(resource mh [, long msgs_in_queue])
329    Get information about the current transfers */
PHP_FUNCTION(curl_multi_info_read)330 PHP_FUNCTION(curl_multi_info_read)
331 {
332 	zval      *z_mh;
333 	php_curlm *mh;
334 	CURLMsg	  *tmp_msg;
335 	int        queued_msgs;
336 	zval      *zmsgs_in_queue = NULL;
337 
338 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|z/", &z_mh, &zmsgs_in_queue) == FAILURE) {
339 		return;
340 	}
341 
342 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
343 		RETURN_FALSE;
344 	}
345 
346 	tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs);
347 	if (tmp_msg == NULL) {
348 		RETURN_FALSE;
349 	}
350 	if (zmsgs_in_queue) {
351 		zval_ptr_dtor(zmsgs_in_queue);
352 		ZVAL_LONG(zmsgs_in_queue, queued_msgs);
353 	}
354 
355 	array_init(return_value);
356 	add_assoc_long(return_value, "msg", tmp_msg->msg);
357 	add_assoc_long(return_value, "result", tmp_msg->data.result);
358 
359 	/* find the original easy curl handle */
360 	{
361 		zval	*pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle);
362 		if (pz_ch != NULL) {
363 			/* we are adding a reference to the underlying php_curl
364 			   resource, so we need to add one to the resource's refcount
365 			   in order to ensure it doesn't get destroyed when the
366 			   underlying curl easy handle goes out of scope.
367 			   Normally you would call zval_copy_ctor( pz_ch ), or
368 			   SEPARATE_ZVAL, but those create new zvals, which is already
369 			   being done in add_assoc_resource */
370 			Z_ADDREF_P(pz_ch);
371 
372 			/* add_assoc_resource automatically creates a new zval to
373 			   wrap the "resource" represented by the current pz_ch */
374 
375 			add_assoc_zval(return_value, "handle", pz_ch);
376 		}
377 	}
378 }
379 /* }}} */
380 
381 /* {{{ proto void curl_multi_close(resource mh)
382    Close a set of cURL handles */
PHP_FUNCTION(curl_multi_close)383 PHP_FUNCTION(curl_multi_close)
384 {
385 	zval      *z_mh;
386 	php_curlm *mh;
387 
388 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_mh) == FAILURE) {
389 		return;
390 	}
391 
392 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
393 		RETURN_FALSE;
394 	}
395 
396 	zend_list_close(Z_RES_P(z_mh));
397 }
398 /* }}} */
399 
_php_curl_multi_close(zend_resource * rsrc)400 void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */
401 {
402 	php_curlm *mh = (php_curlm *)rsrc->ptr;
403 	if (mh) {
404 		zend_llist_position pos;
405 		php_curl *ch;
406 		zval	*pz_ch;
407 
408 		for (pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
409 			pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
410 			/* ptr is NULL means it already be freed */
411 			if (Z_RES_P(pz_ch)->ptr) {
412 				if ((ch = (php_curl *) zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl))) {
413 					_php_curl_verify_handlers(ch, 0);
414 				}
415 			}
416 		}
417 
418 		curl_multi_cleanup(mh->multi);
419 		zend_llist_clean(&mh->easyh);
420 		if (mh->handlers->server_push) {
421 			efree(mh->handlers->server_push);
422 		}
423 		if (mh->handlers) {
424 			efree(mh->handlers);
425 		}
426 		efree(mh);
427 		rsrc->ptr = NULL;
428 	}
429 }
430 /* }}} */
431 
432 /* {{{ proto int curl_multi_errno(resource mh)
433          Return an integer containing the last multi curl error number */
PHP_FUNCTION(curl_multi_errno)434 PHP_FUNCTION(curl_multi_errno)
435 {
436 	zval        *z_mh;
437 	php_curlm   *mh;
438 
439 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &z_mh) == FAILURE) {
440 		return;
441 	}
442 
443 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
444 		RETURN_FALSE;
445 	}
446 
447 	RETURN_LONG(mh->err.no);
448 }
449 /* }}} */
450 
451 #if LIBCURL_VERSION_NUM >= 0x070c00 /* Available since 7.12.0 */
452 /* {{{ proto bool curl_multi_strerror(int code)
453          return string describing error code */
PHP_FUNCTION(curl_multi_strerror)454 PHP_FUNCTION(curl_multi_strerror)
455 {
456 	zend_long code;
457 	const char *str;
458 
459 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &code) == FAILURE) {
460 		return;
461 	}
462 
463 	str = curl_multi_strerror(code);
464 	if (str) {
465 		RETURN_STRING(str);
466 	} else {
467 		RETURN_NULL();
468 	}
469 }
470 /* }}} */
471 #endif
472 
473 #if LIBCURL_VERSION_NUM >= 0x072C00 /* Available since 7.44.0 */
474 
_php_server_push_callback(CURL * parent_ch,CURL * easy,size_t num_headers,struct curl_pushheaders * push_headers,void * userp)475 static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */
476 {
477 	php_curl 				*ch;
478 	php_curl 				*parent;
479 	php_curlm 				*mh 			= (php_curlm *)userp;
480 	size_t 					rval 			= CURL_PUSH_DENY;
481 	php_curlm_server_push 	*t 				= mh->handlers->server_push;
482 	zval					*pz_parent_ch 	= NULL;
483 	zval 					pz_ch;
484 	zval 					headers;
485 	zval 					retval;
486 	zend_resource 			*res;
487 	char 					*header;
488 	int  					error;
489 	zend_fcall_info 		fci 			= empty_fcall_info;
490 
491 	pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch);
492 	if (pz_parent_ch == NULL) {
493 		return rval;
494 	}
495 
496 	parent = (php_curl*)zend_fetch_resource(Z_RES_P(pz_parent_ch), le_curl_name, le_curl);
497 
498 	ch = alloc_curl_handle();
499 	ch->cp = easy;
500 	_php_setup_easy_copy_handlers(ch, parent);
501 
502 	Z_ADDREF_P(pz_parent_ch);
503 
504 	res = zend_register_resource(ch, le_curl);
505 	ZVAL_RES(&pz_ch, res);
506 
507 	size_t i;
508 	array_init(&headers);
509 	for(i=0; i<num_headers; i++) {
510 		header = curl_pushheader_bynum(push_headers, i);
511 		add_next_index_string(&headers, header);
512   	}
513 
514 	zend_fcall_info_init(&t->func_name, 0, &fci, &t->fci_cache, NULL, NULL);
515 
516 	zend_fcall_info_argn(
517 		&fci, 3,
518 		pz_parent_ch,
519 		&pz_ch,
520 		&headers
521 	);
522 
523 	fci.retval = &retval;
524 
525 	error = zend_call_function(&fci, &t->fci_cache);
526 	zend_fcall_info_args_clear(&fci, 1);
527 	zval_dtor(&headers);
528 
529 	if (error == FAILURE) {
530 		php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION");
531 	} else if (!Z_ISUNDEF(retval)) {
532 		if (CURL_PUSH_DENY != zval_get_long(&retval)) {
533 		    rval = CURL_PUSH_OK;
534 
535 			/* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */
536 			zval tmp_val;
537 			ZVAL_DUP(&tmp_val, &pz_ch);
538 			zend_llist_add_element(&mh->easyh, &tmp_val);
539 		} else {
540 			/* libcurl will free this easy handle, avoid double free */
541 			ch->cp = NULL;
542 		}
543 	}
544 
545 	return rval;
546 }
547 
548 #endif
549 
550 #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
_php_curl_multi_setopt(php_curlm * mh,zend_long option,zval * zvalue,zval * return_value)551 static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */
552 {
553 	CURLMcode error = CURLM_OK;
554 
555 	switch (option) {
556 #if LIBCURL_VERSION_NUM >= 0x071000 /* 7.16.0 */
557 		case CURLMOPT_PIPELINING:
558 #endif
559 #if LIBCURL_VERSION_NUM >= 0x071003 /* 7.16.3 */
560 		case CURLMOPT_MAXCONNECTS:
561 #endif
562 #if LIBCURL_VERSION_NUM >= 0x071e00 /* 7.30.0 */
563 		case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
564 		case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
565 		case CURLMOPT_MAX_HOST_CONNECTIONS:
566 		case CURLMOPT_MAX_PIPELINE_LENGTH:
567 		case CURLMOPT_MAX_TOTAL_CONNECTIONS:
568 #endif
569 			error = curl_multi_setopt(mh->multi, option, zval_get_long(zvalue));
570 			break;
571 #if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.46.0 */
572 		case CURLMOPT_PUSHFUNCTION:
573 			if (mh->handlers->server_push == NULL) {
574 				mh->handlers->server_push = ecalloc(1, sizeof(php_curlm_server_push));
575 			} else if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) {
576 				zval_ptr_dtor(&mh->handlers->server_push->func_name);
577 				mh->handlers->server_push->fci_cache = empty_fcall_info_cache;
578 			}
579 
580 			ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue);
581 			mh->handlers->server_push->method = PHP_CURL_USER;
582 			if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) {
583 				zval_ptr_dtor(&mh->handlers->server_push->func_name);
584 				mh->handlers->server_push->fci_cache = empty_fcall_info_cache;
585 
586 			}
587 			error = curl_multi_setopt(mh->multi, option, _php_server_push_callback);
588 			if (error != CURLM_OK) {
589 				return 0;
590 			}
591 			error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh);
592 			break;
593 #endif
594 		default:
595 			php_error_docref(NULL, E_WARNING, "Invalid curl multi configuration option");
596 			error = CURLM_UNKNOWN_OPTION;
597 			break;
598 	}
599 
600 	SAVE_CURLM_ERROR(mh, error);
601 	if (error != CURLM_OK) {
602 		return 1;
603 	} else {
604 		return 0;
605 	}
606 }
607 /* }}} */
608 
609 /* {{{ proto int curl_multi_setopt(resource mh, int option, mixed value)
610        Set an option for the curl multi handle */
PHP_FUNCTION(curl_multi_setopt)611 PHP_FUNCTION(curl_multi_setopt)
612 {
613 	zval       *z_mh, *zvalue;
614 	zend_long        options;
615 	php_curlm *mh;
616 
617 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &z_mh, &options, &zvalue) == FAILURE) {
618 		return;
619 	}
620 
621 	if ((mh = (php_curlm *)zend_fetch_resource(Z_RES_P(z_mh), le_curl_multi_handle_name, le_curl_multi_handle)) == NULL) {
622 		RETURN_FALSE;
623 	}
624 
625 	if (!_php_curl_multi_setopt(mh, options, zvalue, return_value)) {
626 		RETURN_TRUE;
627 	} else {
628 		RETURN_FALSE;
629 	}
630 }
631 /* }}} */
632 #endif
633 
634 #endif
635 
636 /*
637  * Local variables:
638  * tab-width: 4
639  * c-basic-offset: 4
640  * End:
641  * vim600: noet sw=4 ts=4 fdm=marker
642  * vim<600: noet sw=4 ts=4
643  */
644