Make WordPress Core


Ignore:
Timestamp:
08/26/2015 03:55:00 AM (9 years ago)
Author:
wonderboymusic
Message:

HTTP: move classes into their own files, http.php loads the new files, so this is 100% BC if someone is loading http.php directly. New files created using svn cp.

class-http.php requires functions from http.php, so loading it by itself wouldn't have worked.

Creates:
class-wp-http-cookie.php
class-wp-http-curl.php
class-wp-http-encoding.php
class-wp-http-proxy.php
class-wp-http-streams.php
http-functions.php

WP_Http remains in class-http.php.

http.php contains only top-level code. Class files only contain classes. Functions file only contains functions.

See #33413.

File:
1 edited

Legend:

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

    r32964 r33748  
    11<?php
    2 /**
    3  * Simple and uniform HTTP request API.
    4  *
    5  * Standardizes the HTTP requests for WordPress. Handles cookies, gzip encoding and decoding, chunk
    6  * decoding, if HTTP 1.1 and various other difficult HTTP protocol implementations.
    7  *
    8  * @link https://core.trac.wordpress.org/ticket/4779 HTTP API Proposal
    9  *
    10  * @package WordPress
    11  * @subpackage HTTP
    12  * @since 2.7.0
    13  */
    14 
    152/**
    163 * WordPress HTTP Class for managing HTTP Transports and making HTTP requests.
     
    878865
    879866}
    880 
    881 /**
    882  * HTTP request method uses PHP Streams to retrieve the url.
    883  *
    884  * @since 2.7.0
    885  * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client().
    886  */
    887 class WP_Http_Streams {
    888     /**
    889      * Send a HTTP request to a URI using PHP Streams.
    890      *
    891      * @see WP_Http::request For default options descriptions.
    892      *
    893      * @since 2.7.0
    894      * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client().
    895      *
    896      * @access public
    897      * @param string $url The request URL.
    898      * @param string|array $args Optional. Override the defaults.
    899      * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
    900      */
    901     public function request($url, $args = array()) {
    902         $defaults = array(
    903             'method' => 'GET', 'timeout' => 5,
    904             'redirection' => 5, 'httpversion' => '1.0',
    905             'blocking' => true,
    906             'headers' => array(), 'body' => null, 'cookies' => array()
    907         );
    908 
    909         $r = wp_parse_args( $args, $defaults );
    910 
    911         if ( isset( $r['headers']['User-Agent'] ) ) {
    912             $r['user-agent'] = $r['headers']['User-Agent'];
    913             unset( $r['headers']['User-Agent'] );
    914         } elseif ( isset( $r['headers']['user-agent'] ) ) {
    915             $r['user-agent'] = $r['headers']['user-agent'];
    916             unset( $r['headers']['user-agent'] );
    917         }
    918 
    919         // Construct Cookie: header if any cookies are set.
    920         WP_Http::buildCookieHeader( $r );
    921 
    922         $arrURL = parse_url($url);
    923 
    924         $connect_host = $arrURL['host'];
    925 
    926         $secure_transport = ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' );
    927         if ( ! isset( $arrURL['port'] ) ) {
    928             if ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) {
    929                 $arrURL['port'] = 443;
    930                 $secure_transport = true;
    931             } else {
    932                 $arrURL['port'] = 80;
    933             }
    934         }
    935 
    936         // Always pass a Path, defaulting to the root in cases such as http://example.com
    937         if ( ! isset( $arrURL['path'] ) ) {
    938             $arrURL['path'] = '/';
    939         }
    940 
    941         if ( isset( $r['headers']['Host'] ) || isset( $r['headers']['host'] ) ) {
    942             if ( isset( $r['headers']['Host'] ) )
    943                 $arrURL['host'] = $r['headers']['Host'];
    944             else
    945                 $arrURL['host'] = $r['headers']['host'];
    946             unset( $r['headers']['Host'], $r['headers']['host'] );
    947         }
    948 
    949         /*
    950          * Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect
    951          * to ::1, which fails when the server is not set up for it. For compatibility, always
    952          * connect to the IPv4 address.
    953          */
    954         if ( 'localhost' == strtolower( $connect_host ) )
    955             $connect_host = '127.0.0.1';
    956 
    957         $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host;
    958 
    959         $is_local = isset( $r['local'] ) && $r['local'];
    960         $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify'];
    961         if ( $is_local ) {
    962             /**
    963              * Filter whether SSL should be verified for local requests.
    964              *
    965              * @since 2.8.0
    966              *
    967              * @param bool $ssl_verify Whether to verify the SSL connection. Default true.
    968              */
    969             $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify );
    970         } elseif ( ! $is_local ) {
    971             /**
    972              * Filter whether SSL should be verified for non-local requests.
    973              *
    974              * @since 2.8.0
    975              *
    976              * @param bool $ssl_verify Whether to verify the SSL connection. Default true.
    977              */
    978             $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify );
    979         }
    980 
    981         $proxy = new WP_HTTP_Proxy();
    982 
    983         $context = stream_context_create( array(
    984             'ssl' => array(
    985                 'verify_peer' => $ssl_verify,
    986                 //'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate()
    987                 'capture_peer_cert' => $ssl_verify,
    988                 'SNI_enabled' => true,
    989                 'cafile' => $r['sslcertificates'],
    990                 'allow_self_signed' => ! $ssl_verify,
    991             )
    992         ) );
    993 
    994         $timeout = (int) floor( $r['timeout'] );
    995         $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
    996         $connect_timeout = max( $timeout, 1 );
    997 
    998         // Store error number.
    999         $connection_error = null;
    1000 
    1001         // Store error string.
    1002         $connection_error_str = null;
    1003 
    1004         if ( !WP_DEBUG ) {
    1005             // In the event that the SSL connection fails, silence the many PHP Warnings.
    1006             if ( $secure_transport )
    1007                 $error_reporting = error_reporting(0);
    1008 
    1009             if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
    1010                 $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
    1011             else
    1012                 $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
    1013 
    1014             if ( $secure_transport )
    1015                 error_reporting( $error_reporting );
    1016 
    1017         } else {
    1018             if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
    1019                 $handle = stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
    1020             else
    1021                 $handle = stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context );
    1022         }
    1023 
    1024         if ( false === $handle ) {
    1025             // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken.
    1026             if ( $secure_transport && 0 === $connection_error && '' === $connection_error_str )
    1027                 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) );
    1028 
    1029             return new WP_Error('http_request_failed', $connection_error . ': ' . $connection_error_str );
    1030         }
    1031 
    1032         // Verify that the SSL certificate is valid for this request.
    1033         if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) {
    1034             if ( ! self::verify_ssl_certificate( $handle, $arrURL['host'] ) )
    1035                 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) );
    1036         }
    1037 
    1038         stream_set_timeout( $handle, $timeout, $utimeout );
    1039 
    1040         if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) //Some proxies require full URL in this field.
    1041             $requestPath = $url;
    1042         else
    1043             $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' );
    1044 
    1045         $strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n";
    1046 
    1047         $include_port_in_host_header = (
    1048             ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) ||
    1049             ( 'http'  == $arrURL['scheme'] && 80  != $arrURL['port'] ) ||
    1050             ( 'https' == $arrURL['scheme'] && 443 != $arrURL['port'] )
    1051         );
    1052 
    1053         if ( $include_port_in_host_header ) {
    1054             $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n";
    1055         } else {
    1056             $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n";
    1057         }
    1058 
    1059         if ( isset($r['user-agent']) )
    1060             $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n";
    1061 
    1062         if ( is_array($r['headers']) ) {
    1063             foreach ( (array) $r['headers'] as $header => $headerValue )
    1064                 $strHeaders .= $header . ': ' . $headerValue . "\r\n";
    1065         } else {
    1066             $strHeaders .= $r['headers'];
    1067         }
    1068 
    1069         if ( $proxy->use_authentication() )
    1070             $strHeaders .= $proxy->authentication_header() . "\r\n";
    1071 
    1072         $strHeaders .= "\r\n";
    1073 
    1074         if ( ! is_null($r['body']) )
    1075             $strHeaders .= $r['body'];
    1076 
    1077         fwrite($handle, $strHeaders);
    1078 
    1079         if ( ! $r['blocking'] ) {
    1080             stream_set_blocking( $handle, 0 );
    1081             fclose( $handle );
    1082             return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    1083         }
    1084 
    1085         $strResponse = '';
    1086         $bodyStarted = false;
    1087         $keep_reading = true;
    1088         $block_size = 4096;
    1089         if ( isset( $r['limit_response_size'] ) )
    1090             $block_size = min( $block_size, $r['limit_response_size'] );
    1091 
    1092         // If streaming to a file setup the file handle.
    1093         if ( $r['stream'] ) {
    1094             if ( ! WP_DEBUG )
    1095                 $stream_handle = @fopen( $r['filename'], 'w+' );
    1096             else
    1097                 $stream_handle = fopen( $r['filename'], 'w+' );
    1098             if ( ! $stream_handle )
    1099                 return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
    1100 
    1101             $bytes_written = 0;
    1102             while ( ! feof($handle) && $keep_reading ) {
    1103                 $block = fread( $handle, $block_size );
    1104                 if ( ! $bodyStarted ) {
    1105                     $strResponse .= $block;
    1106                     if ( strpos( $strResponse, "\r\n\r\n" ) ) {
    1107                         $process = WP_Http::processResponse( $strResponse );
    1108                         $bodyStarted = true;
    1109                         $block = $process['body'];
    1110                         unset( $strResponse );
    1111                         $process['body'] = '';
    1112                     }
    1113                 }
    1114 
    1115                 $this_block_size = strlen( $block );
    1116 
    1117                 if ( isset( $r['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $r['limit_response_size'] ) {
    1118                     $this_block_size = ( $r['limit_response_size'] - $bytes_written );
    1119                     $block = substr( $block, 0, $this_block_size );
    1120                 }
    1121 
    1122                 $bytes_written_to_file = fwrite( $stream_handle, $block );
    1123 
    1124                 if ( $bytes_written_to_file != $this_block_size ) {
    1125                     fclose( $handle );
    1126                     fclose( $stream_handle );
    1127                     return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) );
    1128                 }
    1129 
    1130                 $bytes_written += $bytes_written_to_file;
    1131 
    1132                 $keep_reading = !isset( $r['limit_response_size'] ) || $bytes_written < $r['limit_response_size'];
    1133             }
    1134 
    1135             fclose( $stream_handle );
    1136 
    1137         } else {
    1138             $header_length = 0;
    1139             while ( ! feof( $handle ) && $keep_reading ) {
    1140                 $block = fread( $handle, $block_size );
    1141                 $strResponse .= $block;
    1142                 if ( ! $bodyStarted && strpos( $strResponse, "\r\n\r\n" ) ) {
    1143                     $header_length = strpos( $strResponse, "\r\n\r\n" ) + 4;
    1144                     $bodyStarted = true;
    1145                 }
    1146                 $keep_reading = ( ! $bodyStarted || !isset( $r['limit_response_size'] ) || strlen( $strResponse ) < ( $header_length + $r['limit_response_size'] ) );
    1147             }
    1148 
    1149             $process = WP_Http::processResponse( $strResponse );
    1150             unset( $strResponse );
    1151 
    1152         }
    1153 
    1154         fclose( $handle );
    1155 
    1156         $arrHeaders = WP_Http::processHeaders( $process['headers'], $url );
    1157 
    1158         $response = array(
    1159             'headers' => $arrHeaders['headers'],
    1160             // Not yet processed.
    1161             'body' => null,
    1162             'response' => $arrHeaders['response'],
    1163             'cookies' => $arrHeaders['cookies'],
    1164             'filename' => $r['filename']
    1165         );
    1166 
    1167         // Handle redirects.
    1168         if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) )
    1169             return $redirect_response;
    1170 
    1171         // If the body was chunk encoded, then decode it.
    1172         if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) && 'chunked' == $arrHeaders['headers']['transfer-encoding'] )
    1173             $process['body'] = WP_Http::chunkTransferDecode($process['body']);
    1174 
    1175         if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders['headers']) )
    1176             $process['body'] = WP_Http_Encoding::decompress( $process['body'] );
    1177 
    1178         if ( isset( $r['limit_response_size'] ) && strlen( $process['body'] ) > $r['limit_response_size'] )
    1179             $process['body'] = substr( $process['body'], 0, $r['limit_response_size'] );
    1180 
    1181         $response['body'] = $process['body'];
    1182 
    1183         return $response;
    1184     }
    1185 
    1186     /**
    1187      * Verifies the received SSL certificate against it's Common Names and subjectAltName fields
    1188      *
    1189      * PHP's SSL verifications only verify that it's a valid Certificate, it doesn't verify if
    1190      * the certificate is valid for the hostname which was requested.
    1191      * This function verifies the requested hostname against certificate's subjectAltName field,
    1192      * if that is empty, or contains no DNS entries, a fallback to the Common Name field is used.
    1193      *
    1194      * IP Address support is included if the request is being made to an IP address.
    1195      *
    1196      * @since 3.7.0
    1197      * @static
    1198      *
    1199      * @param stream $stream The PHP Stream which the SSL request is being made over
    1200      * @param string $host The hostname being requested
    1201      * @return bool If the cerficiate presented in $stream is valid for $host
    1202      */
    1203     public static function verify_ssl_certificate( $stream, $host ) {
    1204         $context_options = stream_context_get_options( $stream );
    1205 
    1206         if ( empty( $context_options['ssl']['peer_certificate'] ) )
    1207             return false;
    1208 
    1209         $cert = openssl_x509_parse( $context_options['ssl']['peer_certificate'] );
    1210         if ( ! $cert )
    1211             return false;
    1212 
    1213         /*
    1214          * If the request is being made to an IP address, we'll validate against IP fields
    1215          * in the cert (if they exist)
    1216          */
    1217         $host_type = ( WP_HTTP::is_ip_address( $host ) ? 'ip' : 'dns' );
    1218 
    1219         $certificate_hostnames = array();
    1220         if ( ! empty( $cert['extensions']['subjectAltName'] ) ) {
    1221             $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] );
    1222             foreach ( $match_against as $match ) {
    1223                 list( $match_type, $match_host ) = explode( ':', $match );
    1224                 if ( $host_type == strtolower( trim( $match_type ) ) ) // IP: or DNS:
    1225                     $certificate_hostnames[] = strtolower( trim( $match_host ) );
    1226             }
    1227         } elseif ( !empty( $cert['subject']['CN'] ) ) {
    1228             // Only use the CN when the certificate includes no subjectAltName extension.
    1229             $certificate_hostnames[] = strtolower( $cert['subject']['CN'] );
    1230         }
    1231 
    1232         // Exact hostname/IP matches.
    1233         if ( in_array( strtolower( $host ), $certificate_hostnames ) )
    1234             return true;
    1235 
    1236         // IP's can't be wildcards, Stop processing.
    1237         if ( 'ip' == $host_type )
    1238             return false;
    1239 
    1240         // Test to see if the domain is at least 2 deep for wildcard support.
    1241         if ( substr_count( $host, '.' ) < 2 )
    1242             return false;
    1243 
    1244         // Wildcard subdomains certs (*.example.com) are valid for a.example.com but not a.b.example.com.
    1245         $wildcard_host = preg_replace( '/^[^.]+\./', '*.', $host );
    1246 
    1247         return in_array( strtolower( $wildcard_host ), $certificate_hostnames );
    1248     }
    1249 
    1250     /**
    1251      * Whether this class can be used for retrieving a URL.
    1252      *
    1253      * @static
    1254      * @access public
    1255      * @since 2.7.0
    1256      * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client().
    1257      *
    1258      * @return bool False means this class can not be used, true means it can.
    1259      */
    1260     public static function test( $args = array() ) {
    1261         if ( ! function_exists( 'stream_socket_client' ) )
    1262             return false;
    1263 
    1264         $is_ssl = isset( $args['ssl'] ) && $args['ssl'];
    1265 
    1266         if ( $is_ssl ) {
    1267             if ( ! extension_loaded( 'openssl' ) )
    1268                 return false;
    1269             if ( ! function_exists( 'openssl_x509_parse' ) )
    1270                 return false;
    1271         }
    1272 
    1273         /**
    1274          * Filter whether streams can be used as a transport for retrieving a URL.
    1275          *
    1276          * @since 2.7.0
    1277          *
    1278          * @param bool  $use_class Whether the class can be used. Default true.
    1279          * @param array $args      Request arguments.
    1280          */
    1281         return apply_filters( 'use_streams_transport', true, $args );
    1282     }
    1283 }
    1284 
    1285 /**
    1286  * Deprecated HTTP Transport method which used fsockopen.
    1287  *
    1288  * This class is not used, and is included for backwards compatibility only.
    1289  * All code should make use of WP_HTTP directly through it's API.
    1290  *
    1291  * @see WP_HTTP::request
    1292  *
    1293  * @since 2.7.0
    1294  * @deprecated 3.7.0 Please use WP_HTTP::request() directly
    1295  */
    1296 class WP_HTTP_Fsockopen extends WP_HTTP_Streams {
    1297     // For backwards compatibility for users who are using the class directly.
    1298 }
    1299 
    1300 /**
    1301  * HTTP request method uses Curl extension to retrieve the url.
    1302  *
    1303  * Requires the Curl extension to be installed.
    1304  *
    1305  * @package WordPress
    1306  * @subpackage HTTP
    1307  * @since 2.7.0
    1308  */
    1309 class WP_Http_Curl {
    1310 
    1311     /**
    1312      * Temporary header storage for during requests.
    1313      *
    1314      * @since 3.2.0
    1315      * @access private
    1316      * @var string
    1317      */
    1318     private $headers = '';
    1319 
    1320     /**
    1321      * Temporary body storage for during requests.
    1322      *
    1323      * @since 3.6.0
    1324      * @access private
    1325      * @var string
    1326      */
    1327     private $body = '';
    1328 
    1329     /**
    1330      * The maximum amount of data to receive from the remote server.
    1331      *
    1332      * @since 3.6.0
    1333      * @access private
    1334      * @var int
    1335      */
    1336     private $max_body_length = false;
    1337 
    1338     /**
    1339      * The file resource used for streaming to file.
    1340      *
    1341      * @since 3.6.0
    1342      * @access private
    1343      * @var resource
    1344      */
    1345     private $stream_handle = false;
    1346 
    1347     /**
    1348      * The total bytes written in the current request.
    1349      *
    1350      * @since 4.1.0
    1351      * @access private
    1352      * @var int
    1353      */
    1354     private $bytes_written_total = 0;
    1355 
    1356     /**
    1357      * Send a HTTP request to a URI using cURL extension.
    1358      *
    1359      * @access public
    1360      * @since 2.7.0
    1361      *
    1362      * @param string $url The request URL.
    1363      * @param string|array $args Optional. Override the defaults.
    1364      * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
    1365      */
    1366     public function request($url, $args = array()) {
    1367         $defaults = array(
    1368             'method' => 'GET', 'timeout' => 5,
    1369             'redirection' => 5, 'httpversion' => '1.0',
    1370             'blocking' => true,
    1371             'headers' => array(), 'body' => null, 'cookies' => array()
    1372         );
    1373 
    1374         $r = wp_parse_args( $args, $defaults );
    1375 
    1376         if ( isset( $r['headers']['User-Agent'] ) ) {
    1377             $r['user-agent'] = $r['headers']['User-Agent'];
    1378             unset( $r['headers']['User-Agent'] );
    1379         } elseif ( isset( $r['headers']['user-agent'] ) ) {
    1380             $r['user-agent'] = $r['headers']['user-agent'];
    1381             unset( $r['headers']['user-agent'] );
    1382         }
    1383 
    1384         // Construct Cookie: header if any cookies are set.
    1385         WP_Http::buildCookieHeader( $r );
    1386 
    1387         $handle = curl_init();
    1388 
    1389         // cURL offers really easy proxy support.
    1390         $proxy = new WP_HTTP_Proxy();
    1391 
    1392         if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
    1393 
    1394             curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
    1395             curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() );
    1396             curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() );
    1397 
    1398             if ( $proxy->use_authentication() ) {
    1399                 curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
    1400                 curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
    1401             }
    1402         }
    1403 
    1404         $is_local = isset($r['local']) && $r['local'];
    1405         $ssl_verify = isset($r['sslverify']) && $r['sslverify'];
    1406         if ( $is_local ) {
    1407             /** This filter is documented in wp-includes/class-http.php */
    1408             $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify );
    1409         } elseif ( ! $is_local ) {
    1410             /** This filter is documented in wp-includes/class-http.php */
    1411             $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify );
    1412         }
    1413 
    1414         /*
    1415          * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since.
    1416          * a value of 0 will allow an unlimited timeout.
    1417          */
    1418         $timeout = (int) ceil( $r['timeout'] );
    1419         curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout );
    1420         curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout );
    1421 
    1422         curl_setopt( $handle, CURLOPT_URL, $url);
    1423         curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
    1424         curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false );
    1425         curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
    1426         curl_setopt( $handle, CURLOPT_CAINFO, $r['sslcertificates'] );
    1427         curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
    1428 
    1429         /*
    1430          * The option doesn't work with safe mode or when open_basedir is set, and there's
    1431          * a bug #17490 with redirected POST requests, so handle redirections outside Curl.
    1432          */
    1433         curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false );
    1434         if ( defined( 'CURLOPT_PROTOCOLS' ) ) // PHP 5.2.10 / cURL 7.19.4
    1435             curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
    1436 
    1437         switch ( $r['method'] ) {
    1438             case 'HEAD':
    1439                 curl_setopt( $handle, CURLOPT_NOBODY, true );
    1440                 break;
    1441             case 'POST':
    1442                 curl_setopt( $handle, CURLOPT_POST, true );
    1443                 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
    1444                 break;
    1445             case 'PUT':
    1446                 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
    1447                 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
    1448                 break;
    1449             default:
    1450                 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] );
    1451                 if ( ! is_null( $r['body'] ) )
    1452                     curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
    1453                 break;
    1454         }
    1455 
    1456         if ( true === $r['blocking'] ) {
    1457             curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) );
    1458             curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) );
    1459         }
    1460 
    1461         curl_setopt( $handle, CURLOPT_HEADER, false );
    1462 
    1463         if ( isset( $r['limit_response_size'] ) )
    1464             $this->max_body_length = intval( $r['limit_response_size'] );
    1465         else
    1466             $this->max_body_length = false;
    1467 
    1468         // If streaming to a file open a file handle, and setup our curl streaming handler.
    1469         if ( $r['stream'] ) {
    1470             if ( ! WP_DEBUG )
    1471                 $this->stream_handle = @fopen( $r['filename'], 'w+' );
    1472             else
    1473                 $this->stream_handle = fopen( $r['filename'], 'w+' );
    1474             if ( ! $this->stream_handle )
    1475                 return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
    1476         } else {
    1477             $this->stream_handle = false;
    1478         }
    1479 
    1480         if ( !empty( $r['headers'] ) ) {
    1481             // cURL expects full header strings in each element.
    1482             $headers = array();
    1483             foreach ( $r['headers'] as $name => $value ) {
    1484                 $headers[] = "{$name}: $value";
    1485             }
    1486             curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
    1487         }
    1488 
    1489         if ( $r['httpversion'] == '1.0' )
    1490             curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
    1491         else
    1492             curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
    1493 
    1494         /**
    1495          * Fires before the cURL request is executed.
    1496          *
    1497          * Cookies are not currently handled by the HTTP API. This action allows
    1498          * plugins to handle cookies themselves.
    1499          *
    1500          * @since 2.8.0
    1501          *
    1502          * @param resource &$handle The cURL handle returned by curl_init().
    1503          * @param array    $r       The HTTP request arguments.
    1504          * @param string   $url     The request URL.
    1505          */
    1506         do_action_ref_array( 'http_api_curl', array( &$handle, $r, $url ) );
    1507 
    1508         // We don't need to return the body, so don't. Just execute request and return.
    1509         if ( ! $r['blocking'] ) {
    1510             curl_exec( $handle );
    1511 
    1512             if ( $curl_error = curl_error( $handle ) ) {
    1513                 curl_close( $handle );
    1514                 return new WP_Error( 'http_request_failed', $curl_error );
    1515             }
    1516             if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) {
    1517                 curl_close( $handle );
    1518                 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
    1519             }
    1520 
    1521             curl_close( $handle );
    1522             return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    1523         }
    1524 
    1525         curl_exec( $handle );
    1526         $theHeaders = WP_Http::processHeaders( $this->headers, $url );
    1527         $theBody = $this->body;
    1528         $bytes_written_total = $this->bytes_written_total;
    1529 
    1530         $this->headers = '';
    1531         $this->body = '';
    1532         $this->bytes_written_total = 0;
    1533 
    1534         $curl_error = curl_errno( $handle );
    1535 
    1536         // If an error occurred, or, no response.
    1537         if ( $curl_error || ( 0 == strlen( $theBody ) && empty( $theHeaders['headers'] ) ) ) {
    1538             if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error ) {
    1539                 if ( ! $this->max_body_length || $this->max_body_length != $bytes_written_total ) {
    1540                     if ( $r['stream'] ) {
    1541                         curl_close( $handle );
    1542                         fclose( $this->stream_handle );
    1543                         return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) );
    1544                     } else {
    1545                         curl_close( $handle );
    1546                         return new WP_Error( 'http_request_failed', curl_error( $handle ) );
    1547                     }
    1548                 }
    1549             } else {
    1550                 if ( $curl_error = curl_error( $handle ) ) {
    1551                     curl_close( $handle );
    1552                     return new WP_Error( 'http_request_failed', $curl_error );
    1553                 }
    1554             }
    1555             if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) {
    1556                 curl_close( $handle );
    1557                 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
    1558             }
    1559         }
    1560 
    1561         curl_close( $handle );
    1562 
    1563         if ( $r['stream'] )
    1564             fclose( $this->stream_handle );
    1565 
    1566         $response = array(
    1567             'headers' => $theHeaders['headers'],
    1568             'body' => null,
    1569             'response' => $theHeaders['response'],
    1570             'cookies' => $theHeaders['cookies'],
    1571             'filename' => $r['filename']
    1572         );
    1573 
    1574         // Handle redirects.
    1575         if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) )
    1576             return $redirect_response;
    1577 
    1578         if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )
    1579             $theBody = WP_Http_Encoding::decompress( $theBody );
    1580 
    1581         $response['body'] = $theBody;
    1582 
    1583         return $response;
    1584     }
    1585 
    1586     /**
    1587      * Grab the headers of the cURL request
    1588      *
    1589      * Each header is sent individually to this callback, so we append to the $header property for temporary storage
    1590      *
    1591      * @since 3.2.0
    1592      * @access private
    1593      * @return int
    1594      */
    1595     private function stream_headers( $handle, $headers ) {
    1596         $this->headers .= $headers;
    1597         return strlen( $headers );
    1598     }
    1599 
    1600     /**
    1601      * Grab the body of the cURL request
    1602      *
    1603      * The contents of the document are passed in chunks, so we append to the $body property for temporary storage.
    1604      * Returning a length shorter than the length of $data passed in will cause cURL to abort the request with CURLE_WRITE_ERROR
    1605      *
    1606      * @since 3.6.0
    1607      * @access private
    1608      * @return int
    1609      */
    1610     private function stream_body( $handle, $data ) {
    1611         $data_length = strlen( $data );
    1612 
    1613         if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) {
    1614             $data_length = ( $this->max_body_length - $this->bytes_written_total );
    1615             $data = substr( $data, 0, $data_length );
    1616         }
    1617 
    1618         if ( $this->stream_handle ) {
    1619             $bytes_written = fwrite( $this->stream_handle, $data );
    1620         } else {
    1621             $this->body .= $data;
    1622             $bytes_written = $data_length;
    1623         }
    1624 
    1625         $this->bytes_written_total += $bytes_written;
    1626 
    1627         // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR.
    1628         return $bytes_written;
    1629     }
    1630 
    1631     /**
    1632      * Whether this class can be used for retrieving an URL.
    1633      *
    1634      * @static
    1635      * @since 2.7.0
    1636      *
    1637      * @return bool False means this class can not be used, true means it can.
    1638      */
    1639     public static function test( $args = array() ) {
    1640         if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) )
    1641             return false;
    1642 
    1643         $is_ssl = isset( $args['ssl'] ) && $args['ssl'];
    1644 
    1645         if ( $is_ssl ) {
    1646             $curl_version = curl_version();
    1647             // Check whether this cURL version support SSL requests.
    1648             if ( ! (CURL_VERSION_SSL & $curl_version['features']) )
    1649                 return false;
    1650         }
    1651 
    1652         /**
    1653          * Filter whether cURL can be used as a transport for retrieving a URL.
    1654          *
    1655          * @since 2.7.0
    1656          *
    1657          * @param bool  $use_class Whether the class can be used. Default true.
    1658          * @param array $args      An array of request arguments.
    1659          */
    1660         return apply_filters( 'use_curl_transport', true, $args );
    1661     }
    1662 }
    1663 
    1664 /**
    1665  * Adds Proxy support to the WordPress HTTP API.
    1666  *
    1667  * There are caveats to proxy support. It requires that defines be made in the wp-config.php file to
    1668  * enable proxy support. There are also a few filters that plugins can hook into for some of the
    1669  * constants.
    1670  *
    1671  * Please note that only BASIC authentication is supported by most transports.
    1672  * cURL MAY support more methods (such as NTLM authentication) depending on your environment.
    1673  *
    1674  * The constants are as follows:
    1675  * <ol>
    1676  * <li>WP_PROXY_HOST - Enable proxy support and host for connecting.</li>
    1677  * <li>WP_PROXY_PORT - Proxy port for connection. No default, must be defined.</li>
    1678  * <li>WP_PROXY_USERNAME - Proxy username, if it requires authentication.</li>
    1679  * <li>WP_PROXY_PASSWORD - Proxy password, if it requires authentication.</li>
    1680  * <li>WP_PROXY_BYPASS_HOSTS - Will prevent the hosts in this list from going through the proxy.
    1681  * You do not need to have localhost and the blog host in this list, because they will not be passed
    1682  * through the proxy. The list should be presented in a comma separated list, wildcards using * are supported, eg. *.wordpress.org</li>
    1683  * </ol>
    1684  *
    1685  * An example can be as seen below.
    1686  *
    1687  *     define('WP_PROXY_HOST', '192.168.84.101');
    1688  *     define('WP_PROXY_PORT', '8080');
    1689  *     define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com, *.wordpress.org');
    1690  *
    1691  * @link https://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress.
    1692  * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_PROXY_BYPASS_HOSTS
    1693  * @since 2.8.0
    1694  */
    1695 class WP_HTTP_Proxy {
    1696 
    1697     /**
    1698      * Whether proxy connection should be used.
    1699      *
    1700      * @since 2.8.0
    1701      *
    1702      * @use WP_PROXY_HOST
    1703      * @use WP_PROXY_PORT
    1704      *
    1705      * @return bool
    1706      */
    1707     public function is_enabled() {
    1708         return defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT');
    1709     }
    1710 
    1711     /**
    1712      * Whether authentication should be used.
    1713      *
    1714      * @since 2.8.0
    1715      *
    1716      * @use WP_PROXY_USERNAME
    1717      * @use WP_PROXY_PASSWORD
    1718      *
    1719      * @return bool
    1720      */
    1721     public function use_authentication() {
    1722         return defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD');
    1723     }
    1724 
    1725     /**
    1726      * Retrieve the host for the proxy server.
    1727      *
    1728      * @since 2.8.0
    1729      *
    1730      * @return string
    1731      */
    1732     public function host() {
    1733         if ( defined('WP_PROXY_HOST') )
    1734             return WP_PROXY_HOST;
    1735 
    1736         return '';
    1737     }
    1738 
    1739     /**
    1740      * Retrieve the port for the proxy server.
    1741      *
    1742      * @since 2.8.0
    1743      *
    1744      * @return string
    1745      */
    1746     public function port() {
    1747         if ( defined('WP_PROXY_PORT') )
    1748             return WP_PROXY_PORT;
    1749 
    1750         return '';
    1751     }
    1752 
    1753     /**
    1754      * Retrieve the username for proxy authentication.
    1755      *
    1756      * @since 2.8.0
    1757      *
    1758      * @return string
    1759      */
    1760     public function username() {
    1761         if ( defined('WP_PROXY_USERNAME') )
    1762             return WP_PROXY_USERNAME;
    1763 
    1764         return '';
    1765     }
    1766 
    1767     /**
    1768      * Retrieve the password for proxy authentication.
    1769      *
    1770      * @since 2.8.0
    1771      *
    1772      * @return string
    1773      */
    1774     public function password() {
    1775         if ( defined('WP_PROXY_PASSWORD') )
    1776             return WP_PROXY_PASSWORD;
    1777 
    1778         return '';
    1779     }
    1780 
    1781     /**
    1782      * Retrieve authentication string for proxy authentication.
    1783      *
    1784      * @since 2.8.0
    1785      *
    1786      * @return string
    1787      */
    1788     public function authentication() {
    1789         return $this->username() . ':' . $this->password();
    1790     }
    1791 
    1792     /**
    1793      * Retrieve header string for proxy authentication.
    1794      *
    1795      * @since 2.8.0
    1796      *
    1797      * @return string
    1798      */
    1799     public function authentication_header() {
    1800         return 'Proxy-Authorization: Basic ' . base64_encode( $this->authentication() );
    1801     }
    1802 
    1803     /**
    1804      * Whether URL should be sent through the proxy server.
    1805      *
    1806      * We want to keep localhost and the blog URL from being sent through the proxy server, because
    1807      * some proxies can not handle this. We also have the constant available for defining other
    1808      * hosts that won't be sent through the proxy.
    1809      *
    1810      * @since 2.8.0
    1811      *
    1812      * @staticvar array|null $bypass_hosts
    1813      * @staticvar array      $wildcard_regex
    1814      *
    1815      * @param string $uri URI to check.
    1816      * @return bool True, to send through the proxy and false if, the proxy should not be used.
    1817      */
    1818     public function send_through_proxy( $uri ) {
    1819         /*
    1820          * parse_url() only handles http, https type URLs, and will emit E_WARNING on failure.
    1821          * This will be displayed on blogs, which is not reasonable.
    1822          */
    1823         $check = @parse_url($uri);
    1824 
    1825         // Malformed URL, can not process, but this could mean ssl, so let through anyway.
    1826         if ( $check === false )
    1827             return true;
    1828 
    1829         $home = parse_url( get_option('siteurl') );
    1830 
    1831         /**
    1832          * Filter whether to preempt sending the request through the proxy server.
    1833          *
    1834          * Returning false will bypass the proxy; returning true will send
    1835          * the request through the proxy. Returning null bypasses the filter.
    1836          *
    1837          * @since 3.5.0
    1838          *
    1839          * @param null   $override Whether to override the request result. Default null.
    1840          * @param string $uri      URL to check.
    1841          * @param array  $check    Associative array result of parsing the URI.
    1842          * @param array  $home     Associative array result of parsing the site URL.
    1843          */
    1844         $result = apply_filters( 'pre_http_send_through_proxy', null, $uri, $check, $home );
    1845         if ( ! is_null( $result ) )
    1846             return $result;
    1847 
    1848         if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) )
    1849             return false;
    1850 
    1851         if ( !defined('WP_PROXY_BYPASS_HOSTS') )
    1852             return true;
    1853 
    1854         static $bypass_hosts = null;
    1855         static $wildcard_regex = array();
    1856         if ( null === $bypass_hosts ) {
    1857             $bypass_hosts = preg_split('|,\s*|', WP_PROXY_BYPASS_HOSTS);
    1858 
    1859             if ( false !== strpos(WP_PROXY_BYPASS_HOSTS, '*') ) {
    1860                 $wildcard_regex = array();
    1861                 foreach ( $bypass_hosts as $host )
    1862                     $wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) );
    1863                 $wildcard_regex = '/^(' . implode('|', $wildcard_regex) . ')$/i';
    1864             }
    1865         }
    1866 
    1867         if ( !empty($wildcard_regex) )
    1868             return !preg_match($wildcard_regex, $check['host']);
    1869         else
    1870             return !in_array( $check['host'], $bypass_hosts );
    1871     }
    1872 }
    1873 /**
    1874  * Internal representation of a single cookie.
    1875  *
    1876  * Returned cookies are represented using this class, and when cookies are set, if they are not
    1877  * already a WP_Http_Cookie() object, then they are turned into one.
    1878  *
    1879  * @todo The WordPress convention is to use underscores instead of camelCase for function and method
    1880  * names. Need to switch to use underscores instead for the methods.
    1881  *
    1882  * @package WordPress
    1883  * @subpackage HTTP
    1884  * @since 2.8.0
    1885  */
    1886 class WP_Http_Cookie {
    1887 
    1888     /**
    1889      * Cookie name.
    1890      *
    1891      * @since 2.8.0
    1892      * @var string
    1893      */
    1894     public $name;
    1895 
    1896     /**
    1897      * Cookie value.
    1898      *
    1899      * @since 2.8.0
    1900      * @var string
    1901      */
    1902     public $value;
    1903 
    1904     /**
    1905      * When the cookie expires.
    1906      *
    1907      * @since 2.8.0
    1908      * @var string
    1909      */
    1910     public $expires;
    1911 
    1912     /**
    1913      * Cookie URL path.
    1914      *
    1915      * @since 2.8.0
    1916      * @var string
    1917      */
    1918     public $path;
    1919 
    1920     /**
    1921      * Cookie Domain.
    1922      *
    1923      * @since 2.8.0
    1924      * @var string
    1925      */
    1926     public $domain;
    1927 
    1928     /**
    1929      * Sets up this cookie object.
    1930      *
    1931      * The parameter $data should be either an associative array containing the indices names below
    1932      * or a header string detailing it.
    1933      *
    1934      * @since 2.8.0
    1935      * @access public
    1936      *
    1937      * @param string|array $data {
    1938      *     Raw cookie data as header string or data array.
    1939      *
    1940      *     @type string     $name    Cookie name.
    1941      *     @type mixed      $value   Value. Should NOT already be urlencoded.
    1942      *     @type string|int $expires Optional. Unix timestamp or formatted date. Default null.
    1943      *     @type string     $path    Optional. Path. Default '/'.
    1944      *     @type string     $domain  Optional. Domain. Default host of parsed $requested_url.
    1945      *     @type int        $port    Optional. Port. Default null.
    1946      * }
    1947      * @param string       $requested_url The URL which the cookie was set on, used for default $domain
    1948      *                                    and $port values.
    1949      */
    1950     public function __construct( $data, $requested_url = '' ) {
    1951         if ( $requested_url )
    1952             $arrURL = @parse_url( $requested_url );
    1953         if ( isset( $arrURL['host'] ) )
    1954             $this->domain = $arrURL['host'];
    1955         $this->path = isset( $arrURL['path'] ) ? $arrURL['path'] : '/';
    1956         if (  '/' != substr( $this->path, -1 ) )
    1957             $this->path = dirname( $this->path ) . '/';
    1958 
    1959         if ( is_string( $data ) ) {
    1960             // Assume it's a header string direct from a previous request.
    1961             $pairs = explode( ';', $data );
    1962 
    1963             // Special handling for first pair; name=value. Also be careful of "=" in value.
    1964             $name  = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
    1965             $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
    1966             $this->name  = $name;
    1967             $this->value = urldecode( $value );
    1968 
    1969             // Removes name=value from items.
    1970             array_shift( $pairs );
    1971 
    1972             // Set everything else as a property.
    1973             foreach ( $pairs as $pair ) {
    1974                 $pair = rtrim($pair);
    1975 
    1976                 // Handle the cookie ending in ; which results in a empty final pair.
    1977                 if ( empty($pair) )
    1978                     continue;
    1979 
    1980                 list( $key, $val ) = strpos( $pair, '=' ) ? explode( '=', $pair ) : array( $pair, '' );
    1981                 $key = strtolower( trim( $key ) );
    1982                 if ( 'expires' == $key )
    1983                     $val = strtotime( $val );
    1984                 $this->$key = $val;
    1985             }
    1986         } else {
    1987             if ( !isset( $data['name'] ) )
    1988                 return;
    1989 
    1990             // Set properties based directly on parameters.
    1991             foreach ( array( 'name', 'value', 'path', 'domain', 'port' ) as $field ) {
    1992                 if ( isset( $data[ $field ] ) )
    1993                     $this->$field = $data[ $field ];
    1994             }
    1995 
    1996             if ( isset( $data['expires'] ) )
    1997                 $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
    1998             else
    1999                 $this->expires = null;
    2000         }
    2001     }
    2002 
    2003     /**
    2004      * Confirms that it's OK to send this cookie to the URL checked against.
    2005      *
    2006      * Decision is based on RFC 2109/2965, so look there for details on validity.
    2007      *
    2008      * @access public
    2009      * @since 2.8.0
    2010      *
    2011      * @param string $url URL you intend to send this cookie to
    2012      * @return bool true if allowed, false otherwise.
    2013      */
    2014     public function test( $url ) {
    2015         if ( is_null( $this->name ) )
    2016             return false;
    2017 
    2018         // Expires - if expired then nothing else matters.
    2019         if ( isset( $this->expires ) && time() > $this->expires )
    2020             return false;
    2021 
    2022         // Get details on the URL we're thinking about sending to.
    2023         $url = parse_url( $url );
    2024         $url['port'] = isset( $url['port'] ) ? $url['port'] : ( 'https' == $url['scheme'] ? 443 : 80 );
    2025         $url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
    2026 
    2027         // Values to use for comparison against the URL.
    2028         $path   = isset( $this->path )   ? $this->path   : '/';
    2029         $port   = isset( $this->port )   ? $this->port   : null;
    2030         $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
    2031         if ( false === stripos( $domain, '.' ) )
    2032             $domain .= '.local';
    2033 
    2034         // Host - very basic check that the request URL ends with the domain restriction (minus leading dot).
    2035         $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
    2036         if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
    2037             return false;
    2038 
    2039         // Port - supports "port-lists" in the format: "80,8000,8080".
    2040         if ( !empty( $port ) && !in_array( $url['port'], explode( ',', $port) ) )
    2041             return false;
    2042 
    2043         // Path - request path must start with path restriction.
    2044         if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
    2045             return false;
    2046 
    2047         return true;
    2048     }
    2049 
    2050     /**
    2051      * Convert cookie name and value back to header string.
    2052      *
    2053      * @access public
    2054      * @since 2.8.0
    2055      *
    2056      * @return string Header encoded cookie name and value.
    2057      */
    2058     public function getHeaderValue() {
    2059         if ( ! isset( $this->name ) || ! isset( $this->value ) )
    2060             return '';
    2061 
    2062         /**
    2063          * Filter the header-encoded cookie value.
    2064          *
    2065          * @since 3.4.0
    2066          *
    2067          * @param string $value The cookie value.
    2068          * @param string $name  The cookie name.
    2069          */
    2070         return $this->name . '=' . apply_filters( 'wp_http_cookie_value', $this->value, $this->name );
    2071     }
    2072 
    2073     /**
    2074      * Retrieve cookie header for usage in the rest of the WordPress HTTP API.
    2075      *
    2076      * @access public
    2077      * @since 2.8.0
    2078      *
    2079      * @return string
    2080      */
    2081     public function getFullHeader() {
    2082         return 'Cookie: ' . $this->getHeaderValue();
    2083     }
    2084 }
    2085 
    2086 /**
    2087  * Implementation for deflate and gzip transfer encodings.
    2088  *
    2089  * Includes RFC 1950, RFC 1951, and RFC 1952.
    2090  *
    2091  * @since 2.8.0
    2092  * @package WordPress
    2093  * @subpackage HTTP
    2094  */
    2095 class WP_Http_Encoding {
    2096 
    2097     /**
    2098      * Compress raw string using the deflate format.
    2099      *
    2100      * Supports the RFC 1951 standard.
    2101      *
    2102      * @since 2.8.0
    2103      *
    2104      * @static
    2105      *
    2106      * @param string $raw String to compress.
    2107      * @param int $level Optional, default is 9. Compression level, 9 is highest.
    2108      * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports.
    2109      * @return string|false False on failure.
    2110      */
    2111     public static function compress( $raw, $level = 9, $supports = null ) {
    2112         return gzdeflate( $raw, $level );
    2113     }
    2114 
    2115     /**
    2116      * Decompression of deflated string.
    2117      *
    2118      * Will attempt to decompress using the RFC 1950 standard, and if that fails
    2119      * then the RFC 1951 standard deflate will be attempted. Finally, the RFC
    2120      * 1952 standard gzip decode will be attempted. If all fail, then the
    2121      * original compressed string will be returned.
    2122      *
    2123      * @since 2.8.0
    2124      *
    2125      * @static
    2126      *
    2127      * @param string $compressed String to decompress.
    2128      * @param int $length The optional length of the compressed data.
    2129      * @return string|bool False on failure.
    2130      */
    2131     public static function decompress( $compressed, $length = null ) {
    2132 
    2133         if ( empty($compressed) )
    2134             return $compressed;
    2135 
    2136         if ( false !== ( $decompressed = @gzinflate( $compressed ) ) )
    2137             return $decompressed;
    2138 
    2139         if ( false !== ( $decompressed = self::compatible_gzinflate( $compressed ) ) )
    2140             return $decompressed;
    2141 
    2142         if ( false !== ( $decompressed = @gzuncompress( $compressed ) ) )
    2143             return $decompressed;
    2144 
    2145         if ( function_exists('gzdecode') ) {
    2146             $decompressed = @gzdecode( $compressed );
    2147 
    2148             if ( false !== $decompressed )
    2149                 return $decompressed;
    2150         }
    2151 
    2152         return $compressed;
    2153     }
    2154 
    2155     /**
    2156      * Decompression of deflated string while staying compatible with the majority of servers.
    2157      *
    2158      * Certain Servers will return deflated data with headers which PHP's gzinflate()
    2159      * function cannot handle out of the box. The following function has been created from
    2160      * various snippets on the gzinflate() PHP documentation.
    2161      *
    2162      * Warning: Magic numbers within. Due to the potential different formats that the compressed
    2163      * data may be returned in, some "magic offsets" are needed to ensure proper decompression
    2164      * takes place. For a simple progmatic way to determine the magic offset in use, see:
    2165      * https://core.trac.wordpress.org/ticket/18273
    2166      *
    2167      * @since 2.8.1
    2168      * @link https://core.trac.wordpress.org/ticket/18273
    2169      * @link http://au2.php.net/manual/en/function.gzinflate.php#70875
    2170      * @link http://au2.php.net/manual/en/function.gzinflate.php#77336
    2171      *
    2172      * @static
    2173      *
    2174      * @param string $gzData String to decompress.
    2175      * @return string|bool False on failure.
    2176      */
    2177     public static function compatible_gzinflate($gzData) {
    2178 
    2179         // Compressed data might contain a full header, if so strip it for gzinflate().
    2180         if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) {
    2181             $i = 10;
    2182             $flg = ord( substr($gzData, 3, 1) );
    2183             if ( $flg > 0 ) {
    2184                 if ( $flg & 4 ) {
    2185                     list($xlen) = unpack('v', substr($gzData, $i, 2) );
    2186                     $i = $i + 2 + $xlen;
    2187                 }
    2188                 if ( $flg & 8 )
    2189                     $i = strpos($gzData, "\0", $i) + 1;
    2190                 if ( $flg & 16 )
    2191                     $i = strpos($gzData, "\0", $i) + 1;
    2192                 if ( $flg & 2 )
    2193                     $i = $i + 2;
    2194             }
    2195             $decompressed = @gzinflate( substr($gzData, $i, -8) );
    2196             if ( false !== $decompressed )
    2197                 return $decompressed;
    2198         }
    2199 
    2200         // Compressed data from java.util.zip.Deflater amongst others.
    2201         $decompressed = @gzinflate( substr($gzData, 2) );
    2202         if ( false !== $decompressed )
    2203             return $decompressed;
    2204 
    2205         return false;
    2206     }
    2207 
    2208     /**
    2209      * What encoding types to accept and their priority values.
    2210      *
    2211      * @since 2.8.0
    2212      *
    2213      * @static
    2214      *
    2215      * @param string $url
    2216      * @param array  $args
    2217      * @return string Types of encoding to accept.
    2218      */
    2219     public static function accept_encoding( $url, $args ) {
    2220         $type = array();
    2221         $compression_enabled = self::is_available();
    2222 
    2223         if ( ! $args['decompress'] ) // Decompression specifically disabled.
    2224             $compression_enabled = false;
    2225         elseif ( $args['stream'] ) // Disable when streaming to file.
    2226             $compression_enabled = false;
    2227         elseif ( isset( $args['limit_response_size'] ) ) // If only partial content is being requested, we won't be able to decompress it.
    2228             $compression_enabled = false;
    2229 
    2230         if ( $compression_enabled ) {
    2231             if ( function_exists( 'gzinflate' ) )
    2232                 $type[] = 'deflate;q=1.0';
    2233 
    2234             if ( function_exists( 'gzuncompress' ) )
    2235                 $type[] = 'compress;q=0.5';
    2236 
    2237             if ( function_exists( 'gzdecode' ) )
    2238                 $type[] = 'gzip;q=0.5';
    2239         }
    2240 
    2241         /**
    2242          * Filter the allowed encoding types.
    2243          *
    2244          * @since 3.6.0
    2245          *
    2246          * @param array  $type Encoding types allowed. Accepts 'gzinflate',
    2247          *                     'gzuncompress', 'gzdecode'.
    2248          * @param string $url  URL of the HTTP request.
    2249          * @param array  $args HTTP request arguments.
    2250          */
    2251         $type = apply_filters( 'wp_http_accept_encoding', $type, $url, $args );
    2252 
    2253         return implode(', ', $type);
    2254     }
    2255 
    2256     /**
    2257      * What encoding the content used when it was compressed to send in the headers.
    2258      *
    2259      * @since 2.8.0
    2260      *
    2261      * @static
    2262      *
    2263      * @return string Content-Encoding string to send in the header.
    2264      */
    2265     public static function content_encoding() {
    2266         return 'deflate';
    2267     }
    2268 
    2269     /**
    2270      * Whether the content be decoded based on the headers.
    2271      *
    2272      * @since 2.8.0
    2273      *
    2274      * @static
    2275      *
    2276      * @param array|string $headers All of the available headers.
    2277      * @return bool
    2278      */
    2279     public static function should_decode($headers) {
    2280         if ( is_array( $headers ) ) {
    2281             if ( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) )
    2282                 return true;
    2283         } elseif ( is_string( $headers ) ) {
    2284             return ( stripos($headers, 'content-encoding:') !== false );
    2285         }
    2286 
    2287         return false;
    2288     }
    2289 
    2290     /**
    2291      * Whether decompression and compression are supported by the PHP version.
    2292      *
    2293      * Each function is tested instead of checking for the zlib extension, to
    2294      * ensure that the functions all exist in the PHP version and aren't
    2295      * disabled.
    2296      *
    2297      * @since 2.8.0
    2298      *
    2299      * @static
    2300      *
    2301      * @return bool
    2302      */
    2303     public static function is_available() {
    2304         return ( function_exists('gzuncompress') || function_exists('gzdeflate') || function_exists('gzinflate') );
    2305     }
    2306 }
Note: See TracChangeset for help on using the changeset viewer.