diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 6ea744d460a94..9449022119812 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1337,6 +1337,9 @@ PHP_MINIT_FUNCTION(curl) #if LIBCURL_VERSION_NUM >= 0x072e00 /* Available since 7.46.0 */ REGISTER_CURL_CONSTANT(CURLOPT_STREAM_WEIGHT); + REGISTER_CURL_CONSTANT(CURLMOPT_PUSHFUNCTION); + REGISTER_CURL_CONSTANT(CURL_PUSH_OK); + REGISTER_CURL_CONSTANT(CURL_PUSH_DENY); #endif #if LIBCURL_VERSION_NUM >= 0x072f00 /* Available since 7.47.0 */ @@ -1853,7 +1856,7 @@ PHP_FUNCTION(curl_version) /* {{{ alloc_curl_handle */ -static php_curl *alloc_curl_handle() +php_curl *alloc_curl_handle() { php_curl *ch = ecalloc(1, sizeof(php_curl)); ch->to_free = ecalloc(1, sizeof(struct _php_curl_free)); @@ -2032,6 +2035,79 @@ PHP_FUNCTION(curl_init) } /* }}} */ +void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source) +{ + if (!Z_ISUNDEF(source->handlers->write->stream)) { + Z_ADDREF(source->handlers->write->stream); + } + ch->handlers->write->stream = source->handlers->write->stream; + ch->handlers->write->method = source->handlers->write->method; + if (!Z_ISUNDEF(source->handlers->read->stream)) { + Z_ADDREF(source->handlers->read->stream); + } + ch->handlers->read->stream = source->handlers->read->stream; + ch->handlers->read->method = source->handlers->read->method; + ch->handlers->write_header->method = source->handlers->write_header->method; + if (!Z_ISUNDEF(source->handlers->write_header->stream)) { + Z_ADDREF(source->handlers->write_header->stream); + } + ch->handlers->write_header->stream = source->handlers->write_header->stream; + + ch->handlers->write->fp = source->handlers->write->fp; + ch->handlers->write_header->fp = source->handlers->write_header->fp; + ch->handlers->read->fp = source->handlers->read->fp; + ch->handlers->read->res = source->handlers->read->res; +#if CURLOPT_PASSWDDATA != 0 + if (!Z_ISUNDEF(source->handlers->passwd)) { + ZVAL_COPY(&ch->handlers->passwd, &source->handlers->passwd); + curl_easy_setopt(source->cp, CURLOPT_PASSWDDATA, (void *) ch); + } +#endif + if (!Z_ISUNDEF(source->handlers->write->func_name)) { + ZVAL_COPY(&ch->handlers->write->func_name, &source->handlers->write->func_name); + } + if (!Z_ISUNDEF(source->handlers->read->func_name)) { + ZVAL_COPY(&ch->handlers->read->func_name, &source->handlers->read->func_name); + } + if (!Z_ISUNDEF(source->handlers->write_header->func_name)) { + ZVAL_COPY(&ch->handlers->write_header->func_name, &source->handlers->write_header->func_name); + } + + curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); + curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); + curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); + + if (source->handlers->progress) { + ch->handlers->progress = ecalloc(1, sizeof(php_curl_progress)); + if (!Z_ISUNDEF(source->handlers->progress->func_name)) { + ZVAL_COPY(&ch->handlers->progress->func_name, &source->handlers->progress->func_name); + } + ch->handlers->progress->method = source->handlers->progress->method; + curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, (void *) ch); + } + +#if LIBCURL_VERSION_NUM >= 0x071500 + if (source->handlers->fnmatch) { + ch->handlers->fnmatch = ecalloc(1, sizeof(php_curl_fnmatch)); + if (!Z_ISUNDEF(source->handlers->fnmatch->func_name)) { + ZVAL_COPY(&ch->handlers->fnmatch->func_name, &source->handlers->fnmatch->func_name); + } + ch->handlers->fnmatch->method = source->handlers->fnmatch->method; + curl_easy_setopt(ch->cp, CURLOPT_FNMATCH_DATA, (void *) ch); + } +#endif + + efree(ch->to_free->slist); + efree(ch->to_free); + ch->to_free = source->to_free; + efree(ch->clone); + ch->clone = source->clone; + + /* Keep track of cloned copies to avoid invoking curl destructors for every clone */ + (*source->clone)++; +} + /* {{{ proto resource curl_copy_handle(resource ch) Copy a cURL handle along with all of it's preferences */ PHP_FUNCTION(curl_copy_handle) @@ -2055,79 +2131,11 @@ PHP_FUNCTION(curl_copy_handle) } dupch = alloc_curl_handle(); - dupch->cp = cp; - Z_ADDREF_P(zid); - if (!Z_ISUNDEF(ch->handlers->write->stream)) { - Z_ADDREF(ch->handlers->write->stream); - } - dupch->handlers->write->stream = ch->handlers->write->stream; - dupch->handlers->write->method = ch->handlers->write->method; - if (!Z_ISUNDEF(ch->handlers->read->stream)) { - Z_ADDREF(ch->handlers->read->stream); - } - dupch->handlers->read->stream = ch->handlers->read->stream; - dupch->handlers->read->method = ch->handlers->read->method; - dupch->handlers->write_header->method = ch->handlers->write_header->method; - if (!Z_ISUNDEF(ch->handlers->write_header->stream)) { - Z_ADDREF(ch->handlers->write_header->stream); - } - dupch->handlers->write_header->stream = ch->handlers->write_header->stream; - - dupch->handlers->write->fp = ch->handlers->write->fp; - dupch->handlers->write_header->fp = ch->handlers->write_header->fp; - dupch->handlers->read->fp = ch->handlers->read->fp; - dupch->handlers->read->res = ch->handlers->read->res; -#if CURLOPT_PASSWDDATA != 0 - if (!Z_ISUNDEF(ch->handlers->passwd)) { - ZVAL_COPY(&dupch->handlers->passwd, &ch->handlers->passwd); - curl_easy_setopt(ch->cp, CURLOPT_PASSWDDATA, (void *) dupch); - } -#endif - if (!Z_ISUNDEF(ch->handlers->write->func_name)) { - ZVAL_COPY(&dupch->handlers->write->func_name, &ch->handlers->write->func_name); - } - if (!Z_ISUNDEF(ch->handlers->read->func_name)) { - ZVAL_COPY(&dupch->handlers->read->func_name, &ch->handlers->read->func_name); - } - if (!Z_ISUNDEF(ch->handlers->write_header->func_name)) { - ZVAL_COPY(&dupch->handlers->write_header->func_name, &ch->handlers->write_header->func_name); - } - curl_easy_setopt(dupch->cp, CURLOPT_ERRORBUFFER, dupch->err.str); - curl_easy_setopt(dupch->cp, CURLOPT_FILE, (void *) dupch); - curl_easy_setopt(dupch->cp, CURLOPT_INFILE, (void *) dupch); - curl_easy_setopt(dupch->cp, CURLOPT_WRITEHEADER, (void *) dupch); + _php_setup_easy_copy_handlers(dupch, ch); - if (ch->handlers->progress) { - dupch->handlers->progress = ecalloc(1, sizeof(php_curl_progress)); - if (!Z_ISUNDEF(ch->handlers->progress->func_name)) { - ZVAL_COPY(&dupch->handlers->progress->func_name, &ch->handlers->progress->func_name); - } - dupch->handlers->progress->method = ch->handlers->progress->method; - curl_easy_setopt(dupch->cp, CURLOPT_PROGRESSDATA, (void *) dupch); - } - -/* Available since 7.21.0 */ -#if LIBCURL_VERSION_NUM >= 0x071500 - if (ch->handlers->fnmatch) { - dupch->handlers->fnmatch = ecalloc(1, sizeof(php_curl_fnmatch)); - if (!Z_ISUNDEF(ch->handlers->fnmatch->func_name)) { - ZVAL_COPY(&dupch->handlers->fnmatch->func_name, &ch->handlers->fnmatch->func_name); - } - dupch->handlers->fnmatch->method = ch->handlers->fnmatch->method; - curl_easy_setopt(dupch->cp, CURLOPT_FNMATCH_DATA, (void *) dupch); - } -#endif - - efree(dupch->to_free->slist); - efree(dupch->to_free); - dupch->to_free = ch->to_free; - efree(dupch->clone); - dupch->clone = ch->clone; - - /* Keep track of cloned copies to avoid invoking curl destructors for every clone */ - (*ch->clone)++; + Z_ADDREF_P(zid); ZVAL_RES(return_value, zend_register_resource(dupch, le_curl)); dupch->res = Z_RES_P(return_value); @@ -3394,10 +3402,12 @@ static void _php_curl_close_ex(php_curl *ch) * * Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2 */ - curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing); - curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing); + if (ch->cp != NULL) { + curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing); + curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing); - curl_easy_cleanup(ch->cp); + curl_easy_cleanup(ch->cp); + } /* cURL destructors should be invoked only by last curl handle */ if (--(*ch->clone) == 0) { diff --git a/ext/curl/multi.c b/ext/curl/multi.c index da99f8f140c3d..c802edf46ddb8 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -63,6 +63,7 @@ PHP_FUNCTION(curl_multi_init) mh = ecalloc(1, sizeof(php_curlm)); mh->multi = curl_multi_init(); + mh->handlers = ecalloc(1, sizeof(php_curlm_handlers)); zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0); @@ -135,6 +136,29 @@ static int curl_compare_resources( zval *z1, zval *z2 ) /* {{{ */ } /* }}} */ +/* Used to find the php_curl resource for a given curl easy handle */ +static zval *_php_curl_multi_find_easy_handle(php_curlm *mh, CURL *easy) /* {{{ */ +{ + php_curl *tmp_ch; + zend_llist_position pos; + zval *pz_ch_temp; + + for(pz_ch_temp = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch_temp; + pz_ch_temp = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) { + + if ((tmp_ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch_temp), le_curl_name, le_curl)) == NULL) { + return NULL; + } + + if (tmp_ch->cp == easy) { + return pz_ch_temp; + } + } + + return NULL; +} +/* }}} */ + /* {{{ proto int curl_multi_remove_handle(resource mh, resource ch) Remove a multi handle from a set of cURL handles */ PHP_FUNCTION(curl_multi_remove_handle) @@ -318,35 +342,21 @@ PHP_FUNCTION(curl_multi_info_read) /* find the original easy curl handle */ { - zend_llist_position pos; - php_curl *ch; - zval *pz_ch; - - /* search the list of easy handles hanging off the multi-handle */ - for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch; - pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) { - - if ((ch = (php_curl *)zend_fetch_resource(Z_RES_P(pz_ch), le_curl_name, le_curl)) == NULL) { - RETURN_FALSE; - } - if (ch->cp == tmp_msg->easy_handle) { - - /* we are adding a reference to the underlying php_curl - resource, so we need to add one to the resource's refcount - in order to ensure it doesn't get destroyed when the - underlying curl easy handle goes out of scope. - Normally you would call zval_copy_ctor( pz_ch ), or - SEPARATE_ZVAL, but those create new zvals, which is already - being done in add_assoc_resource */ - Z_ADDREF_P(pz_ch); - - /* add_assoc_resource automatically creates a new zval to - wrap the "resource" represented by the current pz_ch */ - - add_assoc_zval(return_value, "handle", pz_ch); - - break; - } + zval *pz_ch = _php_curl_multi_find_easy_handle(mh, tmp_msg->easy_handle); + if (pz_ch != NULL) { + /* we are adding a reference to the underlying php_curl + resource, so we need to add one to the resource's refcount + in order to ensure it doesn't get destroyed when the + underlying curl easy handle goes out of scope. + Normally you would call zval_copy_ctor( pz_ch ), or + SEPARATE_ZVAL, but those create new zvals, which is already + being done in add_assoc_resource */ + Z_ADDREF_P(pz_ch); + + /* add_assoc_resource automatically creates a new zval to + wrap the "resource" represented by the current pz_ch */ + + add_assoc_zval(return_value, "handle", pz_ch); } } } @@ -391,6 +401,12 @@ void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */ curl_multi_cleanup(mh->multi); zend_llist_clean(&mh->easyh); + if (mh->handlers->server_push) { + efree(mh->handlers->server_push); + } + if (mh->handlers) { + efree(mh->handlers); + } efree(mh); rsrc->ptr = NULL; } @@ -438,6 +454,83 @@ PHP_FUNCTION(curl_multi_strerror) /* }}} */ #endif +#if LIBCURL_VERSION_NUM >= 0x072C00 /* Available since 7.44.0 */ + +static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */ +{ + php_curl *ch; + php_curl *parent; + php_curlm *mh = (php_curlm *)userp; + size_t rval = CURL_PUSH_DENY; + php_curlm_server_push *t = mh->handlers->server_push; + zval *pz_parent_ch = NULL; + zval pz_ch; + zval headers; + zval retval; + zend_resource *res; + char *header; + int error; + zend_fcall_info fci = empty_fcall_info; + + pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch); + if (pz_parent_ch == NULL) { + return rval; + } + + parent = (php_curl*)zend_fetch_resource(Z_RES_P(pz_parent_ch), le_curl_name, le_curl); + + ch = alloc_curl_handle(); + ch->cp = easy; + _php_setup_easy_copy_handlers(ch, parent); + + Z_ADDREF_P(pz_parent_ch); + + res = zend_register_resource(ch, le_curl); + ZVAL_RES(&pz_ch, res); + + size_t i; + array_init(&headers); + for(i=0; ifunc_name, 0, &fci, &t->fci_cache, NULL, NULL); + + zend_fcall_info_argn( + &fci, 3, + pz_parent_ch, + &pz_ch, + &headers + ); + + fci.retval = &retval; + + error = zend_call_function(&fci, &t->fci_cache); + zend_fcall_info_args_clear(&fci, 1); + zval_dtor(&headers); + + if (error == FAILURE) { + php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION"); + } else if (!Z_ISUNDEF(retval)) { + if (CURL_PUSH_DENY != zval_get_long(&retval)) { + rval = CURL_PUSH_OK; + + /* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */ + zval tmp_val; + ZVAL_DUP(&tmp_val, &pz_ch); + zend_llist_add_element(&mh->easyh, &tmp_val); + } else { + /* libcurl will free this easy handle, avoid double free */ + ch->cp = NULL; + } + } + + return rval; +} + +#endif + #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */ { @@ -459,7 +552,29 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, #endif error = curl_multi_setopt(mh->multi, option, zval_get_long(zvalue)); break; +#if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.46.0 */ + case CURLMOPT_PUSHFUNCTION: + if (mh->handlers->server_push == NULL) { + mh->handlers->server_push = ecalloc(1, sizeof(php_curlm_server_push)); + } else if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) { + zval_ptr_dtor(&mh->handlers->server_push->func_name); + mh->handlers->server_push->fci_cache = empty_fcall_info_cache; + } + ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue); + mh->handlers->server_push->method = PHP_CURL_USER; + if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) { + zval_ptr_dtor(&mh->handlers->server_push->func_name); + mh->handlers->server_push->fci_cache = empty_fcall_info_cache; + + } + error = curl_multi_setopt(mh->multi, option, _php_server_push_callback); + if (error != CURLM_OK) { + return 0; + } + error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh); + break; +#endif default: php_error_docref(NULL, E_WARNING, "Invalid curl multi configuration option"); error = CURLM_UNKNOWN_OPTION; diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h index 02077aaf2702a..4d015f1525107 100644 --- a/ext/curl/php_curl.h +++ b/ext/curl/php_curl.h @@ -66,6 +66,8 @@ extern int le_curl_multi_handle; #define le_curl_multi_handle_name "cURL Multi Handle" extern int le_curl_share_handle; #define le_curl_share_handle_name "cURL Share Handle" +//extern int le_curl_pushheaders; +//#define le_curl_pushheaders "cURL Push Headers" PHP_MINIT_FUNCTION(curl); PHP_MSHUTDOWN_FUNCTION(curl); @@ -146,7 +148,7 @@ typedef struct { zval func_name; zend_fcall_info_cache fci_cache; int method; -} php_curl_progress, php_curl_fnmatch; +} php_curl_progress, php_curl_fnmatch, php_curlm_server_push; typedef struct { php_curl_write *write; @@ -190,10 +192,15 @@ typedef struct { #define CURLOPT_SAFE_UPLOAD -1 +typedef struct { + php_curlm_server_push *server_push; +} php_curlm_handlers; + typedef struct { int still_running; CURLM *multi; zend_llist easyh; + php_curlm_handlers *handlers; struct { int no; } err; @@ -206,9 +213,11 @@ typedef struct { } err; } php_curlsh; +php_curl *alloc_curl_handle(); void _php_curl_cleanup_handle(php_curl *); void _php_curl_multi_cleanup_list(void *data); void _php_curl_verify_handlers(php_curl *ch, int reporterror); +void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source); void curlfile_register_class(void); PHP_CURL_API extern zend_class_entry *curl_CURLFile_class;