X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/53f4633144ed68c8b8fb5861f992b5489894a940..784f914b1e4b1c62d6657e86397c2e83bcee4295:/wp-includes/class-http.php diff --git a/wp-includes/class-http.php b/wp-includes/class-http.php index 5c082970..838fbe81 100644 --- a/wp-includes/class-http.php +++ b/wp-includes/class-http.php @@ -1,11 +1,6 @@ value cookie pairs to WP_HTTP_Cookie instances. foreach ( $r['cookies'] as $name => $value ) { if ( ! is_object( $value ) ) - $r['cookies'][ $name ] = new WP_HTTP_Cookie( array( 'name' => $name, 'value' => $value ) ); + $r['cookies'][ $name ] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value ) ); } $cookies_header = ''; @@ -619,7 +689,7 @@ class WP_Http { * prevent plugins from working and core functionality, if you don't include api.wordpress.org. * * You block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL as true in your wp-config.php - * file and this will only allow localhost and your blog to make requests. The constant + * file and this will only allow localhost and your site to make requests. The constant * WP_ACCESSIBLE_HOSTS will allow additional hosts to go through for requests. The format of the * WP_ACCESSIBLE_HOSTS constant is a comma separated list of hostnames to allow, wildcard domains * are supported, eg *.wordpress.org will allow for all subdomains of wordpress.org to be contacted. @@ -682,47 +752,19 @@ class WP_Http { } /** - * A wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7 + * Used as a wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7. * - * PHP 5.4.7 expanded parse_url()'s ability to handle non-absolute url's, including - * schemeless and relative url's with :// in the path, this works around those - * limitations providing a standard output on PHP 5.2~5.4+. - * - * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated - * when URL parsing failed. - * - * @since 4.1.0 - * - * @static * @access protected + * @deprecated 4.4.0 Use wp_parse_url() + * @see wp_parse_url() * * @param string $url The URL to parse. * @return bool|array False on failure; Array of URL components on success; * See parse_url()'s return values. */ protected static function parse_url( $url ) { - $parts = @parse_url( $url ); - if ( ! $parts ) { - // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path - if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) { - // Since we know it's a relative path, prefix with a scheme/host placeholder and try again - if ( ! $parts = @parse_url( 'placeholder://placeholder' . $url ) ) { - return $parts; - } - // Remove the placeholder values - unset( $parts['scheme'], $parts['host'] ); - } else { - return $parts; - } - } - - // < PHP 5.4.7 compat, doesn't detect schemeless URL's host field - if ( '//' == substr( $url, 0, 2 ) && ! isset( $parts['host'] ) ) { - list( $parts['host'], $slashless_path ) = explode( '/', substr( $parts['path'], 2 ), 2 ); - $parts['path'] = "/{$slashless_path}"; - } - - return $parts; + _deprecated_function( __METHOD__, '4.4.0', 'wp_parse_url()' ); + return wp_parse_url( $url ); } /** @@ -743,11 +785,11 @@ class WP_Http { if ( empty( $url ) ) return $maybe_relative_path; - if ( ! $url_parts = WP_HTTP::parse_url( $url ) ) { + if ( ! $url_parts = wp_parse_url( $url ) ) { return $maybe_relative_path; } - if ( ! $relative_url_parts = WP_HTTP::parse_url( $maybe_relative_path ) ) { + if ( ! $relative_url_parts = wp_parse_url( $maybe_relative_path ) ) { return $maybe_relative_path; } @@ -831,7 +873,7 @@ class WP_Http { if ( is_array( $redirect_location ) ) $redirect_location = array_pop( $redirect_location ); - $redirect_location = WP_HTTP::make_absolute_url( $redirect_location, $url ); + $redirect_location = WP_Http::make_absolute_url( $redirect_location, $url ); // POST requests should not POST to a redirected location. if ( 'POST' == $args['method'] ) { @@ -858,7 +900,7 @@ class WP_Http { * This does not verify if the IP is a valid IP, only that it appears to be * an IP address. * - * @see http://home.deds.nl/~aeron/regex/ for IPv6 regex + * @link http://home.deds.nl/~aeron/regex/ for IPv6 regex * * @since 3.7.0 * @static @@ -877,1430 +919,3 @@ class WP_Http { } } - -/** - * HTTP request method uses PHP Streams to retrieve the url. - * - * @since 2.7.0 - * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). - */ -class WP_Http_Streams { - /** - * Send a HTTP request to a URI using PHP Streams. - * - * @see WP_Http::request For default options descriptions. - * - * @since 2.7.0 - * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). - * - * @access public - * @param string $url The request URL. - * @param string|array $args Optional. Override the defaults. - * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error - */ - public function request($url, $args = array()) { - $defaults = array( - 'method' => 'GET', 'timeout' => 5, - 'redirection' => 5, 'httpversion' => '1.0', - 'blocking' => true, - 'headers' => array(), 'body' => null, 'cookies' => array() - ); - - $r = wp_parse_args( $args, $defaults ); - - if ( isset( $r['headers']['User-Agent'] ) ) { - $r['user-agent'] = $r['headers']['User-Agent']; - unset( $r['headers']['User-Agent'] ); - } elseif ( isset( $r['headers']['user-agent'] ) ) { - $r['user-agent'] = $r['headers']['user-agent']; - unset( $r['headers']['user-agent'] ); - } - - // Construct Cookie: header if any cookies are set. - WP_Http::buildCookieHeader( $r ); - - $arrURL = parse_url($url); - - $connect_host = $arrURL['host']; - - $secure_transport = ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ); - if ( ! isset( $arrURL['port'] ) ) { - if ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) { - $arrURL['port'] = 443; - $secure_transport = true; - } else { - $arrURL['port'] = 80; - } - } - - // Always pass a Path, defaulting to the root in cases such as http://example.com - if ( ! isset( $arrURL['path'] ) ) { - $arrURL['path'] = '/'; - } - - if ( isset( $r['headers']['Host'] ) || isset( $r['headers']['host'] ) ) { - if ( isset( $r['headers']['Host'] ) ) - $arrURL['host'] = $r['headers']['Host']; - else - $arrURL['host'] = $r['headers']['host']; - unset( $r['headers']['Host'], $r['headers']['host'] ); - } - - /* - * Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect - * to ::1, which fails when the server is not set up for it. For compatibility, always - * connect to the IPv4 address. - */ - if ( 'localhost' == strtolower( $connect_host ) ) - $connect_host = '127.0.0.1'; - - $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host; - - $is_local = isset( $r['local'] ) && $r['local']; - $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify']; - if ( $is_local ) { - /** - * Filter whether SSL should be verified for local requests. - * - * @since 2.8.0 - * - * @param bool $ssl_verify Whether to verify the SSL connection. Default true. - */ - $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify ); - } elseif ( ! $is_local ) { - /** - * Filter whether SSL should be verified for non-local requests. - * - * @since 2.8.0 - * - * @param bool $ssl_verify Whether to verify the SSL connection. Default true. - */ - $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify ); - } - - $proxy = new WP_HTTP_Proxy(); - - $context = stream_context_create( array( - 'ssl' => array( - 'verify_peer' => $ssl_verify, - //'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate() - 'capture_peer_cert' => $ssl_verify, - 'SNI_enabled' => true, - 'cafile' => $r['sslcertificates'], - 'allow_self_signed' => ! $ssl_verify, - ) - ) ); - - $timeout = (int) floor( $r['timeout'] ); - $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000; - $connect_timeout = max( $timeout, 1 ); - - // Store error number. - $connection_error = null; - - // Store error string. - $connection_error_str = null; - - if ( !WP_DEBUG ) { - // In the event that the SSL connection fails, silence the many PHP Warnings. - if ( $secure_transport ) - $error_reporting = error_reporting(0); - - if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) - $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); - else - $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); - - if ( $secure_transport ) - error_reporting( $error_reporting ); - - } else { - if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) - $handle = stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); - else - $handle = stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); - } - - if ( false === $handle ) { - // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken. - if ( $secure_transport && 0 === $connection_error && '' === $connection_error_str ) - return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); - - return new WP_Error('http_request_failed', $connection_error . ': ' . $connection_error_str ); - } - - // Verify that the SSL certificate is valid for this request. - if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) { - if ( ! self::verify_ssl_certificate( $handle, $arrURL['host'] ) ) - return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); - } - - stream_set_timeout( $handle, $timeout, $utimeout ); - - if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) //Some proxies require full URL in this field. - $requestPath = $url; - else - $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' ); - - $strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n"; - - $include_port_in_host_header = ( - ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || - ( 'http' == $arrURL['scheme'] && 80 != $arrURL['port'] ) || - ( 'https' == $arrURL['scheme'] && 443 != $arrURL['port'] ) - ); - - if ( $include_port_in_host_header ) { - $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; - } else { - $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; - } - - if ( isset($r['user-agent']) ) - $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n"; - - if ( is_array($r['headers']) ) { - foreach ( (array) $r['headers'] as $header => $headerValue ) - $strHeaders .= $header . ': ' . $headerValue . "\r\n"; - } else { - $strHeaders .= $r['headers']; - } - - if ( $proxy->use_authentication() ) - $strHeaders .= $proxy->authentication_header() . "\r\n"; - - $strHeaders .= "\r\n"; - - if ( ! is_null($r['body']) ) - $strHeaders .= $r['body']; - - fwrite($handle, $strHeaders); - - if ( ! $r['blocking'] ) { - stream_set_blocking( $handle, 0 ); - fclose( $handle ); - return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); - } - - $strResponse = ''; - $bodyStarted = false; - $keep_reading = true; - $block_size = 4096; - if ( isset( $r['limit_response_size'] ) ) - $block_size = min( $block_size, $r['limit_response_size'] ); - - // If streaming to a file setup the file handle. - if ( $r['stream'] ) { - if ( ! WP_DEBUG ) - $stream_handle = @fopen( $r['filename'], 'w+' ); - else - $stream_handle = fopen( $r['filename'], 'w+' ); - if ( ! $stream_handle ) - return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) ); - - $bytes_written = 0; - while ( ! feof($handle) && $keep_reading ) { - $block = fread( $handle, $block_size ); - if ( ! $bodyStarted ) { - $strResponse .= $block; - if ( strpos( $strResponse, "\r\n\r\n" ) ) { - $process = WP_Http::processResponse( $strResponse ); - $bodyStarted = true; - $block = $process['body']; - unset( $strResponse ); - $process['body'] = ''; - } - } - - $this_block_size = strlen( $block ); - - if ( isset( $r['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $r['limit_response_size'] ) { - $this_block_size = ( $r['limit_response_size'] - $bytes_written ); - $block = substr( $block, 0, $this_block_size ); - } - - $bytes_written_to_file = fwrite( $stream_handle, $block ); - - if ( $bytes_written_to_file != $this_block_size ) { - fclose( $handle ); - fclose( $stream_handle ); - return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); - } - - $bytes_written += $bytes_written_to_file; - - $keep_reading = !isset( $r['limit_response_size'] ) || $bytes_written < $r['limit_response_size']; - } - - fclose( $stream_handle ); - - } else { - $header_length = 0; - while ( ! feof( $handle ) && $keep_reading ) { - $block = fread( $handle, $block_size ); - $strResponse .= $block; - if ( ! $bodyStarted && strpos( $strResponse, "\r\n\r\n" ) ) { - $header_length = strpos( $strResponse, "\r\n\r\n" ) + 4; - $bodyStarted = true; - } - $keep_reading = ( ! $bodyStarted || !isset( $r['limit_response_size'] ) || strlen( $strResponse ) < ( $header_length + $r['limit_response_size'] ) ); - } - - $process = WP_Http::processResponse( $strResponse ); - unset( $strResponse ); - - } - - fclose( $handle ); - - $arrHeaders = WP_Http::processHeaders( $process['headers'], $url ); - - $response = array( - 'headers' => $arrHeaders['headers'], - // Not yet processed. - 'body' => null, - 'response' => $arrHeaders['response'], - 'cookies' => $arrHeaders['cookies'], - 'filename' => $r['filename'] - ); - - // Handle redirects. - if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) ) - return $redirect_response; - - // If the body was chunk encoded, then decode it. - if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) && 'chunked' == $arrHeaders['headers']['transfer-encoding'] ) - $process['body'] = WP_Http::chunkTransferDecode($process['body']); - - if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders['headers']) ) - $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); - - if ( isset( $r['limit_response_size'] ) && strlen( $process['body'] ) > $r['limit_response_size'] ) - $process['body'] = substr( $process['body'], 0, $r['limit_response_size'] ); - - $response['body'] = $process['body']; - - return $response; - } - - /** - * Verifies the received SSL certificate against it's Common Names and subjectAltName fields - * - * PHP's SSL verifications only verify that it's a valid Certificate, it doesn't verify if - * the certificate is valid for the hostname which was requested. - * This function verifies the requested hostname against certificate's subjectAltName field, - * if that is empty, or contains no DNS entries, a fallback to the Common Name field is used. - * - * IP Address support is included if the request is being made to an IP address. - * - * @since 3.7.0 - * @static - * - * @param stream $stream The PHP Stream which the SSL request is being made over - * @param string $host The hostname being requested - * @return bool If the cerficiate presented in $stream is valid for $host - */ - public static function verify_ssl_certificate( $stream, $host ) { - $context_options = stream_context_get_options( $stream ); - - if ( empty( $context_options['ssl']['peer_certificate'] ) ) - return false; - - $cert = openssl_x509_parse( $context_options['ssl']['peer_certificate'] ); - if ( ! $cert ) - return false; - - /* - * If the request is being made to an IP address, we'll validate against IP fields - * in the cert (if they exist) - */ - $host_type = ( WP_HTTP::is_ip_address( $host ) ? 'ip' : 'dns' ); - - $certificate_hostnames = array(); - if ( ! empty( $cert['extensions']['subjectAltName'] ) ) { - $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] ); - foreach ( $match_against as $match ) { - list( $match_type, $match_host ) = explode( ':', $match ); - if ( $host_type == strtolower( trim( $match_type ) ) ) // IP: or DNS: - $certificate_hostnames[] = strtolower( trim( $match_host ) ); - } - } elseif ( !empty( $cert['subject']['CN'] ) ) { - // Only use the CN when the certificate includes no subjectAltName extension. - $certificate_hostnames[] = strtolower( $cert['subject']['CN'] ); - } - - // Exact hostname/IP matches. - if ( in_array( strtolower( $host ), $certificate_hostnames ) ) - return true; - - // IP's can't be wildcards, Stop processing. - if ( 'ip' == $host_type ) - return false; - - // Test to see if the domain is at least 2 deep for wildcard support. - if ( substr_count( $host, '.' ) < 2 ) - return false; - - // Wildcard subdomains certs (*.example.com) are valid for a.example.com but not a.b.example.com. - $wildcard_host = preg_replace( '/^[^.]+\./', '*.', $host ); - - return in_array( strtolower( $wildcard_host ), $certificate_hostnames ); - } - - /** - * Whether this class can be used for retrieving a URL. - * - * @static - * @access public - * @since 2.7.0 - * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). - * - * @return bool False means this class can not be used, true means it can. - */ - public static function test( $args = array() ) { - if ( ! function_exists( 'stream_socket_client' ) ) - return false; - - $is_ssl = isset( $args['ssl'] ) && $args['ssl']; - - if ( $is_ssl ) { - if ( ! extension_loaded( 'openssl' ) ) - return false; - if ( ! function_exists( 'openssl_x509_parse' ) ) - return false; - } - - /** - * Filter whether streams can be used as a transport for retrieving a URL. - * - * @since 2.7.0 - * - * @param bool $use_class Whether the class can be used. Default true. - * @param array $args Request arguments. - */ - return apply_filters( 'use_streams_transport', true, $args ); - } -} - -/** - * Deprecated HTTP Transport method which used fsockopen. - * - * This class is not used, and is included for backwards compatibility only. - * All code should make use of WP_HTTP directly through it's API. - * - * @see WP_HTTP::request - * - * @since 2.7.0 - * @deprecated 3.7.0 Please use WP_HTTP::request() directly - */ -class WP_HTTP_Fsockopen extends WP_HTTP_Streams { - // For backwards compatibility for users who are using the class directly. -} - -/** - * HTTP request method uses Curl extension to retrieve the url. - * - * Requires the Curl extension to be installed. - * - * @package WordPress - * @subpackage HTTP - * @since 2.7.0 - */ -class WP_Http_Curl { - - /** - * Temporary header storage for during requests. - * - * @since 3.2.0 - * @access private - * @var string - */ - private $headers = ''; - - /** - * Temporary body storage for during requests. - * - * @since 3.6.0 - * @access private - * @var string - */ - private $body = ''; - - /** - * The maximum amount of data to receive from the remote server. - * - * @since 3.6.0 - * @access private - * @var int - */ - private $max_body_length = false; - - /** - * The file resource used for streaming to file. - * - * @since 3.6.0 - * @access private - * @var resource - */ - private $stream_handle = false; - - /** - * The total bytes written in the current request. - * - * @since 4.1.0 - * @access private - * @var int - */ - private $bytes_written_total = 0; - - /** - * Send a HTTP request to a URI using cURL extension. - * - * @access public - * @since 2.7.0 - * - * @param string $url The request URL. - * @param string|array $args Optional. Override the defaults. - * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error - */ - public function request($url, $args = array()) { - $defaults = array( - 'method' => 'GET', 'timeout' => 5, - 'redirection' => 5, 'httpversion' => '1.0', - 'blocking' => true, - 'headers' => array(), 'body' => null, 'cookies' => array() - ); - - $r = wp_parse_args( $args, $defaults ); - - if ( isset( $r['headers']['User-Agent'] ) ) { - $r['user-agent'] = $r['headers']['User-Agent']; - unset( $r['headers']['User-Agent'] ); - } elseif ( isset( $r['headers']['user-agent'] ) ) { - $r['user-agent'] = $r['headers']['user-agent']; - unset( $r['headers']['user-agent'] ); - } - - // Construct Cookie: header if any cookies are set. - WP_Http::buildCookieHeader( $r ); - - $handle = curl_init(); - - // cURL offers really easy proxy support. - $proxy = new WP_HTTP_Proxy(); - - if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { - - curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); - curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() ); - curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() ); - - if ( $proxy->use_authentication() ) { - curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); - curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() ); - } - } - - $is_local = isset($r['local']) && $r['local']; - $ssl_verify = isset($r['sslverify']) && $r['sslverify']; - if ( $is_local ) { - /** This filter is documented in wp-includes/class-http.php */ - $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify ); - } elseif ( ! $is_local ) { - /** This filter is documented in wp-includes/class-http.php */ - $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify ); - } - - /* - * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since. - * a value of 0 will allow an unlimited timeout. - */ - $timeout = (int) ceil( $r['timeout'] ); - curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout ); - curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout ); - - curl_setopt( $handle, CURLOPT_URL, $url); - curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); - curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false ); - curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); - curl_setopt( $handle, CURLOPT_CAINFO, $r['sslcertificates'] ); - curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] ); - - /* - * The option doesn't work with safe mode or when open_basedir is set, and there's - * a bug #17490 with redirected POST requests, so handle redirections outside Curl. - */ - curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); - if ( defined( 'CURLOPT_PROTOCOLS' ) ) // PHP 5.2.10 / cURL 7.19.4 - curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); - - switch ( $r['method'] ) { - case 'HEAD': - curl_setopt( $handle, CURLOPT_NOBODY, true ); - break; - case 'POST': - curl_setopt( $handle, CURLOPT_POST, true ); - curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); - break; - case 'PUT': - curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); - curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); - break; - default: - curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] ); - if ( ! is_null( $r['body'] ) ) - curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); - break; - } - - if ( true === $r['blocking'] ) { - curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) ); - curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) ); - } - - curl_setopt( $handle, CURLOPT_HEADER, false ); - - if ( isset( $r['limit_response_size'] ) ) - $this->max_body_length = intval( $r['limit_response_size'] ); - else - $this->max_body_length = false; - - // If streaming to a file open a file handle, and setup our curl streaming handler. - if ( $r['stream'] ) { - if ( ! WP_DEBUG ) - $this->stream_handle = @fopen( $r['filename'], 'w+' ); - else - $this->stream_handle = fopen( $r['filename'], 'w+' ); - if ( ! $this->stream_handle ) - return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) ); - } else { - $this->stream_handle = false; - } - - if ( !empty( $r['headers'] ) ) { - // cURL expects full header strings in each element. - $headers = array(); - foreach ( $r['headers'] as $name => $value ) { - $headers[] = "{$name}: $value"; - } - curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers ); - } - - if ( $r['httpversion'] == '1.0' ) - curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); - else - curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 ); - - /** - * Fires before the cURL request is executed. - * - * Cookies are not currently handled by the HTTP API. This action allows - * plugins to handle cookies themselves. - * - * @since 2.8.0 - * - * @param resource &$handle The cURL handle returned by curl_init(). - * @param array $r The HTTP request arguments. - * @param string $url The request URL. - */ - do_action_ref_array( 'http_api_curl', array( &$handle, $r, $url ) ); - - // We don't need to return the body, so don't. Just execute request and return. - if ( ! $r['blocking'] ) { - curl_exec( $handle ); - - if ( $curl_error = curl_error( $handle ) ) { - curl_close( $handle ); - return new WP_Error( 'http_request_failed', $curl_error ); - } - if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) { - curl_close( $handle ); - return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); - } - - curl_close( $handle ); - return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); - } - - curl_exec( $handle ); - $theHeaders = WP_Http::processHeaders( $this->headers, $url ); - $theBody = $this->body; - $bytes_written_total = $this->bytes_written_total; - - $this->headers = ''; - $this->body = ''; - $this->bytes_written_total = 0; - - $curl_error = curl_errno( $handle ); - - // If an error occurred, or, no response. - if ( $curl_error || ( 0 == strlen( $theBody ) && empty( $theHeaders['headers'] ) ) ) { - if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error ) { - if ( ! $this->max_body_length || $this->max_body_length != $bytes_written_total ) { - if ( $r['stream'] ) { - curl_close( $handle ); - fclose( $this->stream_handle ); - return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); - } else { - curl_close( $handle ); - return new WP_Error( 'http_request_failed', curl_error( $handle ) ); - } - } - } else { - if ( $curl_error = curl_error( $handle ) ) { - curl_close( $handle ); - return new WP_Error( 'http_request_failed', $curl_error ); - } - } - if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) { - curl_close( $handle ); - return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); - } - } - - curl_close( $handle ); - - if ( $r['stream'] ) - fclose( $this->stream_handle ); - - $response = array( - 'headers' => $theHeaders['headers'], - 'body' => null, - 'response' => $theHeaders['response'], - 'cookies' => $theHeaders['cookies'], - 'filename' => $r['filename'] - ); - - // Handle redirects. - if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) ) - return $redirect_response; - - if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) ) - $theBody = WP_Http_Encoding::decompress( $theBody ); - - $response['body'] = $theBody; - - return $response; - } - - /** - * Grab the headers of the cURL request - * - * Each header is sent individually to this callback, so we append to the $header property for temporary storage - * - * @since 3.2.0 - * @access private - * @return int - */ - private function stream_headers( $handle, $headers ) { - $this->headers .= $headers; - return strlen( $headers ); - } - - /** - * Grab the body of the cURL request - * - * The contents of the document are passed in chunks, so we append to the $body property for temporary storage. - * Returning a length shorter than the length of $data passed in will cause cURL to abort the request with CURLE_WRITE_ERROR - * - * @since 3.6.0 - * @access private - * @return int - */ - private function stream_body( $handle, $data ) { - $data_length = strlen( $data ); - - if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) { - $data_length = ( $this->max_body_length - $this->bytes_written_total ); - $data = substr( $data, 0, $data_length ); - } - - if ( $this->stream_handle ) { - $bytes_written = fwrite( $this->stream_handle, $data ); - } else { - $this->body .= $data; - $bytes_written = $data_length; - } - - $this->bytes_written_total += $bytes_written; - - // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR. - return $bytes_written; - } - - /** - * Whether this class can be used for retrieving an URL. - * - * @static - * @since 2.7.0 - * - * @return bool False means this class can not be used, true means it can. - */ - public static function test( $args = array() ) { - if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) - return false; - - $is_ssl = isset( $args['ssl'] ) && $args['ssl']; - - if ( $is_ssl ) { - $curl_version = curl_version(); - // Check whether this cURL version support SSL requests. - if ( ! (CURL_VERSION_SSL & $curl_version['features']) ) - return false; - } - - /** - * Filter whether cURL can be used as a transport for retrieving a URL. - * - * @since 2.7.0 - * - * @param bool $use_class Whether the class can be used. Default true. - * @param array $args An array of request arguments. - */ - return apply_filters( 'use_curl_transport', true, $args ); - } -} - -/** - * Adds Proxy support to the WordPress HTTP API. - * - * There are caveats to proxy support. It requires that defines be made in the wp-config.php file to - * enable proxy support. There are also a few filters that plugins can hook into for some of the - * constants. - * - * Please note that only BASIC authentication is supported by most transports. - * cURL MAY support more methods (such as NTLM authentication) depending on your environment. - * - * The constants are as follows: - *
    - *
  1. WP_PROXY_HOST - Enable proxy support and host for connecting.
  2. - *
  3. WP_PROXY_PORT - Proxy port for connection. No default, must be defined.
  4. - *
  5. WP_PROXY_USERNAME - Proxy username, if it requires authentication.
  6. - *
  7. WP_PROXY_PASSWORD - Proxy password, if it requires authentication.
  8. - *
  9. WP_PROXY_BYPASS_HOSTS - Will prevent the hosts in this list from going through the proxy. - * You do not need to have localhost and the blog host in this list, because they will not be passed - * through the proxy. The list should be presented in a comma separated list, wildcards using * are supported, eg. *.wordpress.org
  10. - *
- * - * An example can be as seen below. - * - * define('WP_PROXY_HOST', '192.168.84.101'); - * define('WP_PROXY_PORT', '8080'); - * define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com, *.wordpress.org'); - * - * @link https://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress. - * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_PROXY_BYPASS_HOSTS - * @since 2.8.0 - */ -class WP_HTTP_Proxy { - - /** - * Whether proxy connection should be used. - * - * @since 2.8.0 - * - * @use WP_PROXY_HOST - * @use WP_PROXY_PORT - * - * @return bool - */ - public function is_enabled() { - return defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT'); - } - - /** - * Whether authentication should be used. - * - * @since 2.8.0 - * - * @use WP_PROXY_USERNAME - * @use WP_PROXY_PASSWORD - * - * @return bool - */ - public function use_authentication() { - return defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD'); - } - - /** - * Retrieve the host for the proxy server. - * - * @since 2.8.0 - * - * @return string - */ - public function host() { - if ( defined('WP_PROXY_HOST') ) - return WP_PROXY_HOST; - - return ''; - } - - /** - * Retrieve the port for the proxy server. - * - * @since 2.8.0 - * - * @return string - */ - public function port() { - if ( defined('WP_PROXY_PORT') ) - return WP_PROXY_PORT; - - return ''; - } - - /** - * Retrieve the username for proxy authentication. - * - * @since 2.8.0 - * - * @return string - */ - public function username() { - if ( defined('WP_PROXY_USERNAME') ) - return WP_PROXY_USERNAME; - - return ''; - } - - /** - * Retrieve the password for proxy authentication. - * - * @since 2.8.0 - * - * @return string - */ - public function password() { - if ( defined('WP_PROXY_PASSWORD') ) - return WP_PROXY_PASSWORD; - - return ''; - } - - /** - * Retrieve authentication string for proxy authentication. - * - * @since 2.8.0 - * - * @return string - */ - public function authentication() { - return $this->username() . ':' . $this->password(); - } - - /** - * Retrieve header string for proxy authentication. - * - * @since 2.8.0 - * - * @return string - */ - public function authentication_header() { - return 'Proxy-Authorization: Basic ' . base64_encode( $this->authentication() ); - } - - /** - * Whether URL should be sent through the proxy server. - * - * We want to keep localhost and the blog URL from being sent through the proxy server, because - * some proxies can not handle this. We also have the constant available for defining other - * hosts that won't be sent through the proxy. - * - * @since 2.8.0 - * - * @staticvar array|null $bypass_hosts - * @staticvar array $wildcard_regex - * - * @param string $uri URI to check. - * @return bool True, to send through the proxy and false if, the proxy should not be used. - */ - public function send_through_proxy( $uri ) { - /* - * parse_url() only handles http, https type URLs, and will emit E_WARNING on failure. - * This will be displayed on blogs, which is not reasonable. - */ - $check = @parse_url($uri); - - // Malformed URL, can not process, but this could mean ssl, so let through anyway. - if ( $check === false ) - return true; - - $home = parse_url( get_option('siteurl') ); - - /** - * Filter whether to preempt sending the request through the proxy server. - * - * Returning false will bypass the proxy; returning true will send - * the request through the proxy. Returning null bypasses the filter. - * - * @since 3.5.0 - * - * @param null $override Whether to override the request result. Default null. - * @param string $uri URL to check. - * @param array $check Associative array result of parsing the URI. - * @param array $home Associative array result of parsing the site URL. - */ - $result = apply_filters( 'pre_http_send_through_proxy', null, $uri, $check, $home ); - if ( ! is_null( $result ) ) - return $result; - - if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) ) - return false; - - if ( !defined('WP_PROXY_BYPASS_HOSTS') ) - return true; - - static $bypass_hosts = null; - static $wildcard_regex = array(); - if ( null === $bypass_hosts ) { - $bypass_hosts = preg_split('|,\s*|', WP_PROXY_BYPASS_HOSTS); - - if ( false !== strpos(WP_PROXY_BYPASS_HOSTS, '*') ) { - $wildcard_regex = array(); - foreach ( $bypass_hosts as $host ) - $wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) ); - $wildcard_regex = '/^(' . implode('|', $wildcard_regex) . ')$/i'; - } - } - - if ( !empty($wildcard_regex) ) - return !preg_match($wildcard_regex, $check['host']); - else - return !in_array( $check['host'], $bypass_hosts ); - } -} -/** - * Internal representation of a single cookie. - * - * Returned cookies are represented using this class, and when cookies are set, if they are not - * already a WP_Http_Cookie() object, then they are turned into one. - * - * @todo The WordPress convention is to use underscores instead of camelCase for function and method - * names. Need to switch to use underscores instead for the methods. - * - * @package WordPress - * @subpackage HTTP - * @since 2.8.0 - */ -class WP_Http_Cookie { - - /** - * Cookie name. - * - * @since 2.8.0 - * @var string - */ - public $name; - - /** - * Cookie value. - * - * @since 2.8.0 - * @var string - */ - public $value; - - /** - * When the cookie expires. - * - * @since 2.8.0 - * @var string - */ - public $expires; - - /** - * Cookie URL path. - * - * @since 2.8.0 - * @var string - */ - public $path; - - /** - * Cookie Domain. - * - * @since 2.8.0 - * @var string - */ - public $domain; - - /** - * Sets up this cookie object. - * - * The parameter $data should be either an associative array containing the indices names below - * or a header string detailing it. - * - * @since 2.8.0 - * @access public - * - * @param string|array $data { - * Raw cookie data as header string or data array. - * - * @type string $name Cookie name. - * @type mixed $value Value. Should NOT already be urlencoded. - * @type string|int $expires Optional. Unix timestamp or formatted date. Default null. - * @type string $path Optional. Path. Default '/'. - * @type string $domain Optional. Domain. Default host of parsed $requested_url. - * @type int $port Optional. Port. Default null. - * } - * @param string $requested_url The URL which the cookie was set on, used for default $domain - * and $port values. - */ - public function __construct( $data, $requested_url = '' ) { - if ( $requested_url ) - $arrURL = @parse_url( $requested_url ); - if ( isset( $arrURL['host'] ) ) - $this->domain = $arrURL['host']; - $this->path = isset( $arrURL['path'] ) ? $arrURL['path'] : '/'; - if ( '/' != substr( $this->path, -1 ) ) - $this->path = dirname( $this->path ) . '/'; - - if ( is_string( $data ) ) { - // Assume it's a header string direct from a previous request. - $pairs = explode( ';', $data ); - - // Special handling for first pair; name=value. Also be careful of "=" in value. - $name = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) ); - $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 ); - $this->name = $name; - $this->value = urldecode( $value ); - - // Removes name=value from items. - array_shift( $pairs ); - - // Set everything else as a property. - foreach ( $pairs as $pair ) { - $pair = rtrim($pair); - - // Handle the cookie ending in ; which results in a empty final pair. - if ( empty($pair) ) - continue; - - list( $key, $val ) = strpos( $pair, '=' ) ? explode( '=', $pair ) : array( $pair, '' ); - $key = strtolower( trim( $key ) ); - if ( 'expires' == $key ) - $val = strtotime( $val ); - $this->$key = $val; - } - } else { - if ( !isset( $data['name'] ) ) - return; - - // Set properties based directly on parameters. - foreach ( array( 'name', 'value', 'path', 'domain', 'port' ) as $field ) { - if ( isset( $data[ $field ] ) ) - $this->$field = $data[ $field ]; - } - - if ( isset( $data['expires'] ) ) - $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] ); - else - $this->expires = null; - } - } - - /** - * Confirms that it's OK to send this cookie to the URL checked against. - * - * Decision is based on RFC 2109/2965, so look there for details on validity. - * - * @access public - * @since 2.8.0 - * - * @param string $url URL you intend to send this cookie to - * @return bool true if allowed, false otherwise. - */ - public function test( $url ) { - if ( is_null( $this->name ) ) - return false; - - // Expires - if expired then nothing else matters. - if ( isset( $this->expires ) && time() > $this->expires ) - return false; - - // Get details on the URL we're thinking about sending to. - $url = parse_url( $url ); - $url['port'] = isset( $url['port'] ) ? $url['port'] : ( 'https' == $url['scheme'] ? 443 : 80 ); - $url['path'] = isset( $url['path'] ) ? $url['path'] : '/'; - - // Values to use for comparison against the URL. - $path = isset( $this->path ) ? $this->path : '/'; - $port = isset( $this->port ) ? $this->port : null; - $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] ); - if ( false === stripos( $domain, '.' ) ) - $domain .= '.local'; - - // Host - very basic check that the request URL ends with the domain restriction (minus leading dot). - $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain; - if ( substr( $url['host'], -strlen( $domain ) ) != $domain ) - return false; - - // Port - supports "port-lists" in the format: "80,8000,8080". - if ( !empty( $port ) && !in_array( $url['port'], explode( ',', $port) ) ) - return false; - - // Path - request path must start with path restriction. - if ( substr( $url['path'], 0, strlen( $path ) ) != $path ) - return false; - - return true; - } - - /** - * Convert cookie name and value back to header string. - * - * @access public - * @since 2.8.0 - * - * @return string Header encoded cookie name and value. - */ - public function getHeaderValue() { - if ( ! isset( $this->name ) || ! isset( $this->value ) ) - return ''; - - /** - * Filter the header-encoded cookie value. - * - * @since 3.4.0 - * - * @param string $value The cookie value. - * @param string $name The cookie name. - */ - return $this->name . '=' . apply_filters( 'wp_http_cookie_value', $this->value, $this->name ); - } - - /** - * Retrieve cookie header for usage in the rest of the WordPress HTTP API. - * - * @access public - * @since 2.8.0 - * - * @return string - */ - public function getFullHeader() { - return 'Cookie: ' . $this->getHeaderValue(); - } -} - -/** - * Implementation for deflate and gzip transfer encodings. - * - * Includes RFC 1950, RFC 1951, and RFC 1952. - * - * @since 2.8.0 - * @package WordPress - * @subpackage HTTP - */ -class WP_Http_Encoding { - - /** - * Compress raw string using the deflate format. - * - * Supports the RFC 1951 standard. - * - * @since 2.8.0 - * - * @static - * - * @param string $raw String to compress. - * @param int $level Optional, default is 9. Compression level, 9 is highest. - * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports. - * @return string|false False on failure. - */ - public static function compress( $raw, $level = 9, $supports = null ) { - return gzdeflate( $raw, $level ); - } - - /** - * Decompression of deflated string. - * - * Will attempt to decompress using the RFC 1950 standard, and if that fails - * then the RFC 1951 standard deflate will be attempted. Finally, the RFC - * 1952 standard gzip decode will be attempted. If all fail, then the - * original compressed string will be returned. - * - * @since 2.8.0 - * - * @static - * - * @param string $compressed String to decompress. - * @param int $length The optional length of the compressed data. - * @return string|bool False on failure. - */ - public static function decompress( $compressed, $length = null ) { - - if ( empty($compressed) ) - return $compressed; - - if ( false !== ( $decompressed = @gzinflate( $compressed ) ) ) - return $decompressed; - - if ( false !== ( $decompressed = self::compatible_gzinflate( $compressed ) ) ) - return $decompressed; - - if ( false !== ( $decompressed = @gzuncompress( $compressed ) ) ) - return $decompressed; - - if ( function_exists('gzdecode') ) { - $decompressed = @gzdecode( $compressed ); - - if ( false !== $decompressed ) - return $decompressed; - } - - return $compressed; - } - - /** - * Decompression of deflated string while staying compatible with the majority of servers. - * - * Certain Servers will return deflated data with headers which PHP's gzinflate() - * function cannot handle out of the box. The following function has been created from - * various snippets on the gzinflate() PHP documentation. - * - * Warning: Magic numbers within. Due to the potential different formats that the compressed - * data may be returned in, some "magic offsets" are needed to ensure proper decompression - * takes place. For a simple progmatic way to determine the magic offset in use, see: - * https://core.trac.wordpress.org/ticket/18273 - * - * @since 2.8.1 - * @link https://core.trac.wordpress.org/ticket/18273 - * @link http://au2.php.net/manual/en/function.gzinflate.php#70875 - * @link http://au2.php.net/manual/en/function.gzinflate.php#77336 - * - * @static - * - * @param string $gzData String to decompress. - * @return string|bool False on failure. - */ - public static function compatible_gzinflate($gzData) { - - // Compressed data might contain a full header, if so strip it for gzinflate(). - if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) { - $i = 10; - $flg = ord( substr($gzData, 3, 1) ); - if ( $flg > 0 ) { - if ( $flg & 4 ) { - list($xlen) = unpack('v', substr($gzData, $i, 2) ); - $i = $i + 2 + $xlen; - } - if ( $flg & 8 ) - $i = strpos($gzData, "\0", $i) + 1; - if ( $flg & 16 ) - $i = strpos($gzData, "\0", $i) + 1; - if ( $flg & 2 ) - $i = $i + 2; - } - $decompressed = @gzinflate( substr($gzData, $i, -8) ); - if ( false !== $decompressed ) - return $decompressed; - } - - // Compressed data from java.util.zip.Deflater amongst others. - $decompressed = @gzinflate( substr($gzData, 2) ); - if ( false !== $decompressed ) - return $decompressed; - - return false; - } - - /** - * What encoding types to accept and their priority values. - * - * @since 2.8.0 - * - * @static - * - * @param string $url - * @param array $args - * @return string Types of encoding to accept. - */ - public static function accept_encoding( $url, $args ) { - $type = array(); - $compression_enabled = self::is_available(); - - if ( ! $args['decompress'] ) // Decompression specifically disabled. - $compression_enabled = false; - elseif ( $args['stream'] ) // Disable when streaming to file. - $compression_enabled = false; - elseif ( isset( $args['limit_response_size'] ) ) // If only partial content is being requested, we won't be able to decompress it. - $compression_enabled = false; - - if ( $compression_enabled ) { - if ( function_exists( 'gzinflate' ) ) - $type[] = 'deflate;q=1.0'; - - if ( function_exists( 'gzuncompress' ) ) - $type[] = 'compress;q=0.5'; - - if ( function_exists( 'gzdecode' ) ) - $type[] = 'gzip;q=0.5'; - } - - /** - * Filter the allowed encoding types. - * - * @since 3.6.0 - * - * @param array $type Encoding types allowed. Accepts 'gzinflate', - * 'gzuncompress', 'gzdecode'. - * @param string $url URL of the HTTP request. - * @param array $args HTTP request arguments. - */ - $type = apply_filters( 'wp_http_accept_encoding', $type, $url, $args ); - - return implode(', ', $type); - } - - /** - * What encoding the content used when it was compressed to send in the headers. - * - * @since 2.8.0 - * - * @static - * - * @return string Content-Encoding string to send in the header. - */ - public static function content_encoding() { - return 'deflate'; - } - - /** - * Whether the content be decoded based on the headers. - * - * @since 2.8.0 - * - * @static - * - * @param array|string $headers All of the available headers. - * @return bool - */ - public static function should_decode($headers) { - if ( is_array( $headers ) ) { - if ( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) ) - return true; - } elseif ( is_string( $headers ) ) { - return ( stripos($headers, 'content-encoding:') !== false ); - } - - return false; - } - - /** - * Whether decompression and compression are supported by the PHP version. - * - * Each function is tested instead of checking for the zlib extension, to - * ensure that the functions all exist in the PHP version and aren't - * disabled. - * - * @since 2.8.0 - * - * @static - * - * @return bool - */ - public static function is_available() { - return ( function_exists('gzuncompress') || function_exists('gzdeflate') || function_exists('gzinflate') ); - } -}