Make WordPress Core


Ignore:
Timestamp:
09/04/2013 04:48:21 AM (11 years ago)
Author:
dd32
Message:

WP_HTTP: Replacing the Fsockopen & Streams Transports with a new Streams transport which fully supports HTTPS communication.
This changeset also bundles ca-bundle.crt from the Mozilla project to allow for us to verify SSL certificates on hosts which have an incomplete, outdated, or invalid local SSL configuration.
Props rmccue for major assistance getting this this far. See #25007 for discussion, also Fixes #16606

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-http.php

    r25222 r25224  
    8282            'decompress' => true,
    8383            'sslverify' => true,
     84            'sslcertificates' => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
    8485            'stream' => false,
    8586            'filename' => null,
     
    215216     */
    216217    public function _get_first_available_transport( $args, $url = null ) {
    217         $request_order = apply_filters( 'http_api_transports', array( 'curl', 'streams', 'fsockopen' ), $args, $url );
     218        $request_order = apply_filters( 'http_api_transports', array(  'streams' ), $args, $url );
    218219
    219220        // Loop over each transport on each HTTP request looking for one which will serve this request's needs
     
    237238     * Also caches the transport instance to be used later.
    238239     *
    239      * The order for blocking requests is cURL, Streams, and finally Fsockopen.
    240      * The order for non-blocking requests is cURL, Streams and Fsockopen().
     240     * The order for requests is cURL, and then PHP Streams.
    241241     *
    242242     * @since 3.2.0
     
    633633        return wp_remote_request( $redirect_location, $args ); 
    634634    }
     635
     636    /**
     637     * Determines if a specified string represents an IP address or not.
     638     *
     639     * This function also detects the type of the IP address, returning either
     640     * '4' or '6' to represent a IPv4 and IPv6 address respectively.
     641     * This does not verify if the IP is a valid IP, only that it appears to be
     642     * an IP address.
     643     *
     644     * @see http://home.deds.nl/~aeron/regex/ for IPv6 regex
     645     *
     646     * @since 3.7.0
     647     * @static
     648     *
     649     * @param string $maybe_ip A suspected IP address
     650     * @return integer|bool Upon success, '4' or '6' to represent a IPv4 or IPv6 address, false upon failure
     651     */
     652    static function is_ip_address( $maybe_ip ) {
     653        if ( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $maybe_ip ) )
     654            return 4;
     655
     656        if ( false !== strpos( $maybe_ip, ':' ) && preg_match( '/^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i', trim( $maybe_ip, ' []' ) ) )
     657            return 6;
     658
     659        return false;
     660    }
     661
    635662}
    636663
    637664/**
    638  * HTTP request method uses fsockopen function to retrieve the url.
    639  *
    640  * This would be the preferred method, but the fsockopen implementation has the most overhead of all
    641  * the HTTP transport implementations.
     665 * HTTP request method uses PHP Streams to retrieve the url.
    642666 *
    643667 * @package WordPress
    644668 * @subpackage HTTP
    645  * @since 2.7.0
     669 * @since 3.7.0
    646670 */
    647 class WP_Http_Fsockopen {
    648     /**
    649      * Send a HTTP request to a URI using fsockopen().
    650      *
    651      * Does not support non-blocking mode.
     671class WP_Http_Streams {
     672    /**
     673     * Send a HTTP request to a URI using PHP Streams.
    652674     *
    653675     * @see WP_Http::request For default options descriptions.
    654676     *
    655      * @since 2.7
     677     * @since 3.7.0
    656678     * @access public
    657679     * @param string $url URI resource.
    658      * @param str|array $args Optional. Override the defaults.
     680     * @param string|array $args Optional. Override the defaults.
    659681     * @return array 'headers', 'body', 'response', 'cookies' and 'filename' keys.
    660682     */
     
    680702        WP_Http::buildCookieHeader( $r );
    681703
    682         $iError = null; // Store error number
    683         $strError = null; // Store error string
    684 
    685704        $arrURL = parse_url($url);
    686705
    687         $fsockopen_host = $arrURL['host'];
    688 
    689         $secure_transport = false;
    690 
     706        $connect_host = $arrURL['host'];
     707
     708        $secure_transport = ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' );
    691709        if ( ! isset( $arrURL['port'] ) ) {
    692             if ( ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) && extension_loaded('openssl') ) {
    693                 $fsockopen_host = "ssl://$fsockopen_host";
     710            if ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) {
    694711                $arrURL['port'] = 443;
    695712                $secure_transport = true;
     
    707724        }
    708725
    709         //fsockopen has issues with 'localhost' with IPv6 with certain versions of PHP, It attempts to connect to ::1,
     726        // Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect to ::1,
    710727        // which fails when the server is not set up for it. For compatibility, always connect to the IPv4 address.
    711         if ( 'localhost' == strtolower($fsockopen_host) )
    712             $fsockopen_host = '127.0.0.1';
    713 
    714         // There are issues with the HTTPS and SSL protocols that cause errors that can be safely
    715         // ignored and should be ignored.
    716         if ( true === $secure_transport )
    717             $error_reporting = error_reporting(0);
    718 
    719         $startDelay = time();
     728        if ( 'localhost' == strtolower( $connect_host ) )
     729            $connect_host = '127.0.0.1';
     730
     731        $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host;
     732
     733        $is_local = isset( $r['local'] ) && $r['local'];
     734        $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify'];
     735        if ( $is_local )
     736            $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify );
     737        elseif ( ! $is_local )
     738            $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify );
    720739
    721740        $proxy = new WP_HTTP_Proxy();
    722741
     742        $context = stream_context_create( array(
     743            'ssl' => array(
     744                'verify_peer' => $ssl_verify,
     745                //'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certficate()
     746                'capture_peer_cert' => $ssl_verify,
     747                'SNI_enabled' => true,
     748                'cafile' => $r['sslcertificates'],
     749                'allow_self_signed' => ! $ssl_verify,
     750            )
     751        ) );
     752
     753        $timeout = (int) floor( $r['timeout'] );
     754        $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
     755        $connect_timeout = max( $timeout, 1 );
     756
     757        $connection_error = null; // Store error number
     758        $connection_error_str = null; // Store error string
     759
    723760        if ( !WP_DEBUG ) {
     761            // In the event that the SSL connection fails, silence the many PHP Warnings
     762            if ( $secure_transport )
     763                $error_reporting = error_reporting(0);
     764
    724765            if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
    725                 $handle = @fsockopen( $proxy->host(), $proxy->port(), $iError, $strError, $r['timeout'] );
     766                $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
    726767            else
    727                 $handle = @fsockopen( $fsockopen_host, $arrURL['port'], $iError, $strError, $r['timeout'] );
     768                $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
     769
     770            if ( $secure_transport )
     771                error_reporting( $error_reporting );
     772
    728773        } else {
    729774            if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
    730                 $handle = fsockopen( $proxy->host(), $proxy->port(), $iError, $strError, $r['timeout'] );
     775                $handle = stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
    731776            else
    732                 $handle = fsockopen( $fsockopen_host, $arrURL['port'], $iError, $strError, $r['timeout'] );
    733         }
    734 
    735         $endDelay = time();
    736 
    737         // If the delay is greater than the timeout then fsockopen shouldn't be used, because it will
    738         // cause a long delay.
    739         $elapseDelay = ($endDelay-$startDelay) > $r['timeout'];
    740         if ( true === $elapseDelay )
    741             add_option( 'disable_fsockopen', $endDelay, null, true );
    742 
    743         if ( false === $handle )
    744             return new WP_Error('http_request_failed', $iError . ': ' . $strError);
    745 
    746         $timeout = (int) floor( $r['timeout'] );
    747         $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
     777                $handle = stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
     778        }
     779
     780        if ( false === $handle ) {
     781            // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken
     782            if ( $secure_transport && 0 === $connection_error && '' === $connection_error_str )
     783                return new WP_Error( 'http_request_failed', __( 'The SSL Certificate for the host could not be verified.' ) );
     784
     785            return new WP_Error('http_request_failed', $connection_error . ': ' . $connection_error_str );
     786        }
     787
     788        // Verify that the SSL certificate is valid for this request
     789        if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) {
     790            if ( ! self::verify_ssl_certficate( $handle, $arrURL['host'] ) )
     791                return new WP_Error( 'http_request_failed', __( 'The SSL Certificate for the host could not be verified.' ) );
     792        }
     793
    748794        stream_set_timeout( $handle, $timeout, $utimeout );
    749795
     
    784830
    785831        if ( ! $r['blocking'] ) {
    786             fclose($handle);
     832            stream_set_blocking( $handle, 0 );
     833            fclose( $handle );
    787834            return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    788835        }
     
    847894        fclose( $handle );
    848895
    849         if ( true === $secure_transport )
    850             error_reporting($error_reporting);
    851 
    852896        $arrHeaders = WP_Http::processHeaders( $process['headers'], $url );
    853897
     
    880924
    881925    /**
     926     * Verifies the received SSL certificate against it's Common Names and subjectAltName fields
     927     *
     928     * PHP's SSL verifications only verify that it's a valid Certificate, it doesn't verify if
     929     * the certificate is valid for the hostname which was requested.
     930     * This function verifies the requested hostname against certificate's subjectAltName field,
     931     * if that is empty, or contains no DNS entries, a fallback to the Common Name field is used.
     932     *
     933     * IP Address support is included if the request is being made to an IP address.
     934     *
     935     * @since 3.7.0
     936     * @static
     937     *
     938     * @param stream $stream The PHP Stream which the SSL request is being made over
     939     * @param string $host The hostname being requested
     940     * @return bool If the cerficiate presented in $stream is valid for $host
     941     */
     942    static function verify_ssl_certficate( $stream, $host ) {
     943        $context_options = stream_context_get_options( $stream );
     944
     945        if ( empty( $context_options['ssl']['peer_certificate'] ) )
     946            return false;
     947
     948        $cert = openssl_x509_parse( $context_options['ssl']['peer_certificate'] );
     949        if ( ! $cert )
     950            return false;
     951
     952        // If the request is being made to an IP address, we'll validate against IP fields in the cert (if they exist)
     953        $host_type = ( WP_HTTP::is_ip_address( $host ) ? 'ip' : 'dns' );
     954
     955        $certificate_hostnames = array();
     956        if ( ! empty( $cert['extensions']['subjectAltName'] ) ) {
     957            $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] );
     958            foreach ( $match_against as $match ) {
     959                list( $match_type, $match_host ) = explode( ':', $match );
     960                if ( $host_type == strtolower( trim( $match_type ) ) ) // IP: or DNS:
     961                    $certificate_hostnames[] = strtolower( trim( $match_host ) );
     962            }
     963        } elseif ( !empty( $cert['subject']['CN'] ) ) {
     964            // Only use the CN when the certificate includes no subjectAltName extension
     965            $certificate_hostnames[] = strtolower( $cert['subject']['CN'] );
     966        }
     967
     968        // Exact hostname/IP matches
     969        if ( in_array( strtolower( $host ), $certificate_hostnames ) )
     970            return true;
     971
     972        // IP's can't be wildcards, Stop processing
     973        if ( 'ip' == $host_type )
     974            return false;
     975
     976        // Test to see if the domain is at least 2 deep for wildcard support
     977        if ( substr_count( $host, '.' ) < 2 )
     978            return false;
     979
     980        // Wildcard subdomains certs (*.example.com) are valid for a.example.com but not a.b.example.com
     981        $wildcard_host = preg_replace( '/^[^.]+\./', '*.', $host );
     982
     983        return in_array( strtolower( $wildcard_host ), $certificate_hostnames );
     984    }
     985
     986    /**
    882987     * Whether this class can be used for retrieving an URL.
    883988     *
    884      * @since 2.7.0
    885989     * @static
     990     * @access public
     991     * @since 3.7.0
     992     *
    886993     * @return boolean False means this class can not be used, true means it can.
    887994     */
    888995    public static function test( $args = array() ) {
    889         if ( ! function_exists( 'fsockopen' ) )
     996        if ( ! function_exists( 'stream_socket_client' ) )
    890997            return false;
    891998
    892         if ( false !== ( $option = get_option( 'disable_fsockopen' ) ) && time() - $option < 12 * HOUR_IN_SECONDS )
    893             return false;
    894 
    895999        $is_ssl = isset( $args['ssl'] ) && $args['ssl'];
    8961000
    897         if ( $is_ssl && ! extension_loaded( 'openssl' ) )
    898             return false;
    899 
    900         return apply_filters( 'use_fsockopen_transport', true, $args );
     1001        if ( $is_ssl ) {
     1002            if ( ! extension_loaded( 'openssl' ) )
     1003                return false;
     1004            if ( ! function_exists( 'openssl_x509_parse' ) )
     1005                return false;
     1006        }
     1007
     1008        return apply_filters( 'use_streams_transport', true, $args );
    9011009    }
    9021010}
    9031011
    9041012/**
    905  * HTTP request method uses Streams to retrieve the url.
    906  *
    907  * Requires PHP 5.0+ and uses fopen with stream context. Requires that 'allow_url_fopen' PHP setting
    908  * to be enabled.
    909  *
    910  * Second preferred method for getting the URL, for PHP 5.
     1013 * Deprecated HTTP Transport method which used fsockopen.
     1014 * This class is not used, and is included for backwards compatibility only.
     1015 * All code should make use of WP_HTTP directly through it's API.
     1016 *
     1017 * @see WP_HTTP::request
     1018 *
     1019 * @package WordPress
     1020 * @subpackage HTTP
     1021 * @since 3.7.0
     1022 */
     1023class WP_HTTP_Fsockopen extends WP_HTTP_Streams {
     1024    // For backwards compatibility for users who are using the class directly
     1025}
     1026
     1027/**
     1028 * HTTP request method uses Curl extension to retrieve the url.
     1029 *
     1030 * Requires the Curl extension to be installed.
    9111031 *
    9121032 * @package WordPress
     
    9141034 * @since 2.7.0
    9151035 */
    916 class WP_Http_Streams {
    917     /**
    918      * Send a HTTP request to a URI using streams with fopen().
     1036class WP_Http_Curl {
     1037
     1038    /**
     1039     * Temporary header storage for during requests.
     1040     *
     1041     * @since 3.2.0
     1042     * @access private
     1043     * @var string
     1044     */
     1045    private $headers = '';
     1046
     1047    /**
     1048     * Temporary body storage for during requests.
     1049     *
     1050     * @since 3.6.0
     1051     * @access private
     1052     * @var string
     1053     */
     1054    private $body = '';
     1055
     1056    /**
     1057     * The maximum amount of data to recieve from the remote server
     1058     *
     1059     * @since 3.6.0
     1060     * @access private
     1061     * @var int
     1062     */
     1063    private $max_body_length = false;
     1064
     1065    /**
     1066     * The file resource used for streaming to file.
     1067     *
     1068     * @since 3.6.0
     1069     * @access private
     1070     * @var resource
     1071     */
     1072    private $stream_handle = false;
     1073
     1074    /**
     1075     * Send a HTTP request to a URI using cURL extension.
    9191076     *
    9201077     * @access public
     
    9431100        }
    9441101
    945         // Construct Cookie: header if any cookies are set
    946         WP_Http::buildCookieHeader( $r );
    947 
    948         $arrURL = parse_url($url);
    949 
    950         if ( false === $arrURL )
    951             return new WP_Error('http_request_failed', sprintf(__('Malformed URL: %s'), $url));
    952 
    953         if ( 'http' != $arrURL['scheme'] && 'https' != $arrURL['scheme'] )
    954             $url = preg_replace('|^' . preg_quote($arrURL['scheme'], '|') . '|', 'http', $url);
    955 
    956         // Convert Header array to string.
    957         $strHeaders = '';
    958         if ( is_array( $r['headers'] ) )
    959             foreach ( $r['headers'] as $name => $value )
    960                 $strHeaders .= "{$name}: $value\r\n";
    961         else if ( is_string( $r['headers'] ) )
    962             $strHeaders = $r['headers'];
    963 
    964         $is_local = isset($args['local']) && $args['local'];
    965         $ssl_verify = isset($args['sslverify']) && $args['sslverify'];
    966         if ( $is_local )
    967             $ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
    968         elseif ( ! $is_local )
    969             $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
    970 
    971         $arrContext = array('http' =>
    972             array(
    973                 'method' => strtoupper($r['method']),
    974                 'user_agent' => $r['user-agent'],
    975                 'max_redirects' => 0, // Follow no redirects
    976                 'follow_redirects' => false,
    977                 'protocol_version' => (float) $r['httpversion'],
    978                 'header' => $strHeaders,
    979                 'ignore_errors' => true, // Return non-200 requests.
    980                 'timeout' => $r['timeout'],
    981                 'ssl' => array(
    982                         'verify_peer' => $ssl_verify,
    983                         'verify_host' => $ssl_verify
    984                 )
    985             )
    986         );
    987 
    988         $proxy = new WP_HTTP_Proxy();
    989 
    990         if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
    991             $arrContext['http']['proxy'] = 'tcp://' . $proxy->host() . ':' . $proxy->port();
    992             $arrContext['http']['request_fulluri'] = true;
    993 
    994             // We only support Basic authentication so this will only work if that is what your proxy supports.
    995             if ( $proxy->use_authentication() )
    996                 $arrContext['http']['header'] .= $proxy->authentication_header() . "\r\n";
    997         }
    998 
    999         if ( ! is_null( $r['body'] ) )
    1000             $arrContext['http']['content'] = $r['body'];
    1001 
    1002         $context = stream_context_create($arrContext);
    1003 
    1004         if ( !WP_DEBUG )
    1005             $handle = @fopen($url, 'r', false, $context);
    1006         else
    1007             $handle = fopen($url, 'r', false, $context);
    1008 
    1009         if ( ! $handle )
    1010             return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url));
    1011 
    1012         $timeout = (int) floor( $r['timeout'] );
    1013         $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
    1014         stream_set_timeout( $handle, $timeout, $utimeout );
    1015 
    1016         if ( ! $r['blocking'] ) {
    1017             stream_set_blocking($handle, 0);
    1018             fclose($handle);
    1019             return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    1020         }
    1021 
    1022         $max_bytes = isset( $r['limit_response_size'] ) ? intval( $r['limit_response_size'] ) : -1;
    1023         if ( $r['stream'] ) {
    1024             if ( ! WP_DEBUG )
    1025                 $stream_handle = @fopen( $r['filename'], 'w+' );
    1026             else
    1027                 $stream_handle = fopen( $r['filename'], 'w+' );
    1028 
    1029             if ( ! $stream_handle )
    1030                 return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
    1031 
    1032             stream_copy_to_stream( $handle, $stream_handle, $max_bytes );
    1033 
    1034             fclose( $stream_handle );
    1035             $strResponse = '';
    1036         } else {
    1037             $strResponse = stream_get_contents( $handle, $max_bytes );
    1038         }
    1039 
    1040         $meta = stream_get_meta_data( $handle );
    1041 
    1042         fclose( $handle );
    1043 
    1044         $processedHeaders = array();
    1045         if ( isset( $meta['wrapper_data']['headers'] ) )
    1046             $processedHeaders = WP_Http::processHeaders( $meta['wrapper_data']['headers'], $url );
    1047         else
    1048             $processedHeaders = WP_Http::processHeaders( $meta['wrapper_data'], $url );
    1049 
    1050         $response = array(
    1051             'headers' => $processedHeaders['headers'],
    1052             'body' => null,
    1053             'response' => $processedHeaders['response'],
    1054             'cookies' => $processedHeaders['cookies'],
    1055             'filename' => $r['filename']
    1056         );
    1057 
    1058         // Handle redirects
    1059         if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) )
    1060             return $redirect_response;
    1061 
    1062         if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] )
    1063             $strResponse = WP_Http::chunkTransferDecode($strResponse);
    1064 
    1065         if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders['headers']) )
    1066             $strResponse = WP_Http_Encoding::decompress( $strResponse );
    1067 
    1068         $response['body'] = $strResponse;
    1069 
    1070         return $response;
    1071     }
    1072 
    1073     /**
    1074      * Whether this class can be used for retrieving an URL.
    1075      *
    1076      * @static
    1077      * @access public
    1078      * @since 2.7.0
    1079      *
    1080      * @return boolean False means this class can not be used, true means it can.
    1081      */
    1082     public static function test( $args = array() ) {
    1083         if ( ! function_exists( 'fopen' ) )
    1084             return false;
    1085 
    1086         if ( ! function_exists( 'ini_get' ) || true != ini_get( 'allow_url_fopen' ) )
    1087             return false;
    1088 
    1089         $is_ssl = isset( $args['ssl'] ) && $args['ssl'];
    1090 
    1091         if ( $is_ssl && ! extension_loaded( 'openssl' ) )
    1092             return false;
    1093 
    1094         return apply_filters( 'use_streams_transport', true, $args );
    1095     }
    1096 }
    1097 
    1098 /**
    1099  * HTTP request method uses Curl extension to retrieve the url.
    1100  *
    1101  * Requires the Curl extension to be installed.
    1102  *
    1103  * @package WordPress
    1104  * @subpackage HTTP
    1105  * @since 2.7
    1106  */
    1107 class WP_Http_Curl {
    1108 
    1109     /**
    1110      * Temporary header storage for during requests.
    1111      *
    1112      * @since 3.2.0
    1113      * @access private
    1114      * @var string
    1115      */
    1116     private $headers = '';
    1117 
    1118     /**
    1119      * Temporary body storage for during requests.
    1120      *
    1121      * @since 3.6.0
    1122      * @access private
    1123      * @var string
    1124      */
    1125     private $body = '';
    1126 
    1127     /**
    1128      * The maximum amount of data to recieve from the remote server
    1129      *
    1130      * @since 3.6.0
    1131      * @access private
    1132      * @var int
    1133      */
    1134     private $max_body_length = false;
    1135 
    1136     /**
    1137      * The file resource used for streaming to file.
    1138      *
    1139      * @since 3.6.0
    1140      * @access private
    1141      * @var resource
    1142      */
    1143     private $stream_handle = false;
    1144 
    1145     /**
    1146      * Send a HTTP request to a URI using cURL extension.
    1147      *
    1148      * @access public
    1149      * @since 2.7.0
    1150      *
    1151      * @param string $url
    1152      * @param str|array $args Optional. Override the defaults.
    1153      * @return array 'headers', 'body', 'response', 'cookies' and 'filename' keys.
    1154      */
    1155     function request($url, $args = array()) {
    1156         $defaults = array(
    1157             'method' => 'GET', 'timeout' => 5,
    1158             'redirection' => 5, 'httpversion' => '1.0',
    1159             'blocking' => true,
    1160             'headers' => array(), 'body' => null, 'cookies' => array()
    1161         );
    1162 
    1163         $r = wp_parse_args( $args, $defaults );
    1164 
    1165         if ( isset($r['headers']['User-Agent']) ) {
    1166             $r['user-agent'] = $r['headers']['User-Agent'];
    1167             unset($r['headers']['User-Agent']);
    1168         } else if ( isset($r['headers']['user-agent']) ) {
    1169             $r['user-agent'] = $r['headers']['user-agent'];
    1170             unset($r['headers']['user-agent']);
    1171         }
    1172 
    11731102        // Construct Cookie: header if any cookies are set.
    11741103        WP_Http::buildCookieHeader( $r );
     
    12081137        curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false );
    12091138        curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
     1139        curl_setopt( $handle, CURLOPT_CAINFO, $r['sslcertificates'] );
    12101140        curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
    12111141        // The option doesn't work with safe mode or when open_basedir is set, and there's a
Note: See TracChangeset for help on using the changeset viewer.