+ if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CERTINFO, &ci)) {
+ MAKE_STD_ZVAL(ci_array);
+ array_init(ci_array);
+
+ for (i = 0; i < ci->num_of_certs; ++i) {
+ s = ci->certinfo[i];
+
+ MAKE_STD_ZVAL(subarray);
+ array_init(subarray);
+ for (p = s; p; p = p->next) {
+ if (p->data) {
+ if ((colon = strchr(p->data, ':'))) {
+ keyname = estrndup(p->data, colon - p->data);
+ add_assoc_string_ex(subarray, keyname, colon - p->data + 1, colon + 1, 1);
+ efree(keyname);
+ } else {
+ add_next_index_string(subarray, p->data, 1);
+ }
+ }
+ }
+ add_next_index_zval(ci_array, subarray);
+ }
+ add_assoc_zval_ex(&array, "certinfo", sizeof("certinfo"), ci_array);
+ }
+ }
+#endif
+ {
+ php_http_curle_storage_t *st = php_http_curle_get_storage(ch);
+
+ add_assoc_long_ex(&array, "curlcode", sizeof("curlcode"), st->errorcode);
+ add_assoc_string_ex(&array, "error", sizeof("error"), st->errorbuffer, 1);
+ }
+
+ return SUCCESS;
+}
+
+static int compare_queue(php_http_client_enqueue_t *e, void *handle)
+{
+ return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle;
+}
+
+static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h TSRMLS_DC)
+{
+ php_http_message_t *response;
+ php_http_header_parser_t parser;
+ zval *zh;
+
+ response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC);
+ php_http_header_parser_init(&parser TSRMLS_CC);
+ while (h->response.headers.used) {
+ php_http_header_parser_state_t st = php_http_header_parser_parse(&parser,
+ &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs,
+ (php_http_info_callback_t) php_http_message_info_callback, (void *) &response);
+ if (PHP_HTTP_HEADER_PARSER_STATE_FAILURE == st) {
+ break;
+ }
+ }
+ php_http_header_parser_dtor(&parser);
+
+ /* move body to right message */
+ if (response->body != h->response.body) {
+ php_http_message_t *ptr = response;
+
+ while (ptr->parent) {
+ ptr = ptr->parent;
+ }
+ php_http_message_body_free(&response->body);
+ 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"), 1))) {
+ zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL);
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding"), 0))) {
+ zend_hash_update(&response->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &zh, sizeof(zval *), NULL);
+ zend_hash_del(&response->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range"), 0))) {
+ zend_hash_update(&response->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &zh, sizeof(zval *), NULL);
+ zend_hash_del(&response->hdrs, "Content-Range", sizeof("Content-Range"));
+ }
+ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding"), 0))) {
+ zend_hash_update(&response->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &zh, sizeof(zval *), NULL);
+ zend_hash_del(&response->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
+ }
+ php_http_message_update_headers(response);
+
+ return response;
+}
+
+static void php_http_curlm_responsehandler(php_http_client_t *context)
+{
+ int err_count = 0, remaining = 0;
+ php_http_curle_storage_t *st, *err = NULL;
+ php_http_client_enqueue_t *enqueue;
+ php_http_client_curl_t *curl = context->ctx;
+ TSRMLS_FETCH_FROM_CTX(context->ts);
+
+ do {
+ CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining);
+
+ if (msg && CURLMSG_DONE == msg->msg) {
+ if (CURLE_OK != msg->data.result) {
+ st = php_http_curle_get_storage(msg->easy_handle);
+ st->errorcode = msg->data.result;
+
+ /* defer the warnings/exceptions, so the callback is still called for this request */
+ if (!err) {
+ err = ecalloc(remaining + 1, sizeof(*err));
+ }
+ memcpy(&err[err_count], st, sizeof(*st));
+ if (st->url) {
+ err[err_count].url = estrdup(st->url);
+ }
+ err_count++;
+ }
+
+ 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 (err_count) {
+ int i = 0;
+
+ do {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(err[i].errorcode), err[i].errorbuffer, STR_PTR(err[i].url));
+ if (err[i].url) {
+ efree(err[i].url);
+ }
+ } while (++i < err_count);
+
+ efree(err);
+ }
+}
+
+#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;
+ TSRMLS_FETCH_FROM_CTX(context->ts);
+
+ /* 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 TSRMLS_CC, 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;
+ TSRMLS_FETCH_FROM_CTX(context->ts);
+
+ 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 TSRMLS_CC, 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;
+ TSRMLS_FETCH_FROM_CTX(context->ts);
+
+ 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 TSRMLS_CC, 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_BVAL_P(val) ? 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_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;
+ TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ if (curl->options.encode_cookies) {
+ if (SUCCESS == php_http_url_encode_hash_ex(HASH_OF(val), &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0 TSRMLS_CC)) {
+ 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 {
+ HashPosition pos;
+ php_http_array_hashkey_t cookie_key = php_http_array_hashkey_init(0);
+ zval **cookie_val;
+
+ FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) {
+ zval *zv = php_http_ztyp(IS_STRING, *cookie_val);
+
+ php_http_array_hashkey_stringify(&cookie_key);
+ php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.str, Z_STRVAL_P(zv));
+ php_http_array_hashkey_stringfree(&cookie_key);
+
+ zval_ptr_dtor(&zv);
+ }
+
+ 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_BVAL_P(val);
+ 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;
+ TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+ 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(TSRMLS_C) + 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_BVAL_P(val) ? "" : 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 (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;
+ TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+ php_http_buffer_reset(&curl->options.ranges);
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ HashPosition pos;
+ zval **rr, **rb, **re;
+
+ FOREACH_VAL(pos, val, rr) {
+ if (Z_TYPE_PP(rr) == IS_ARRAY) {
+ if (2 == php_http_array_list(Z_ARRVAL_PP(rr) TSRMLS_CC, 2, &rb, &re)) {
+ if ( ((Z_TYPE_PP(rb) == IS_LONG) || ((Z_TYPE_PP(rb) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(rb), Z_STRLEN_PP(rb), NULL, NULL, 1))) &&
+ ((Z_TYPE_PP(re) == IS_LONG) || ((Z_TYPE_PP(re) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(re), Z_STRLEN_PP(re), NULL, NULL, 1)))) {
+ zval *rbl = php_http_ztyp(IS_LONG, *rb);
+ zval *rel = php_http_ztyp(IS_LONG, *re);
+
+ if ((Z_LVAL_P(rbl) >= 0) && (Z_LVAL_P(rel) >= 0)) {
+ php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", Z_LVAL_P(rbl), Z_LVAL_P(rel));
+ }
+ zval_ptr_dtor(&rbl);
+ zval_ptr_dtor(&rel);
+ }
+
+ }
+ }
+ }
+
+ 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;
+ TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ zval **z_port_start, *zps_copy = NULL, **z_port_end, *zpe_copy = NULL;
+
+ switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) {
+ case 2:
+ zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
+ zpe_copy = php_http_ztyp(IS_LONG, *z_port_end);
+ localportrange = labs(Z_LVAL_P(zps_copy)-Z_LVAL_P(zpe_copy))+1L;
+ /* no break */
+ case 1:
+ if (!zps_copy) {
+ zps_copy = php_http_ztyp(IS_LONG, *z_port_start);
+ }
+ localport = (zpe_copy && Z_LVAL_P(zpe_copy) > 0) ? MIN(Z_LVAL_P(zps_copy), Z_LVAL_P(zpe_copy)) : Z_LVAL_P(zps_copy);
+ zval_ptr_dtor(&zps_copy);
+ if (zpe_copy) {
+ zval_ptr_dtor(&zpe_copy);
+ }
+ 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;
+ TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0);
+ zval **header_val, *header_cpy;
+ HashPosition pos;
+ php_http_buffer_t header;
+
+ php_http_buffer_init(&header);
+ FOREACH_KEYVAL(pos, val, header_key, header_val) {
+ if (header_key.type == HASH_KEY_IS_STRING) {
+ header_cpy = php_http_ztyp(IS_STRING, *header_val);
+ php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy));
+ php_http_buffer_fix(&header);
+ curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data);
+ php_http_buffer_reset(&header);
+
+ zval_ptr_dtor(&header_cpy);
+ }
+ }
+ 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;
+ TSRMLS_FETCH_FROM_CTX(curl->client->ts);
+
+ if (val && Z_TYPE_P(val) != IS_NULL) {
+ php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
+ HashPosition pos;
+ zval **data;
+
+ FOREACH_KEYVAL(pos, val, key, data) {
+ zval *cpy = php_http_ztyp(IS_STRING, *data);
+ curl->options.resolve = curl_slist_append(curl->options.resolve, Z_STRVAL_P(cpy));
+ zval_ptr_dtor(&cpy);
+ }
+
+ 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;
+ }
+ }
+ return SUCCESS;
+}
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP)
+static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata)
+{
+ php_http_client_curl_handler_t *curl = userdata;
+ CURL *ch = curl->handle;
+
+ if (val && Z_LVAL_P(val)) {
+ switch (Z_LVAL_P(val)) {
+ case CURL_TLSAUTH_SRP:
+ if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) {
+ return SUCCESS;
+ }
+ /* no break */
+ default:
+ return FAILURE;
+ }
+ }
+ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+#endif
+
+static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC)
+{
+ php_http_option_t *opt;
+
+ /* url options */
+#if PHP_HTTP_CURL_VERSION(7,42,0)
+ php_http_option_register(registry, ZEND_STRL("path_as_is"), CURLOPT_PATH_AS_IS, IS_BOOL);
+#endif
+
+ /* proxy */
+ if ((opt = php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+ php_http_option_register(registry, ZEND_STRL("proxytype"), CURLOPT_PROXYTYPE, IS_LONG);
+ php_http_option_register(registry, ZEND_STRL("proxyport"), CURLOPT_PROXYPORT, IS_LONG);
+ if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauth"), CURLOPT_PROXYUSERPWD, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+ if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauthtype"), CURLOPT_PROXYAUTH, IS_LONG))) {
+ Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE;
+ }
+ php_http_option_register(registry, ZEND_STRL("proxytunnel"), CURLOPT_HTTPPROXYTUNNEL, IS_BOOL);
+#if PHP_HTTP_CURL_VERSION(7,19,4)
+ php_http_option_register(registry, ZEND_STRL("noproxy"), CURLOPT_NOPROXY, IS_STRING);
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,37,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("proxyheader"), CURLOPT_PROXYHEADER, IS_ARRAY))) {
+ opt->setter = php_http_curle_option_set_proxyheader;
+ }
+#endif
+#if PHP_HTTP_CURL_VERSION(7,43,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("proxy_service_name"), CURLOPT_PROXY_SERVICE_NAME, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+#endif
+
+#if PHP_HTTP_CURL_VERSION(7,40,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("unix_socket_path"), CURLOPT_UNIX_SOCKET_PATH, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR;
+ }
+#endif
+
+ /* dns */
+ if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) {
+ Z_LVAL(opt->defval) = 60;
+ }
+ php_http_option_register(registry, ZEND_STRL("ipresolve"), CURLOPT_IPRESOLVE, IS_LONG);
+#if PHP_HTTP_CURL_VERSION(7,21,3)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("resolve"), CURLOPT_RESOLVE, IS_ARRAY))) {
+ opt->setter = php_http_curle_option_set_resolve;
+ }
+#endif
+#if PHP_HTTP_HAVE_ARES
+# if PHP_HTTP_CURL_VERSION(7,24,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("dns_servers"), CURLOPT_DNS_SERVERS, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+# endif
+# if PHP_HTTP_CURL_VERSION(7,33,0)
+ if ((opt = php_http_option_register(registry, ZEND_STRL("dns_interface"), CURLOPT_DNS_INTERFACE, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+ if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip4"), CURLOPT_DNS_LOCAL_IP4, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+ if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip6"), CURLOPT_DNS_LOCAL_IP6, IS_STRING))) {
+ opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN;
+ }
+# endif
+#endif
+
+ /* limits */
+ php_http_option_register(registry, ZEND_STRL("low_speed_limit"), CURLOPT_LOW_SPEED_LIMIT, IS_LONG);
+ php_http_option_register(registry, ZEND_STRL("low_speed_time"), CURLOPT_LOW_SPEED_TIME, IS_LONG);