+ while (ptr->parent) {
+ ptr = ptr->parent;
+ }
+ response->body = ptr->body;
+ ptr->body = NULL;
+ }
+ php_http_message_body_addref(h->response.body);
+
+ /* let's update the response headers */
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length")))) {
+ Z_TRY_ADDREF_P(zh);
+ zend_hash_str_update(&response->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), zh);
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding")))) {
+ Z_TRY_ADDREF_P(zh);
+ zend_hash_str_update(&response->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), zh);
+ zend_hash_str_del(&response->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding"));
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range")))) {
+ Z_TRY_ADDREF_P(zh);
+ zend_hash_str_update(&response->hdrs, "X-Original-Content-Range", lenof("X-Original-Content-Range"), zh);
+ zend_hash_str_del(&response->hdrs, "Content-Range", lenof("Content-Range"));
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding")))) {
+ Z_TRY_ADDREF_P(zh);
+ zend_hash_str_update(&response->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), zh);
+ zend_hash_str_del(&response->hdrs, "Content-Encoding", lenof("Content-Encoding"));
+ }
+ php_http_message_update_headers(response);
+
+ return response;
+}
+
+static void php_http_curlm_responsehandler(php_http_client_t *context)
+{
+ int remaining = 0;
+ php_http_client_enqueue_t *enqueue;
+ php_http_client_curl_t *curl = context->ctx;
+
+ do {
+ CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining);
+
+ if (msg && CURLMSG_DONE == msg->msg) {
+ if (CURLE_OK != msg->data.result) {
+ php_http_curle_storage_t *st = php_http_curle_get_storage(msg->easy_handle);
+ php_error_docref(NULL, E_WARNING, "%s; %s (%s)", curl_easy_strerror(st->errorcode = msg->data.result), STR_PTR(st->errorbuffer), STR_PTR(st->url));
+ }
+
+ if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) {
+ php_http_client_curl_handler_t *handler = enqueue->opaque;
+ php_http_message_t *response = php_http_curlm_responseparser(handler TSRMLS_CC);
+
+ if (response) {
+ context->callback.response.func(context->callback.response.arg, context, &handler->queue, &response);
+ php_http_message_free(&response);
+ }
+ }
+ }
+ } while (remaining);
+}
+
+#if PHP_HTTP_HAVE_EVENT
+
+typedef struct php_http_curlm_event {
+ struct event evnt;
+ php_http_client_t *context;
+} php_http_curlm_event_t;
+
+static inline int etoca(short action) {
+ switch (action & (EV_READ|EV_WRITE)) {
+ case EV_READ:
+ return CURL_CSELECT_IN;
+ break;
+ case EV_WRITE:
+ return CURL_CSELECT_OUT;
+ break;
+ case EV_READ|EV_WRITE:
+ return CURL_CSELECT_IN|CURL_CSELECT_OUT;
+ break;
+ default:
+ return 0;
+ }
+}
+
+static void php_http_curlm_timeout_callback(int socket, short action, void *event_data)
+{
+ php_http_client_t *context = event_data;
+ php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+ fprintf(stderr, "T");
+#endif
+ if (curl->useevents) {
+ CURLMcode rc;
+
+ /* ignore and use -1,0 on timeout */
+ (void) socket;
+ (void) action;
+
+ while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished)));
+
+ if (CURLM_OK != rc) {
+ php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc));
+ }
+
+ php_http_curlm_responsehandler(context);
+ }
+}
+
+static void php_http_curlm_event_callback(int socket, short action, void *event_data)
+{
+ php_http_client_t *context = event_data;
+ php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+ fprintf(stderr, "E");
+#endif
+ if (curl->useevents) {
+ CURLMcode rc = CURLM_OK;
+
+ while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, socket, etoca(action), &curl->unfinished)));
+
+ if (CURLM_OK != rc) {
+ php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc));
+ }
+
+ php_http_curlm_responsehandler(context);
+
+ /* remove timeout if there are no transfers left */
+ if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
+ event_del(curl->timeout);
+ }
+ }
+}
+
+static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
+{
+ php_http_client_t *context = socket_data;
+ php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+ fprintf(stderr, "S");
+#endif
+ if (curl->useevents) {
+ int events = EV_PERSIST;
+ php_http_curlm_event_t *ev = assign_data;
+
+ if (!ev) {
+ ev = ecalloc(1, sizeof(php_http_curlm_event_t));
+ ev->context = context;
+ curl_multi_assign(curl->handle, sock, ev);
+ } else {
+ event_del(&ev->evnt);
+ }
+
+ switch (action) {
+ case CURL_POLL_IN:
+ events |= EV_READ;
+ break;
+ case CURL_POLL_OUT:
+ events |= EV_WRITE;
+ break;
+ case CURL_POLL_INOUT:
+ events |= EV_READ|EV_WRITE;
+ break;
+
+ case CURL_POLL_REMOVE:
+ efree(ev);
+ /* no break */
+ case CURL_POLL_NONE:
+ return 0;
+
+ default:
+ php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action);
+ return -1;
+ }
+
+ event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context);
+ event_add(&ev->evnt, NULL);
+ }
+
+ return 0;
+}
+
+static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data)
+{
+ php_http_client_t *context = timer_data;
+ php_http_client_curl_t *curl = context->ctx;
+
+#if DBG_EVENTS
+ fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
+#endif
+ if (curl->useevents) {
+
+ if (timeout_ms < 0) {
+ php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context);
+ } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) {
+ struct timeval timeout;
+
+ if (!event_initialized(curl->timeout)) {
+ event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context);
+ }
+
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+ event_add(curl->timeout, &timeout);
+ }
+ }
+}
+
+#endif /* HAVE_EVENT */
+
+/* curl options */
+
+static php_http_options_t php_http_curle_options, php_http_curlm_options;
+
+#define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN 0x0001
+#define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR 0x0002
+#define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS 0x0004
+
+static ZEND_RESULT_CODE php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, Z_TYPE_P(val) == IS_TRUE ? 2 : 0)) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+ php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle);
+
+ if (storage->cookiestore) {
+ pefree(storage->cookiestore, 1);
+ }
+ if (val && Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val)) {
+ storage->cookiestore = pestrndup(Z_STRVAL_P(val), Z_STRLEN_P(val), 1);
+ } else {
+ storage->cookiestore = NULL;
+ }
+ if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEFILE, storage->cookiestore)
+ || CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEJAR, storage->cookiestore)
+ ) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ HashTable *ht = HASH_OF(val);
+
+ if (curl->options.encode_cookies) {
+ if (SUCCESS == php_http_url_encode_hash_ex(ht, &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0)) {
+ php_http_buffer_fix(&curl->options.cookies);
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
+ return FAILURE;
+ }
+ } else {
+ return FAILURE;
+ }
+ } else {
+ php_http_arrkey_t cookie_key;
+ zval *cookie_val;
+
+ ZEND_HASH_FOREACH_KEY_VAL(ht, cookie_key.h, cookie_key.key, cookie_val)
+ {
+ zend_string *zs = zval_get_string(cookie_val);
+
+ php_http_arrkey_stringify(&cookie_key, NULL);
+ php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.key->val, zs->val);
+ php_http_arrkey_dtor(&cookie_key);
+
+ zend_string_release(zs);
+ }
+ ZEND_HASH_FOREACH_END();
+
+ php_http_buffer_fix(&curl->options.cookies);
+ if (curl->options.cookies.used) {
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) {
+ return FAILURE;
+ }
+ }
+ }
+ } else {
+ php_http_buffer_reset(&curl->options.cookies);
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, NULL)) {
+ return FAILURE;
+ }
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+
+ curl->options.encode_cookies = Z_TYPE_P(val) == IS_TRUE;
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if (Z_LVAL_P(val)) {
+ if (Z_LVAL_P(val) > 0) {
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, Z_LVAL_P(val))) {
+ return FAILURE;
+ }
+ } else {
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) sapi_get_request_time() + Z_LVAL_P(val))) {
+ return FAILURE;
+ }
+ }
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, (long) (curl->options.range_request ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE))) {
+ return FAILURE;
+ }
+ } else {
+ if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, 0)
+ || CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, 0)
+ ) {
+ return FAILURE;
+ }
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+#if !PHP_HTTP_CURL_VERSION(7,21,6)
+# define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING
+#endif
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_TYPE_P(val) == IS_TRUE ? "" : NULL)) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ php_http_buffer_t header;
+
+ if (val && Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val)) {
+ zend_bool is_quoted = !((Z_STRVAL_P(val)[0] != '"') || (Z_STRVAL_P(val)[Z_STRLEN_P(val)-1] != '"'));
+ php_http_buffer_init(&header);
+ php_http_buffer_appendf(&header, is_quoted?"%s: %s":"%s: \"%s\"", curl->options.range_request?"If-Match":"If-None-Match", Z_STRVAL_P(val));
+ php_http_buffer_fix(&header);
+ curl->options.headers = curl_slist_append(curl->options.headers, header.data);
+ php_http_buffer_dtor(&header);
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ php_http_buffer_reset(&curl->options.ranges);
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ zval *rr, *rb, *re;
+ zend_long rbl, rel;
+ HashTable *ht = HASH_OF(val);
+
+ ZEND_HASH_FOREACH_VAL(ht, rr)
+ {
+ if (Z_TYPE_P(rr) == IS_ARRAY) {
+ if (2 == php_http_array_list(Z_ARRVAL_P(rr), 2, &rb, &re)) {
+ if ( ((Z_TYPE_P(rb) == IS_LONG) || ((Z_TYPE_P(rb) == IS_STRING) && is_numeric_string(Z_STRVAL_P(rb), Z_STRLEN_P(rb), &rbl, NULL, 1))) &&
+ ((Z_TYPE_P(re) == IS_LONG) || ((Z_TYPE_P(re) == IS_STRING) && is_numeric_string(Z_STRVAL_P(re), Z_STRLEN_P(re), &rel, NULL, 1)))) {
+ if ((rbl >= 0) && (rel >= 0)) {
+ php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", rbl, rel);
+ }
+ }
+
+ }
+ }
+ }
+ ZEND_HASH_FOREACH_END();
+
+ if (curl->options.ranges.used) {
+ curl->options.range_request = 1;
+ /* ditch last comma */
+ curl->options.ranges.data[curl->options.ranges.used - 1] = '\0';
+ }
+ }
+
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RANGE, curl->options.ranges.data)) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if (Z_LVAL_P(val) > 0) {
+ curl->options.range_request = 1;
+ }
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESUME_FROM, Z_LVAL_P(val))) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+
+ curl->options.retry.delay = Z_DVAL_P(val);
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+
+ curl->options.retry.count = Z_LVAL_P(val);
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(val) ? 1L : 0L)
+ || CURLE_OK != curl_easy_setopt(ch, CURLOPT_MAXREDIRS, curl->options.redirects = Z_LVAL_P(val))
+ ) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+ long localport = 0, localportrange = 0;
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ zval *zps, *zpe;
+
+ switch (php_http_array_list(Z_ARRVAL_P(val), 2, &zps, &zpe)) {
+ case 2:
+ localportrange = labs(zval_get_long(zps)-zval_get_long(zpe))+1L;
+ /* no break */
+ case 1:
+ localport = (zval_get_long(zpe) > 0) ? MIN(zval_get_long(zps), zval_get_long(zpe)) : zval_get_long(zps);
+ break;
+ default:
+ break;
+ }
+ }
+ if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORT, localport)
+ || CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORTRANGE, localportrange)
+ ) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+#if PHP_HTTP_CURL_VERSION(7,37,0)
+static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ php_http_arrkey_t header_key;
+ zval *header_val;
+ php_http_buffer_t header;
+
+ php_http_buffer_init(&header);
+ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(val), header_key.h, header_key.key, header_val)
+ {
+ if (header_key.key) {
+ zend_string *zs = zval_get_string(header_val);
+
+ php_http_buffer_appendf(&header, "%s: %s", header_key.key->val, zs->val);
+ zend_string_release(zs);
+
+ php_http_buffer_fix(&header);
+ curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
+ php_http_buffer_reset(&header);
+
+ }
+ }
+ ZEND_HASH_FOREACH_END();
+ php_http_buffer_dtor(&header);
+ }
+ if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) {
+ return FAILURE;
+ }
+ if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) {
+ curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL);
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,21,3)
+static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ HashTable *ht = HASH_OF(val);
+ zval *data;
+
+ ZEND_HASH_FOREACH_VAL(ht, data)
+ {
+ zend_string *zs = zval_get_string(data);
+ curl->options.resolve = curl_slist_append(curl->options.resolve, zs->val);
+ zend_string_release(zs);
+ }
+ ZEND_HASH_FOREACH_END();
+
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, curl->options.resolve)) {
+ return FAILURE;
+ }
+ } else {
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, NULL)) {
+ return FAILURE;
+ }