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