Make WordPress Core

Changeset 13274


Ignore:
Timestamp:
02/21/2010 02:06:24 AM (17 years ago)
Author:
dd32
Message:

Split WP_Http classes into separate file. Fixes #11559

Location:
trunk/wp-includes
Files:
1 added
1 edited

Legend:

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

    r13268 r13274  
    1414
    1515/**
    16  * WordPress HTTP Class for managing HTTP Transports and making HTTP requests.
    17  *
    18  * This class is called for the functionality of making HTTP requests and should replace Snoopy
    19  * functionality, eventually. There is no available functionality to add HTTP transport
    20  * implementations, since most of the HTTP transports are added and available for use.
    21  *
    22  * The exception is that cURL is not available as a transport and lacking an implementation. It will
    23  * be added later and should be a patch on the WordPress Trac.
    24  *
    25  * There are no properties, because none are needed and for performance reasons. Some of the
    26  * functions are static and while they do have some overhead over functions in PHP4, the purpose is
    27  * maintainability. When PHP5 is finally the requirement, it will be easy to add the static keyword
    28  * to the code. It is not as easy to convert a function to a method after enough code uses the old
    29  * way.
    30  *
    31  * Debugging includes several actions, which pass different variables for debugging the HTTP API.
    32  *
    33  * <strong>http_transport_get_debug</strong> - gives working, nonblocking, and blocking transports.
    34  *
    35  * <strong>http_transport_post_debug</strong> - gives working, nonblocking, and blocking transports.
    36  *
    37  * @package WordPress
    38  * @subpackage HTTP
    39  * @since 2.7.0
    40  */
    41 class WP_Http {
    42 
    43         /**
    44          * PHP4 style Constructor - Calls PHP5 Style Constructor
    45          *
    46          * @since 2.7.0
    47          * @return WP_Http
    48          */
    49         function WP_Http() {
    50                 $this->__construct();
    51         }
    52 
    53         /**
    54          * PHP5 style Constructor - Setup available transport if not available.
    55          *
    56          * PHP4 does not have the 'self' keyword and since WordPress supports PHP4,
    57          * the class needs to be used for the static call.
    58          *
    59          * The transport are setup to save time. This should only be called once, so
    60          * the overhead should be fine.
    61          *
    62          * @since 2.7.0
    63          * @return WP_Http
    64          */
    65         function __construct() {
    66                 WP_Http::_getTransport();
    67                 WP_Http::_postTransport();
    68         }
    69 
    70         /**
    71          * Tests the WordPress HTTP objects for an object to use and returns it.
    72          *
    73          * Tests all of the objects and returns the object that passes. Also caches
    74          * that object to be used later.
    75          *
    76          * The order for the GET/HEAD requests are HTTP Extension, cURL, Streams, Fopen,
    77          * and finally Fsockopen. fsockopen() is used last, because it has the most
    78          * overhead in its implementation. There isn't any real way around it, since
    79          * redirects have to be supported, much the same way the other transports
    80          * also handle redirects.
    81          *
    82          * There are currently issues with "localhost" not resolving correctly with
    83          * DNS. This may cause an error "failed to open stream: A connection attempt
    84          * failed because the connected party did not properly respond after a
    85          * period of time, or established connection failed because connected host
    86          * has failed to respond."
    87          *
    88          * @since 2.7.0
    89          * @access private
    90          *
    91          * @param array $args Request args, default us an empty array
    92          * @return object|null Null if no transports are available, HTTP transport object.
    93          */
    94         function &_getTransport( $args = array() ) {
    95                 static $working_transport, $blocking_transport, $nonblocking_transport;
    96 
    97                 if ( is_null($working_transport) ) {
    98                         if ( true === WP_Http_ExtHttp::test($args) ) {
    99                                 $working_transport['exthttp'] = new WP_Http_ExtHttp();
    100                                 $blocking_transport[] = &$working_transport['exthttp'];
    101                         } else if ( true === WP_Http_Curl::test($args) ) {
    102                                 $working_transport['curl'] = new WP_Http_Curl();
    103                                 $blocking_transport[] = &$working_transport['curl'];
    104                         } else if ( true === WP_Http_Streams::test($args) ) {
    105                                 $working_transport['streams'] = new WP_Http_Streams();
    106                                 $blocking_transport[] = &$working_transport['streams'];
    107                         } else if ( true === WP_Http_Fopen::test($args) ) {
    108                                 $working_transport['fopen'] = new WP_Http_Fopen();
    109                                 $blocking_transport[] = &$working_transport['fopen'];
    110                         } else if ( true === WP_Http_Fsockopen::test($args) ) {
    111                                 $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    112                                 $blocking_transport[] = &$working_transport['fsockopen'];
    113                         }
    114 
    115                         foreach ( array('curl', 'streams', 'fopen', 'fsockopen', 'exthttp') as $transport ) {
    116                                 if ( isset($working_transport[$transport]) )
    117                                         $nonblocking_transport[] = &$working_transport[$transport];
    118                         }
    119                 }
    120 
    121                 do_action( 'http_transport_get_debug', $working_transport, $blocking_transport, $nonblocking_transport );
    122 
    123                 if ( isset($args['blocking']) && !$args['blocking'] )
    124                         return $nonblocking_transport;
    125                 else
    126                         return $blocking_transport;
    127         }
    128 
    129         /**
    130          * Tests the WordPress HTTP objects for an object to use and returns it.
    131          *
    132          * Tests all of the objects and returns the object that passes. Also caches
    133          * that object to be used later. This is for posting content to a URL and
    134          * is used when there is a body. The plain Fopen Transport can not be used
    135          * to send content, but the streams transport can. This is a limitation that
    136          * is addressed here, by just not including that transport.
    137          *
    138          * @since 2.7.0
    139          * @access private
    140          *
    141          * @param array $args Request args, default us an empty array
    142          * @return object|null Null if no transports are available, HTTP transport object.
    143          */
    144         function &_postTransport( $args = array() ) {
    145                 static $working_transport, $blocking_transport, $nonblocking_transport;
    146 
    147                 if ( is_null($working_transport) ) {
    148                         if ( true === WP_Http_ExtHttp::test($args) ) {
    149                                 $working_transport['exthttp'] = new WP_Http_ExtHttp();
    150                                 $blocking_transport[] = &$working_transport['exthttp'];
    151                         } else if ( true === WP_Http_Curl::test($args) ) {
    152                                 $working_transport['curl'] = new WP_Http_Curl();
    153                                 $blocking_transport[] = &$working_transport['curl'];
    154                         } else if ( true === WP_Http_Streams::test($args) ) {
    155                                 $working_transport['streams'] = new WP_Http_Streams();
    156                                 $blocking_transport[] = &$working_transport['streams'];
    157                         } else if ( true === WP_Http_Fsockopen::test($args) ) {
    158                                 $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    159                                 $blocking_transport[] = &$working_transport['fsockopen'];
    160                         }
    161 
    162                         foreach ( array('curl', 'streams', 'fsockopen', 'exthttp') as $transport ) {
    163                                 if ( isset($working_transport[$transport]) )
    164                                         $nonblocking_transport[] = &$working_transport[$transport];
    165                         }
    166                 }
    167 
    168                 do_action( 'http_transport_post_debug', $working_transport, $blocking_transport, $nonblocking_transport );
    169 
    170                 if ( isset($args['blocking']) && !$args['blocking'] )
    171                         return $nonblocking_transport;
    172                 else
    173                         return $blocking_transport;
    174         }
    175 
    176         /**
    177          * Send a HTTP request to a URI.
    178          *
    179          * The body and headers are part of the arguments. The 'body' argument is for the body and will
    180          * accept either a string or an array. The 'headers' argument should be an array, but a string
    181          * is acceptable. If the 'body' argument is an array, then it will automatically be escaped
    182          * using http_build_query().
    183          *
    184          * The only URI that are supported in the HTTP Transport implementation are the HTTP and HTTPS
    185          * protocols. HTTP and HTTPS are assumed so the server might not know how to handle the send
    186          * headers. Other protocols are unsupported and most likely will fail.
    187          *
    188          * The defaults are 'method', 'timeout', 'redirection', 'httpversion', 'blocking' and
    189          * 'user-agent'.
    190          *
    191          * Accepted 'method' values are 'GET', 'POST', and 'HEAD', some transports technically allow
    192          * others, but should not be assumed. The 'timeout' is used to sent how long the connection
    193          * should stay open before failing when no response. 'redirection' is used to track how many
    194          * redirects were taken and used to sent the amount for other transports, but not all transports
    195          * accept setting that value.
    196          *
    197          * The 'httpversion' option is used to sent the HTTP version and accepted values are '1.0', and
    198          * '1.1' and should be a string. Version 1.1 is not supported, because of chunk response. The
    199          * 'user-agent' option is the user-agent and is used to replace the default user-agent, which is
    200          * 'WordPress/WP_Version', where WP_Version is the value from $wp_version.
    201          *
    202          * 'blocking' is the default, which is used to tell the transport, whether it should halt PHP
    203          * while it performs the request or continue regardless. Actually, that isn't entirely correct.
    204          * Blocking mode really just means whether the fread should just pull what it can whenever it
    205          * gets bytes or if it should wait until it has enough in the buffer to read or finishes reading
    206          * the entire content. It doesn't actually always mean that PHP will continue going after making
    207          * the request.
    208          *
    209          * @access public
    210          * @since 2.7.0
    211          * @todo Refactor this code. The code in this method extends the scope of its original purpose
    212          *              and should be refactored to allow for cleaner abstraction and reduce duplication of the
    213          *              code. One suggestion is to create a class specifically for the arguments, however
    214          *              preliminary refactoring to this affect has affect more than just the scope of the
    215          *              arguments. Something to ponder at least.
    216          *
    217          * @param string $url URI resource.
    218          * @param str|array $args Optional. Override the defaults.
    219          * @return array containing 'headers', 'body', 'response', 'cookies'
    220          */
    221         function request( $url, $args = array() ) {
    222                 global $wp_version;
    223 
    224                 $defaults = array(
    225                         'method' => 'GET',
    226                         'timeout' => apply_filters( 'http_request_timeout', 5),
    227                         'redirection' => apply_filters( 'http_request_redirection_count', 5),
    228                         'httpversion' => apply_filters( 'http_request_version', '1.0'),
    229                         'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )  ),
    230                         'blocking' => true,
    231                         'headers' => array(),
    232                         'cookies' => array(),
    233                         'body' => null,
    234                         'compress' => false,
    235                         'decompress' => true,
    236                         'sslverify' => true
    237                 );
    238 
    239                 $r = wp_parse_args( $args, $defaults );
    240                 $r = apply_filters( 'http_request_args', $r, $url );
    241 
    242                 // Allow plugins to short-circuit the request
    243                 $pre = apply_filters( 'pre_http_request', false, $r, $url );
    244                 if ( false !== $pre )
    245                         return $pre;
    246 
    247                 $arrURL = parse_url($url);
    248 
    249                 if ( $this->block_request( $url ) )
    250                         return new WP_Error('http_request_failed', __('User has blocked requests through HTTP.'));
    251 
    252                 // Determine if this is a https call and pass that on to the transport functions
    253                 // so that we can blacklist the transports that do not support ssl verification
    254                 $r['ssl'] = $arrURL['scheme'] == 'https' || $arrURL['scheme'] == 'ssl';
    255 
    256                 // Determine if this request is to OUR install of WordPress
    257                 $homeURL = parse_url(get_bloginfo('url'));
    258                 $r['local'] = $homeURL['host'] == $arrURL['host'] || 'localhost' == $arrURL['host'];
    259                 unset($homeURL);
    260 
    261                 if ( is_null( $r['headers'] ) )
    262                         $r['headers'] = array();
    263 
    264                 if ( ! is_array($r['headers']) ) {
    265                         $processedHeaders = WP_Http::processHeaders($r['headers']);
    266                         $r['headers'] = $processedHeaders['headers'];
    267                 }
    268 
    269                 if ( isset($r['headers']['User-Agent']) ) {
    270                         $r['user-agent'] = $r['headers']['User-Agent'];
    271                         unset($r['headers']['User-Agent']);
    272                 }
    273 
    274                 if ( isset($r['headers']['user-agent']) ) {
    275                         $r['user-agent'] = $r['headers']['user-agent'];
    276                         unset($r['headers']['user-agent']);
    277                 }
    278 
    279                 // Construct Cookie: header if any cookies are set
    280                 WP_Http::buildCookieHeader( $r );
    281 
    282                 if ( WP_Http_Encoding::is_available() )
    283                         $r['headers']['Accept-Encoding'] = WP_Http_Encoding::accept_encoding();
    284 
    285                 if ( empty($r['body']) ) {
    286                         // Some servers fail when sending content without the content-length header being set.
    287                         // Also, to fix another bug, we only send when doing POST and PUT and the content-length
    288                         // header isn't already set.
    289                         if( ($r['method'] == 'POST' || $r['method'] == 'PUT') && ! isset($r['headers']['Content-Length']) )
    290                                 $r['headers']['Content-Length'] = 0;
    291 
    292                         // The method is ambiguous, because we aren't talking about HTTP methods, the "get" in
    293                         // this case is simply that we aren't sending any bodies and to get the transports that
    294                         // don't support sending bodies along with those which do.
    295                         $transports = WP_Http::_getTransport($r);
    296                 } else {
    297                         if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) {
    298                                 if ( ! version_compare(phpversion(), '5.1.2', '>=') )
    299                                         $r['body'] = _http_build_query($r['body'], null, '&');
    300                                 else
    301                                         $r['body'] = http_build_query($r['body'], null, '&');
    302                                 $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option('blog_charset');
    303                                 $r['headers']['Content-Length'] = strlen($r['body']);
    304                         }
    305 
    306                         if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) )
    307                                 $r['headers']['Content-Length'] = strlen($r['body']);
    308 
    309                         // The method is ambiguous, because we aren't talking about HTTP methods, the "post" in
    310                         // this case is simply that we are sending HTTP body and to get the transports that do
    311                         // support sending the body. Not all do, depending on the limitations of the PHP core
    312                         // limitations.
    313                         $transports = WP_Http::_postTransport($r);
    314                 }
    315 
    316                 do_action( 'http_api_debug', $transports, 'transports_list' );
    317 
    318                 $response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    319                 foreach ( (array) $transports as $transport ) {
    320                         $response = $transport->request($url, $r);
    321 
    322                         do_action( 'http_api_debug', $response, 'response', get_class($transport) );
    323 
    324                         if ( ! is_wp_error($response) )
    325                                 return apply_filters( 'http_response', $response, $r, $url );
    326                 }
    327 
    328                 return $response;
    329         }
    330 
    331         /**
    332          * Uses the POST HTTP method.
    333          *
    334          * Used for sending data that is expected to be in the body.
    335          *
    336          * @access public
    337          * @since 2.7.0
    338          *
    339          * @param string $url URI resource.
    340          * @param str|array $args Optional. Override the defaults.
    341          * @return boolean
    342          */
    343         function post($url, $args = array()) {
    344                 $defaults = array('method' => 'POST');
    345                 $r = wp_parse_args( $args, $defaults );
    346                 return $this->request($url, $r);
    347         }
    348 
    349         /**
    350          * Uses the GET HTTP method.
    351          *
    352          * Used for sending data that is expected to be in the body.
    353          *
    354          * @access public
    355          * @since 2.7.0
    356          *
    357          * @param string $url URI resource.
    358          * @param str|array $args Optional. Override the defaults.
    359          * @return boolean
    360          */
    361         function get($url, $args = array()) {
    362                 $defaults = array('method' => 'GET');
    363                 $r = wp_parse_args( $args, $defaults );
    364                 return $this->request($url, $r);
    365         }
    366 
    367         /**
    368          * Uses the HEAD HTTP method.
    369          *
    370          * Used for sending data that is expected to be in the body.
    371          *
    372          * @access public
    373          * @since 2.7.0
    374          *
    375          * @param string $url URI resource.
    376          * @param str|array $args Optional. Override the defaults.
    377          * @return boolean
    378          */
    379         function head($url, $args = array()) {
    380                 $defaults = array('method' => 'HEAD');
    381                 $r = wp_parse_args( $args, $defaults );
    382                 return $this->request($url, $r);
    383         }
    384 
    385         /**
    386          * Parses the responses and splits the parts into headers and body.
    387          *
    388          * @access public
    389          * @static
    390          * @since 2.7.0
    391          *
    392          * @param string $strResponse The full response string
    393          * @return array Array with 'headers' and 'body' keys.
    394          */
    395         function processResponse($strResponse) {
    396                 list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
    397                 return array('headers' => $theHeaders, 'body' => $theBody);
    398         }
    399 
    400         /**
    401          * Transform header string into an array.
    402          *
    403          * If an array is given then it is assumed to be raw header data with numeric keys with the
    404          * headers as the values. No headers must be passed that were already processed.
    405          *
    406          * @access public
    407          * @static
    408          * @since 2.7.0
    409          *
    410          * @param string|array $headers
    411          * @return array Processed string headers. If duplicate headers are encountered,
    412          *                                      Then a numbered array is returned as the value of that header-key.
    413          */
    414         function processHeaders($headers) {
    415                 // split headers, one per array element
    416                 if ( is_string($headers) ) {
    417                         // tolerate line terminator: CRLF = LF (RFC 2616 19.3)
    418                         $headers = str_replace("\r\n", "\n", $headers);
    419                         // unfold folded header fields. LWS = [CRLF] 1*( SP | HT ) <US-ASCII SP, space (32)>, <US-ASCII HT, horizontal-tab (9)> (RFC 2616 2.2)
    420                         $headers = preg_replace('/\n[ \t]/', ' ', $headers);
    421                         // create the headers array
    422                         $headers = explode("\n", $headers);
    423                 }
    424 
    425                 $response = array('code' => 0, 'message' => '');
    426 
    427                 // If a redirection has taken place, The headers for each page request may have been passed.
    428                 // In this case, determine the final HTTP header and parse from there.
    429                 for ( $i = count($headers)-1; $i >= 0; $i-- ) {
    430                         if ( false === strpos($headers[$i], ':') ) {
    431                                 $headers = array_splice($headers, $i);
    432                                 break;
    433                         }
    434                 }
    435 
    436                 $cookies = array();
    437                 $newheaders = array();
    438                 foreach ( $headers as $tempheader ) {
    439                         if ( empty($tempheader) )
    440                                 continue;
    441 
    442                         if ( false === strpos($tempheader, ':') ) {
    443                                 list( , $iResponseCode, $strResponseMsg) = explode(' ', $tempheader, 3);
    444                                 $response['code'] = $iResponseCode;
    445                                 $response['message'] = $strResponseMsg;
    446                                 continue;
    447                         }
    448 
    449                         list($key, $value) = explode(':', $tempheader, 2);
    450 
    451                         if ( !empty( $value ) ) {
    452                                 $key = strtolower( $key );
    453                                 if ( isset( $newheaders[$key] ) ) {
    454                                         $newheaders[$key] = array( $newheaders[$key], trim( $value ) );
    455                                 } else {
    456                                         $newheaders[$key] = trim( $value );
    457                                 }
    458                                 if ( 'set-cookie' == strtolower( $key ) )
    459                                         $cookies[] = new WP_Http_Cookie( $value );
    460                         }
    461                 }
    462 
    463                 return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies);
    464         }
    465 
    466         /**
    467          * Takes the arguments for a ::request() and checks for the cookie array.
    468          *
    469          * If it's found, then it's assumed to contain WP_Http_Cookie objects, which are each parsed
    470          * into strings and added to the Cookie: header (within the arguments array). Edits the array by
    471          * reference.
    472          *
    473          * @access public
    474          * @version 2.8.0
    475          * @static
    476          *
    477          * @param array $r Full array of args passed into ::request()
    478          */
    479         function buildCookieHeader( &$r ) {
    480                 if ( ! empty($r['cookies']) ) {
    481                         $cookies_header = '';
    482                         foreach ( (array) $r['cookies'] as $cookie ) {
    483                                 $cookies_header .= $cookie->getHeaderValue() . '; ';
    484                         }
    485                         $cookies_header = substr( $cookies_header, 0, -2 );
    486                         $r['headers']['cookie'] = $cookies_header;
    487                 }
    488         }
    489 
    490         /**
    491          * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
    492          *
    493          * Based off the HTTP http_encoding_dechunk function. Does not support UTF-8. Does not support
    494          * returning footer headers. Shouldn't be too difficult to support it though.
    495          *
    496          * @todo Add support for footer chunked headers.
    497          * @access public
    498          * @since 2.7.0
    499          * @static
    500          *
    501          * @param string $body Body content
    502          * @return string Chunked decoded body on success or raw body on failure.
    503          */
    504         function chunkTransferDecode($body) {
    505                 $body = str_replace(array("\r\n", "\r"), "\n", $body);
    506                 // The body is not chunked encoding or is malformed.
    507                 if ( ! preg_match( '/^[0-9a-f]+(\s|\n)+/mi', trim($body) ) )
    508                         return $body;
    509 
    510                 $parsedBody = '';
    511                 //$parsedHeaders = array(); Unsupported
    512 
    513                 while ( true ) {
    514                         $hasChunk = (bool) preg_match( '/^([0-9a-f]+)(\s|\n)+/mi', $body, $match );
    515 
    516                         if ( $hasChunk ) {
    517                                 if ( empty( $match[1] ) )
    518                                         return $body;
    519 
    520                                 $length = hexdec( $match[1] );
    521                                 $chunkLength = strlen( $match[0] );
    522 
    523                                 $strBody = substr($body, $chunkLength, $length);
    524                                 $parsedBody .= $strBody;
    525 
    526                                 $body = ltrim(str_replace(array($match[0], $strBody), '', $body), "\n");
    527 
    528                                 if ( "0" == trim($body) )
    529                                         return $parsedBody; // Ignore footer headers.
    530                         } else {
    531                                 return $body;
    532                         }
    533                 }
    534         }
    535 
    536         /**
    537          * Block requests through the proxy.
    538          *
    539          * Those who are behind a proxy and want to prevent access to certain hosts may do so. This will
    540          * prevent plugins from working and core functionality, if you don't include api.wordpress.org.
    541          *
    542          * You block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL in your wp-config.php file
    543          * and this will only allow localhost and your blog to make requests. The constant
    544          * WP_ACCESSIBLE_HOSTS will allow additional hosts to go through for requests. The format of the
    545          * WP_ACCESSIBLE_HOSTS constant is a comma separated list of hostnames to allow.
    546          *
    547          * @since 2.8.0
    548          * @link http://core.trac.wordpress.org/ticket/8927 Allow preventing external requests.
    549          *
    550          * @param string $uri URI of url.
    551          * @return bool True to block, false to allow.
    552          */
    553         function block_request($uri) {
    554                 // We don't need to block requests, because nothing is blocked.
    555                 if ( ! defined('WP_HTTP_BLOCK_EXTERNAL') || ( defined('WP_HTTP_BLOCK_EXTERNAL') && WP_HTTP_BLOCK_EXTERNAL == false ) )
    556                         return false;
    557 
    558                 // parse_url() only handles http, https type URLs, and will emit E_WARNING on failure.
    559                 // This will be displayed on blogs, which is not reasonable.
    560                 $check = @parse_url($uri);
    561 
    562                 /* Malformed URL, can not process, but this could mean ssl, so let through anyway.
    563                  *
    564                  * This isn't very security sound. There are instances where a hacker might attempt
    565                  * to bypass the proxy and this check. However, the reason for this behavior is that
    566                  * WordPress does not do any checking currently for non-proxy requests, so it is keeps with
    567                  * the default unsecure nature of the HTTP request.
    568                  */
    569                 if ( $check === false )
    570                         return false;
    571 
    572                 $home = parse_url( get_option('siteurl') );
    573 
    574                 // Don't block requests back to ourselves by default
    575                 if ( $check['host'] == 'localhost' || $check['host'] == $home['host'] )
    576                         return apply_filters('block_local_requests', false);
    577 
    578                 if ( !defined('WP_ACCESSIBLE_HOSTS') )
    579                         return true;
    580 
    581                 static $accessible_hosts;
    582                 if ( null == $accessible_hosts )
    583                         $accessible_hosts = preg_split('|,\s*|', WP_ACCESSIBLE_HOSTS);
    584 
    585                 return !in_array( $check['host'], $accessible_hosts ); //Inverse logic, If its in the array, then we can't access it.
    586         }
    587 }
    588 
    589 /**
    590  * HTTP request method uses fsockopen function to retrieve the url.
    591  *
    592  * This would be the preferred method, but the fsockopen implementation has the most overhead of all
    593  * the HTTP transport implementations.
    594  *
    595  * @package WordPress
    596  * @subpackage HTTP
    597  * @since 2.7.0
    598  */
    599 class WP_Http_Fsockopen {
    600         /**
    601          * Send a HTTP request to a URI using fsockopen().
    602          *
    603          * Does not support non-blocking mode.
    604          *
    605          * @see WP_Http::request For default options descriptions.
    606          *
    607          * @since 2.7
    608          * @access public
    609          * @param string $url URI resource.
    610          * @param str|array $args Optional. Override the defaults.
    611          * @return array 'headers', 'body', 'cookies' and 'response' keys.
    612          */
    613         function request($url, $args = array()) {
    614                 $defaults = array(
    615                         'method' => 'GET', 'timeout' => 5,
    616                         'redirection' => 5, 'httpversion' => '1.0',
    617                         'blocking' => true,
    618                         'headers' => array(), 'body' => null, 'cookies' => array()
    619                 );
    620 
    621                 $r = wp_parse_args( $args, $defaults );
    622 
    623                 if ( isset($r['headers']['User-Agent']) ) {
    624                         $r['user-agent'] = $r['headers']['User-Agent'];
    625                         unset($r['headers']['User-Agent']);
    626                 } else if( isset($r['headers']['user-agent']) ) {
    627                         $r['user-agent'] = $r['headers']['user-agent'];
    628                         unset($r['headers']['user-agent']);
    629                 }
    630 
    631                 // Construct Cookie: header if any cookies are set
    632                 WP_Http::buildCookieHeader( $r );
    633 
    634                 $iError = null; // Store error number
    635                 $strError = null; // Store error string
    636 
    637                 $arrURL = parse_url($url);
    638 
    639                 $fsockopen_host = $arrURL['host'];
    640 
    641                 $secure_transport = false;
    642 
    643                 if ( ! isset( $arrURL['port'] ) ) {
    644                         if ( ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) && extension_loaded('openssl') ) {
    645                                 $fsockopen_host = "ssl://$fsockopen_host";
    646                                 $arrURL['port'] = 443;
    647                                 $secure_transport = true;
    648                         } else {
    649                                 $arrURL['port'] = 80;
    650                         }
    651                 }
    652 
    653                 //fsockopen has issues with 'localhost' with IPv6 with certain versions of PHP, It attempts to connect to ::1,
    654                 // which fails when the server is not setup for it. For compatibility, always connect to the IPv4 address.
    655                 if ( 'localhost' == strtolower($fsockopen_host) )
    656                         $fsockopen_host = '127.0.0.1';
    657 
    658                 // There are issues with the HTTPS and SSL protocols that cause errors that can be safely
    659                 // ignored and should be ignored.
    660                 if ( true === $secure_transport )
    661                         $error_reporting = error_reporting(0);
    662 
    663                 $startDelay = time();
    664 
    665                 $proxy = new WP_HTTP_Proxy();
    666 
    667                 if ( !WP_DEBUG ) {
    668                         if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
    669                                 $handle = @fsockopen( $proxy->host(), $proxy->port(), $iError, $strError, $r['timeout'] );
    670                         else
    671                                 $handle = @fsockopen( $fsockopen_host, $arrURL['port'], $iError, $strError, $r['timeout'] );
    672                 } else {
    673                         if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
    674                                 $handle = fsockopen( $proxy->host(), $proxy->port(), $iError, $strError, $r['timeout'] );
    675                         else
    676                                 $handle = fsockopen( $fsockopen_host, $arrURL['port'], $iError, $strError, $r['timeout'] );
    677                 }
    678 
    679                 $endDelay = time();
    680 
    681                 // If the delay is greater than the timeout then fsockopen should't be used, because it will
    682                 // cause a long delay.
    683                 $elapseDelay = ($endDelay-$startDelay) > $r['timeout'];
    684                 if ( true === $elapseDelay )
    685                         add_option( 'disable_fsockopen', $endDelay, null, true );
    686 
    687                 if ( false === $handle )
    688                         return new WP_Error('http_request_failed', $iError . ': ' . $strError);
    689 
    690                 $timeout = (int) floor( $r['timeout'] );
    691                 $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
    692                 stream_set_timeout( $handle, $timeout, $utimeout );
    693 
    694                 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) //Some proxies require full URL in this field.
    695                         $requestPath = $url;
    696                 else
    697                         $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' );
    698 
    699                 if ( empty($requestPath) )
    700                         $requestPath .= '/';
    701 
    702                 $strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n";
    703 
    704                 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
    705                         $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n";
    706                 else
    707                         $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n";
    708 
    709                 if ( isset($r['user-agent']) )
    710                         $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n";
    711 
    712                 if ( is_array($r['headers']) ) {
    713                         foreach ( (array) $r['headers'] as $header => $headerValue )
    714                                 $strHeaders .= $header . ': ' . $headerValue . "\r\n";
    715                 } else {
    716                         $strHeaders .= $r['headers'];
    717                 }
    718 
    719                 if ( $proxy->use_authentication() )
    720                         $strHeaders .= $proxy->authentication_header() . "\r\n";
    721 
    722                 $strHeaders .= "\r\n";
    723 
    724                 if ( ! is_null($r['body']) )
    725                         $strHeaders .= $r['body'];
    726 
    727                 fwrite($handle, $strHeaders);
    728 
    729                 if ( ! $r['blocking'] ) {
    730                         fclose($handle);
    731                         return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    732                 }
    733 
    734                 $strResponse = '';
    735                 while ( ! feof($handle) )
    736                         $strResponse .= fread($handle, 4096);
    737 
    738                 fclose($handle);
    739 
    740                 if ( true === $secure_transport )
    741                         error_reporting($error_reporting);
    742 
    743                 $process = WP_Http::processResponse($strResponse);
    744                 $arrHeaders = WP_Http::processHeaders($process['headers']);
    745 
    746                 // Is the response code within the 400 range?
    747                 if ( (int) $arrHeaders['response']['code'] >= 400 && (int) $arrHeaders['response']['code'] < 500 )
    748                         return new WP_Error('http_request_failed', $arrHeaders['response']['code'] . ': ' . $arrHeaders['response']['message']);
    749 
    750                 // If location is found, then assume redirect and redirect to location.
    751                 if ( 'HEAD' != $r['method'] && isset($arrHeaders['headers']['location']) ) {
    752                         if ( $r['redirection']-- > 0 ) {
    753                                 return $this->request($arrHeaders['headers']['location'], $r);
    754                         } else {
    755                                 return new WP_Error('http_request_failed', __('Too many redirects.'));
    756                         }
    757                 }
    758 
    759                 // If the body was chunk encoded, then decode it.
    760                 if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) && 'chunked' == $arrHeaders['headers']['transfer-encoding'] )
    761                         $process['body'] = WP_Http::chunkTransferDecode($process['body']);
    762 
    763                 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders['headers']) )
    764                         $process['body'] = WP_Http_Encoding::decompress( $process['body'] );
    765 
    766                 return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);
    767         }
    768 
    769         /**
    770          * Whether this class can be used for retrieving an URL.
    771          *
    772          * @since 2.7.0
    773          * @static
    774          * @return boolean False means this class can not be used, true means it can.
    775          */
    776         function test( $args = array() ) {
    777                 if ( false !== ($option = get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours
    778                         return false;
    779 
    780                 $is_ssl = isset($args['ssl']) && $args['ssl'];
    781 
    782                 if ( ! $is_ssl && function_exists( 'fsockopen' ) )
    783                         $use = true;
    784                 elseif ( $is_ssl && extension_loaded('openssl') && function_exists( 'fsockopen' ) )
    785                         $use = true;
    786                 else
    787                         $use = false;
    788 
    789                 return apply_filters('use_fsockopen_transport', $use, $args);
    790         }
    791 }
    792 
    793 /**
    794  * HTTP request method uses fopen function to retrieve the url.
    795  *
    796  * Requires PHP version greater than 4.3.0 for stream support. Does not allow for $context support,
    797  * but should still be okay, to write the headers, before getting the response. Also requires that
    798  * 'allow_url_fopen' to be enabled.
    799  *
    800  * @package WordPress
    801  * @subpackage HTTP
    802  * @since 2.7.0
    803  */
    804 class WP_Http_Fopen {
    805         /**
    806          * Send a HTTP request to a URI using fopen().
    807          *
    808          * This transport does not support sending of headers and body, therefore should not be used in
    809          * the instances, where there is a body and headers.
    810          *
    811          * Notes: Does not support non-blocking mode. Ignores 'redirection' option.
    812          *
    813          * @see WP_Http::retrieve For default options descriptions.
    814          *
    815          * @access public
    816          * @since 2.7.0
    817          *
    818          * @param string $url URI resource.
    819          * @param str|array $args Optional. Override the defaults.
    820          * @return array 'headers', 'body', 'cookies' and 'response' keys.
    821          */
    822         function request($url, $args = array()) {
    823                 $defaults = array(
    824                         'method' => 'GET', 'timeout' => 5,
    825                         'redirection' => 5, 'httpversion' => '1.0',
    826                         'blocking' => true,
    827                         'headers' => array(), 'body' => null, 'cookies' => array()
    828                 );
    829 
    830                 $r = wp_parse_args( $args, $defaults );
    831 
    832                 $arrURL = parse_url($url);
    833 
    834                 if ( false === $arrURL )
    835                         return new WP_Error('http_request_failed', sprintf(__('Malformed URL: %s'), $url));
    836 
    837                 if ( 'http' != $arrURL['scheme'] && 'https' != $arrURL['scheme'] )
    838                         $url = str_replace($arrURL['scheme'], 'http', $url);
    839 
    840                 if ( is_null( $r['headers'] ) )
    841                         $r['headers'] = array();
    842 
    843                 if ( is_string($r['headers']) ) {
    844                         $processedHeaders = WP_Http::processHeaders($r['headers']);
    845                         $r['headers'] = $processedHeaders['headers'];
    846                 }
    847 
    848                 $initial_user_agent = ini_get('user_agent');
    849 
    850                 if ( !empty($r['headers']) && is_array($r['headers']) ) {
    851                         $user_agent_extra_headers = '';
    852                         foreach ( $r['headers'] as $header => $value )
    853                                 $user_agent_extra_headers .= "\r\n$header: $value";
    854                         @ini_set('user_agent', $r['user-agent'] . $user_agent_extra_headers);
    855                 } else {
    856                         @ini_set('user_agent', $r['user-agent']);
    857                 }
    858 
    859                 if ( !WP_DEBUG )
    860                         $handle = @fopen($url, 'r');
    861                 else
    862                         $handle = fopen($url, 'r');
    863 
    864                 if (! $handle)
    865                         return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url));
    866 
    867                 $timeout = (int) floor( $r['timeout'] );
    868                 $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
    869                 stream_set_timeout( $handle, $timeout, $utimeout );
    870 
    871                 if ( ! $r['blocking'] ) {
    872                         fclose($handle);
    873                         @ini_set('user_agent', $initial_user_agent); //Clean up any extra headers added
    874                         return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    875                 }
    876 
    877                 $strResponse = '';
    878                 while ( ! feof($handle) )
    879                         $strResponse .= fread($handle, 4096);
    880 
    881                 if ( function_exists('stream_get_meta_data') ) {
    882                         $meta = stream_get_meta_data($handle);
    883                         $theHeaders = $meta['wrapper_data'];
    884                         if ( isset( $meta['wrapper_data']['headers'] ) )
    885                                 $theHeaders = $meta['wrapper_data']['headers'];
    886                 } else {
    887                         //$http_response_header is a PHP reserved variable which is set in the current-scope when using the HTTP Wrapper
    888                         //see http://php.oregonstate.edu/manual/en/reserved.variables.httpresponseheader.php
    889                         $theHeaders = $http_response_header;
    890                 }
    891 
    892                 fclose($handle);
    893 
    894                 @ini_set('user_agent', $initial_user_agent); //Clean up any extra headers added
    895 
    896                 $processedHeaders = WP_Http::processHeaders($theHeaders);
    897 
    898                 if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] )
    899                         $strResponse = WP_Http::chunkTransferDecode($strResponse);
    900 
    901                 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders['headers']) )
    902                         $strResponse = WP_Http_Encoding::decompress( $strResponse );
    903 
    904                 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
    905         }
    906 
    907         /**
    908          * Whether this class can be used for retrieving an URL.
    909          *
    910          * @since 2.7.0
    911          * @static
    912          * @return boolean False means this class can not be used, true means it can.
    913          */
    914         function test($args = array()) {
    915                 if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )
    916                         return false;
    917 
    918                 if ( isset($args['method']) && 'HEAD' == $args['method'] ) //This transport cannot make a HEAD request
    919                         return false;
    920 
    921                 $use = true;
    922                 //PHP does not verify SSL certs, We can only make a request via this transports if SSL Verification is turned off.
    923                 $is_ssl = isset($args['ssl']) && $args['ssl'];
    924                 if ( $is_ssl ) {
    925                         $is_local = isset($args['local']) && $args['local'];
    926                         $ssl_verify = isset($args['sslverify']) && $args['sslverify'];
    927                         if ( $is_local && true != apply_filters('https_local_ssl_verify', true) )
    928                                 $use = true;
    929                         elseif ( !$is_local && true != apply_filters('https_ssl_verify', true) )
    930                                 $use = true;
    931                         elseif ( !$ssl_verify )
    932                                 $use = true;
    933                         else
    934                                 $use = false;
    935                 }
    936 
    937                 return apply_filters('use_fopen_transport', $use, $args);
    938         }
    939 }
    940 
    941 /**
    942  * HTTP request method uses Streams to retrieve the url.
    943  *
    944  * Requires PHP 5.0+ and uses fopen with stream context. Requires that 'allow_url_fopen' PHP setting
    945  * to be enabled.
    946  *
    947  * Second preferred method for getting the URL, for PHP 5.
    948  *
    949  * @package WordPress
    950  * @subpackage HTTP
    951  * @since 2.7.0
    952  */
    953 class WP_Http_Streams {
    954         /**
    955          * Send a HTTP request to a URI using streams with fopen().
    956          *
    957          * @access public
    958          * @since 2.7.0
    959          *
    960          * @param string $url
    961          * @param str|array $args Optional. Override the defaults.
    962          * @return array 'headers', 'body', 'cookies' and 'response' keys.
    963          */
    964         function request($url, $args = array()) {
    965                 $defaults = array(
    966                         'method' => 'GET', 'timeout' => 5,
    967                         'redirection' => 5, 'httpversion' => '1.0',
    968                         'blocking' => true,
    969                         'headers' => array(), 'body' => null, 'cookies' => array()
    970                 );
    971 
    972                 $r = wp_parse_args( $args, $defaults );
    973 
    974                 if ( isset($r['headers']['User-Agent']) ) {
    975                         $r['user-agent'] = $r['headers']['User-Agent'];
    976                         unset($r['headers']['User-Agent']);
    977                 } else if( isset($r['headers']['user-agent']) ) {
    978                         $r['user-agent'] = $r['headers']['user-agent'];
    979                         unset($r['headers']['user-agent']);
    980                 }
    981 
    982                 // Construct Cookie: header if any cookies are set
    983                 WP_Http::buildCookieHeader( $r );
    984 
    985                 $arrURL = parse_url($url);
    986 
    987                 if ( false === $arrURL )
    988                         return new WP_Error('http_request_failed', sprintf(__('Malformed URL: %s'), $url));
    989 
    990                 if ( 'http' != $arrURL['scheme'] && 'https' != $arrURL['scheme'] )
    991                         $url = preg_replace('|^' . preg_quote($arrURL['scheme'], '|') . '|', 'http', $url);
    992 
    993                 // Convert Header array to string.
    994                 $strHeaders = '';
    995                 if ( is_array( $r['headers'] ) )
    996                         foreach ( $r['headers'] as $name => $value )
    997                                 $strHeaders .= "{$name}: $value\r\n";
    998                 else if ( is_string( $r['headers'] ) )
    999                         $strHeaders = $r['headers'];
    1000 
    1001                 $is_local = isset($args['local']) && $args['local'];
    1002                 $ssl_verify = isset($args['sslverify']) && $args['sslverify'];
    1003                 if ( $is_local )
    1004                         $ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
    1005                 elseif ( ! $is_local )
    1006                         $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
    1007 
    1008                 $arrContext = array('http' =>
    1009                         array(
    1010                                 'method' => strtoupper($r['method']),
    1011                                 'user_agent' => $r['user-agent'],
    1012                                 'max_redirects' => $r['redirection'] + 1, // See #11557
    1013                                 'protocol_version' => (float) $r['httpversion'],
    1014                                 'header' => $strHeaders,
    1015                                 'ignore_errors' => true, // Return non-200 requests.
    1016                                 'timeout' => $r['timeout'],
    1017                                 'ssl' => array(
    1018                                                 'verify_peer' => $ssl_verify,
    1019                                                 'verify_host' => $ssl_verify
    1020                                 )
    1021                         )
    1022                 );
    1023 
    1024                 $proxy = new WP_HTTP_Proxy();
    1025 
    1026                 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
    1027                         $arrContext['http']['proxy'] = 'tcp://' . $proxy->host() . ':' . $proxy->port();
    1028                         $arrContext['http']['request_fulluri'] = true;
    1029 
    1030                         // We only support Basic authentication so this will only work if that is what your proxy supports.
    1031                         if ( $proxy->use_authentication() )
    1032                                 $arrContext['http']['header'] .= $proxy->authentication_header() . "\r\n";
    1033                 }
    1034 
    1035                 if ( 'HEAD' == $r['method'] ) // Disable redirects for HEAD requests
    1036                         $arrContext['http']['max_redirects'] = 1;
    1037 
    1038                 if ( ! empty($r['body'] ) )
    1039                         $arrContext['http']['content'] = $r['body'];
    1040 
    1041                 $context = stream_context_create($arrContext);
    1042 
    1043                 if ( !WP_DEBUG )
    1044                         $handle = @fopen($url, 'r', false, $context);
    1045                 else
    1046                         $handle = fopen($url, 'r', false, $context);
    1047 
    1048                 if ( ! $handle )
    1049                         return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url));
    1050 
    1051                 $timeout = (int) floor( $r['timeout'] );
    1052                 $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
    1053                 stream_set_timeout( $handle, $timeout, $utimeout );
    1054 
    1055                 if ( ! $r['blocking'] ) {
    1056                         stream_set_blocking($handle, 0);
    1057                         fclose($handle);
    1058                         return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    1059                 }
    1060 
    1061                 $strResponse = stream_get_contents($handle);
    1062                 $meta = stream_get_meta_data($handle);
    1063 
    1064                 fclose($handle);
    1065 
    1066                 $processedHeaders = array();
    1067                 if ( isset( $meta['wrapper_data']['headers'] ) )
    1068                         $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']['headers']);
    1069                 else
    1070                         $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']);
    1071 
    1072                 if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] )
    1073                         $strResponse = WP_Http::chunkTransferDecode($strResponse);
    1074 
    1075                 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders['headers']) )
    1076                         $strResponse = WP_Http_Encoding::decompress( $strResponse );
    1077 
    1078                 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
    1079         }
    1080 
    1081         /**
    1082          * Whether this class can be used for retrieving an URL.
    1083          *
    1084          * @static
    1085          * @access public
    1086          * @since 2.7.0
    1087          *
    1088          * @return boolean False means this class can not be used, true means it can.
    1089          */
    1090         function test($args = array()) {
    1091                 if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )
    1092                         return false;
    1093 
    1094                 if ( version_compare(PHP_VERSION, '5.0', '<') )
    1095                         return false;
    1096 
    1097                 //HTTPS via Proxy was added in 5.1.0
    1098                 $is_ssl = isset($args['ssl']) && $args['ssl'];
    1099                 if ( $is_ssl && version_compare(PHP_VERSION, '5.1.0', '<') ) {
    1100                         $proxy = new WP_HTTP_Proxy();
    1101                         /**
    1102                          * No URL check, as its not currently passed to the ::test() function
    1103                          * In the case where a Proxy is in use, Just bypass this transport for HTTPS.
    1104                          */
    1105                         if ( $proxy->is_enabled() )
    1106                                 return false;
    1107                 }
    1108 
    1109                 return apply_filters('use_streams_transport', true, $args);
    1110         }
    1111 }
    1112 
    1113 /**
    1114  * HTTP request method uses HTTP extension to retrieve the url.
    1115  *
    1116  * Requires the HTTP extension to be installed. This would be the preferred transport since it can
    1117  * handle a lot of the problems that forces the others to use the HTTP version 1.0. Even if PHP 5.2+
    1118  * is being used, it doesn't mean that the HTTP extension will be enabled.
    1119  *
    1120  * @package WordPress
    1121  * @subpackage HTTP
    1122  * @since 2.7.0
    1123  */
    1124 class WP_Http_ExtHTTP {
    1125         /**
    1126          * Send a HTTP request to a URI using HTTP extension.
    1127          *
    1128          * Does not support non-blocking.
    1129          *
    1130          * @access public
    1131          * @since 2.7
    1132          *
    1133          * @param string $url
    1134          * @param str|array $args Optional. Override the defaults.
    1135          * @return array 'headers', 'body', 'cookies' and 'response' keys.
    1136          */
    1137         function request($url, $args = array()) {
    1138                 $defaults = array(
    1139                         'method' => 'GET', 'timeout' => 5,
    1140                         'redirection' => 5, 'httpversion' => '1.0',
    1141                         'blocking' => true,
    1142                         'headers' => array(), 'body' => null, 'cookies' => array()
    1143                 );
    1144 
    1145                 $r = wp_parse_args( $args, $defaults );
    1146 
    1147                 if ( isset($r['headers']['User-Agent']) ) {
    1148                         $r['user-agent'] = $r['headers']['User-Agent'];
    1149                         unset($r['headers']['User-Agent']);
    1150                 } else if( isset($r['headers']['user-agent']) ) {
    1151                         $r['user-agent'] = $r['headers']['user-agent'];
    1152                         unset($r['headers']['user-agent']);
    1153                 }
    1154 
    1155                 // Construct Cookie: header if any cookies are set
    1156                 WP_Http::buildCookieHeader( $r );
    1157 
    1158                 switch ( $r['method'] ) {
    1159                         case 'POST':
    1160                                 $r['method'] = HTTP_METH_POST;
    1161                                 break;
    1162                         case 'HEAD':
    1163                                 $r['method'] = HTTP_METH_HEAD;
    1164                                 break;
    1165                         case 'PUT':
    1166                                 $r['method'] =  HTTP_METH_PUT;
    1167                                 break;
    1168                         case 'GET':
    1169                         default:
    1170                                 $r['method'] = HTTP_METH_GET;
    1171                 }
    1172 
    1173                 $arrURL = parse_url($url);
    1174 
    1175                 if ( 'http' != $arrURL['scheme'] || 'https' != $arrURL['scheme'] )
    1176                         $url = preg_replace('|^' . preg_quote($arrURL['scheme'], '|') . '|', 'http', $url);
    1177 
    1178                 $is_local = isset($args['local']) && $args['local'];
    1179                 $ssl_verify = isset($args['sslverify']) && $args['sslverify'];
    1180                 if ( $is_local )
    1181                         $ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
    1182                 elseif ( ! $is_local )
    1183                         $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
    1184 
    1185                 $r['timeout'] = (int) ceil( $r['timeout'] );
    1186 
    1187                 $options = array(
    1188                         'timeout' => $r['timeout'],
    1189                         'connecttimeout' => $r['timeout'],
    1190                         'redirect' => $r['redirection'],
    1191                         'useragent' => $r['user-agent'],
    1192                         'headers' => $r['headers'],
    1193                         'ssl' => array(
    1194                                 'verifypeer' => $ssl_verify,
    1195                                 'verifyhost' => $ssl_verify
    1196                         )
    1197                 );
    1198 
    1199                 if ( HTTP_METH_HEAD == $r['method'] )
    1200                         $options['redirect'] = 0; // Assumption: Docs seem to suggest that this means do not follow. Untested.
    1201 
    1202                 // The HTTP extensions offers really easy proxy support.
    1203                 $proxy = new WP_HTTP_Proxy();
    1204 
    1205                 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
    1206                         $options['proxyhost'] = $proxy->host();
    1207                         $options['proxyport'] = $proxy->port();
    1208                         $options['proxytype'] = HTTP_PROXY_HTTP;
    1209 
    1210                         if ( $proxy->use_authentication() ) {
    1211                                 $options['proxyauth'] = $proxy->authentication();
    1212                                 $options['proxyauthtype'] = HTTP_AUTH_BASIC;
    1213                         }
    1214                 }
    1215 
    1216                 if ( !WP_DEBUG ) //Emits warning level notices for max redirects and timeouts
    1217                         $strResponse = @http_request($r['method'], $url, $r['body'], $options, $info);
    1218                 else
    1219                         $strResponse = http_request($r['method'], $url, $r['body'], $options, $info); //Emits warning level notices for max redirects and timeouts
    1220 
    1221                 // Error may still be set, Response may return headers or partial document, and error
    1222                 // contains a reason the request was aborted, eg, timeout expired or max-redirects reached.
    1223                 if ( false === $strResponse || ! empty($info['error']) )
    1224                         return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
    1225 
    1226                 if ( ! $r['blocking'] )
    1227                         return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    1228 
    1229                 list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
    1230                 $theHeaders = WP_Http::processHeaders($theHeaders);
    1231 
    1232                 if ( ! empty( $theBody ) && isset( $theHeaders['headers']['transfer-encoding'] ) && 'chunked' == $theHeaders['headers']['transfer-encoding'] ) {
    1233                         if ( !WP_DEBUG )
    1234                                 $theBody = @http_chunked_decode($theBody);
    1235                         else
    1236                                 $theBody = http_chunked_decode($theBody);
    1237                 }
    1238 
    1239                 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )
    1240                         $theBody = http_inflate( $theBody );
    1241 
    1242                 $theResponse = array();
    1243                 $theResponse['code'] = $info['response_code'];
    1244                 $theResponse['message'] = get_status_header_desc($info['response_code']);
    1245 
    1246                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']);
    1247         }
    1248 
    1249         /**
    1250          * Whether this class can be used for retrieving an URL.
    1251          *
    1252          * @static
    1253          * @since 2.7.0
    1254          *
    1255          * @return boolean False means this class can not be used, true means it can.
    1256          */
    1257         function test($args = array()) {
    1258                 return apply_filters('use_http_extension_transport', function_exists('http_request'), $args );
    1259         }
    1260 }
    1261 
    1262 /**
    1263  * HTTP request method uses Curl extension to retrieve the url.
    1264  *
    1265  * Requires the Curl extension to be installed.
    1266  *
    1267  * @package WordPress
    1268  * @subpackage HTTP
    1269  * @since 2.7
    1270  */
    1271 class WP_Http_Curl {
    1272 
    1273         /**
    1274          * Send a HTTP request to a URI using cURL extension.
    1275          *
    1276          * @access public
    1277          * @since 2.7.0
    1278          *
    1279          * @param string $url
    1280          * @param str|array $args Optional. Override the defaults.
    1281          * @return array 'headers', 'body', 'cookies' and 'response' keys.
    1282          */
    1283         function request($url, $args = array()) {
    1284                 $defaults = array(
    1285                         'method' => 'GET', 'timeout' => 5,
    1286                         'redirection' => 5, 'httpversion' => '1.0',
    1287                         'blocking' => true,
    1288                         'headers' => array(), 'body' => null, 'cookies' => array()
    1289                 );
    1290 
    1291                 $r = wp_parse_args( $args, $defaults );
    1292 
    1293                 if ( isset($r['headers']['User-Agent']) ) {
    1294                         $r['user-agent'] = $r['headers']['User-Agent'];
    1295                         unset($r['headers']['User-Agent']);
    1296                 } else if( isset($r['headers']['user-agent']) ) {
    1297                         $r['user-agent'] = $r['headers']['user-agent'];
    1298                         unset($r['headers']['user-agent']);
    1299                 }
    1300 
    1301                 // Construct Cookie: header if any cookies are set.
    1302                 WP_Http::buildCookieHeader( $r );
    1303 
    1304                 $handle = curl_init();
    1305 
    1306                 // cURL offers really easy proxy support.
    1307                 $proxy = new WP_HTTP_Proxy();
    1308 
    1309                 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
    1310 
    1311                         $isPHP5 = version_compare(PHP_VERSION, '5.0.0', '>=');
    1312 
    1313                         if ( $isPHP5 ) {
    1314                                 curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
    1315                                 curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() );
    1316                                 curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() );
    1317                         } else {
    1318                                 curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() .':'. $proxy->port() );
    1319                         }
    1320 
    1321                         if ( $proxy->use_authentication() ) {
    1322                                 if ( $isPHP5 )
    1323                                         curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_BASIC );
    1324 
    1325                                 curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
    1326                         }
    1327                 }
    1328 
    1329                 $is_local = isset($args['local']) && $args['local'];
    1330                 $ssl_verify = isset($args['sslverify']) && $args['sslverify'];
    1331                 if ( $is_local )
    1332                         $ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
    1333                 elseif ( ! $is_local )
    1334                         $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
    1335 
    1336 
    1337                 // CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers.  Have to use ceil since
    1338                 // a value of 0 will allow an ulimited timeout.
    1339                 $timeout = (int) ceil( $r['timeout'] );
    1340                 curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout );
    1341                 curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout );
    1342 
    1343                 curl_setopt( $handle, CURLOPT_URL, $url);
    1344                 curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
    1345                 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, $ssl_verify );
    1346                 curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
    1347                 curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
    1348                 curl_setopt( $handle, CURLOPT_MAXREDIRS, $r['redirection'] );
    1349 
    1350                 switch ( $r['method'] ) {
    1351                         case 'HEAD':
    1352                                 curl_setopt( $handle, CURLOPT_NOBODY, true );
    1353                                 break;
    1354                         case 'POST':
    1355                                 curl_setopt( $handle, CURLOPT_POST, true );
    1356                                 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
    1357                                 break;
    1358                         case 'PUT':
    1359                                 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
    1360                                 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
    1361                                 break;
    1362                 }
    1363 
    1364                 if ( true === $r['blocking'] )
    1365                         curl_setopt( $handle, CURLOPT_HEADER, true );
    1366                 else
    1367                         curl_setopt( $handle, CURLOPT_HEADER, false );
    1368 
    1369                 // The option doesn't work with safe mode or when open_basedir is set.
    1370                 // Disable HEAD when making HEAD requests.
    1371                 if ( !ini_get('safe_mode') && !ini_get('open_basedir') && 'HEAD' != $r['method'] )
    1372                         curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
    1373 
    1374                 if ( !empty( $r['headers'] ) ) {
    1375                         // cURL expects full header strings in each element
    1376                         $headers = array();
    1377                         foreach ( $r['headers'] as $name => $value ) {
    1378                                 $headers[] = "{$name}: $value";
    1379                         }
    1380                         curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
    1381                 }
    1382 
    1383                 if ( $r['httpversion'] == '1.0' )
    1384                         curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
    1385                 else
    1386                         curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
    1387 
    1388                 // Cookies are not handled by the HTTP API currently. Allow for plugin authors to handle it
    1389                 // themselves... Although, it is somewhat pointless without some reference.
    1390                 do_action_ref_array( 'http_api_curl', array(&$handle) );
    1391 
    1392                 // We don't need to return the body, so don't. Just execute request and return.
    1393                 if ( ! $r['blocking'] ) {
    1394                         curl_exec( $handle );
    1395                         curl_close( $handle );
    1396                         return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    1397                 }
    1398 
    1399                 $theResponse = curl_exec( $handle );
    1400 
    1401                 if ( !empty($theResponse) ) {
    1402                         $headerLength = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
    1403                         $theHeaders = trim( substr($theResponse, 0, $headerLength) );
    1404                         if ( strlen($theResponse) > $headerLength )
    1405                                 $theBody = substr( $theResponse, $headerLength );
    1406                         else
    1407                                 $theBody = '';
    1408                         if ( false !== strrpos($theHeaders, "\r\n\r\n") ) {
    1409                                 $headerParts = explode("\r\n\r\n", $theHeaders);
    1410                                 $theHeaders = $headerParts[ count($headerParts) -1 ];
    1411                         }
    1412                         $theHeaders = WP_Http::processHeaders($theHeaders);
    1413                 } else {
    1414                         if ( $curl_error = curl_error($handle) )
    1415                                 return new WP_Error('http_request_failed', $curl_error);
    1416                         if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
    1417                                 return new WP_Error('http_request_failed', __('Too many redirects.'));
    1418 
    1419                         $theHeaders = array( 'headers' => array(), 'cookies' => array() );
    1420                         $theBody = '';
    1421                 }
    1422 
    1423                 $response = array();
    1424                 $response['code'] = curl_getinfo( $handle, CURLINFO_HTTP_CODE );
    1425                 $response['message'] = get_status_header_desc($response['code']);
    1426 
    1427                 curl_close( $handle );
    1428 
    1429                 // See #11305 - When running under safe mode, redirection is disabled above. Handle it manually.
    1430                 if ( !empty($theHeaders['headers']['location']) && (ini_get('safe_mode') || ini_get('open_basedir')) ) {
    1431                         if ( $r['redirection']-- > 0 ) {
    1432                                 return $this->request($theHeaders['headers']['location'], $r);
    1433                         } else {
    1434                                 return new WP_Error('http_request_failed', __('Too many redirects.'));
    1435                         }
    1436                 }
    1437 
    1438                 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )
    1439                         $theBody = WP_Http_Encoding::decompress( $theBody );
    1440 
    1441                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']);
    1442         }
    1443 
    1444         /**
    1445          * Whether this class can be used for retrieving an URL.
    1446          *
    1447          * @static
    1448          * @since 2.7.0
    1449          *
    1450          * @return boolean False means this class can not be used, true means it can.
    1451          */
    1452         function test($args = array()) {
    1453                 if ( function_exists('curl_init') && function_exists('curl_exec') )
    1454                         return apply_filters('use_curl_transport', true, $args);
    1455 
    1456                 return false;
    1457         }
    1458 }
    1459 
    1460 /**
    1461  * Adds Proxy support to the WordPress HTTP API.
    1462  *
    1463  * There are caveats to proxy support. It requires that defines be made in the wp-config.php file to
    1464  * enable proxy support. There are also a few filters that plugins can hook into for some of the
    1465  * constants.
    1466  *
    1467  * The constants are as follows:
    1468  * <ol>
    1469  * <li>WP_PROXY_HOST - Enable proxy support and host for connecting.</li>
    1470  * <li>WP_PROXY_PORT - Proxy port for connection. No default, must be defined.</li>
    1471  * <li>WP_PROXY_USERNAME - Proxy username, if it requires authentication.</li>
    1472  * <li>WP_PROXY_PASSWORD - Proxy password, if it requires authentication.</li>
    1473  * <li>WP_PROXY_BYPASS_HOSTS - Will prevent the hosts in this list from going through the proxy.
    1474  * You do not need to have localhost and the blog host in this list, because they will not be passed
    1475  * through the proxy. The list should be presented in a comma separated list</li>
    1476  * </ol>
    1477  *
    1478  * An example can be as seen below.
    1479  * <code>
    1480  * define('WP_PROXY_HOST', '192.168.84.101');
    1481  * define('WP_PROXY_PORT', '8080');
    1482  * define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com');
    1483  * </code>
    1484  *
    1485  * @link http://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress.
    1486  * @since 2.8
    1487  */
    1488 class WP_HTTP_Proxy {
    1489 
    1490         /**
    1491          * Whether proxy connection should be used.
    1492          *
    1493          * @since 2.8
    1494          * @use WP_PROXY_HOST
    1495          * @use WP_PROXY_PORT
    1496          *
    1497          * @return bool
    1498          */
    1499         function is_enabled() {
    1500                 return defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT');
    1501         }
    1502 
    1503         /**
    1504          * Whether authentication should be used.
    1505          *
    1506          * @since 2.8
    1507          * @use WP_PROXY_USERNAME
    1508          * @use WP_PROXY_PASSWORD
    1509          *
    1510          * @return bool
    1511          */
    1512         function use_authentication() {
    1513                 return defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD');
    1514         }
    1515 
    1516         /**
    1517          * Retrieve the host for the proxy server.
    1518          *
    1519          * @since 2.8
    1520          *
    1521          * @return string
    1522          */
    1523         function host() {
    1524                 if ( defined('WP_PROXY_HOST') )
    1525                         return WP_PROXY_HOST;
    1526 
    1527                 return '';
    1528         }
    1529 
    1530         /**
    1531          * Retrieve the port for the proxy server.
    1532          *
    1533          * @since 2.8
    1534          *
    1535          * @return string
    1536          */
    1537         function port() {
    1538                 if ( defined('WP_PROXY_PORT') )
    1539                         return WP_PROXY_PORT;
    1540 
    1541                 return '';
    1542         }
    1543 
    1544         /**
    1545          * Retrieve the username for proxy authentication.
    1546          *
    1547          * @since 2.8
    1548          *
    1549          * @return string
    1550          */
    1551         function username() {
    1552                 if ( defined('WP_PROXY_USERNAME') )
    1553                         return WP_PROXY_USERNAME;
    1554 
    1555                 return '';
    1556         }
    1557 
    1558         /**
    1559          * Retrieve the password for proxy authentication.
    1560          *
    1561          * @since 2.8
    1562          *
    1563          * @return string
    1564          */
    1565         function password() {
    1566                 if ( defined('WP_PROXY_PASSWORD') )
    1567                         return WP_PROXY_PASSWORD;
    1568 
    1569                 return '';
    1570         }
    1571 
    1572         /**
    1573          * Retrieve authentication string for proxy authentication.
    1574          *
    1575          * @since 2.8
    1576          *
    1577          * @return string
    1578          */
    1579         function authentication() {
    1580                 return $this->username() . ':' . $this->password();
    1581         }
    1582 
    1583         /**
    1584          * Retrieve header string for proxy authentication.
    1585          *
    1586          * @since 2.8
    1587          *
    1588          * @return string
    1589          */
    1590         function authentication_header() {
    1591                 return 'Proxy-Authentication: Basic ' . base64_encode( $this->authentication() );
    1592         }
    1593 
    1594         /**
    1595          * Whether URL should be sent through the proxy server.
    1596          *
    1597          * We want to keep localhost and the blog URL from being sent through the proxy server, because
    1598          * some proxies can not handle this. We also have the constant available for defining other
    1599          * hosts that won't be sent through the proxy.
    1600          *
    1601          * @uses WP_PROXY_BYPASS_HOSTS
    1602          * @since unknown
    1603          *
    1604          * @param string $uri URI to check.
    1605          * @return bool True, to send through the proxy and false if, the proxy should not be used.
    1606          */
    1607         function send_through_proxy( $uri ) {
    1608                 // parse_url() only handles http, https type URLs, and will emit E_WARNING on failure.
    1609                 // This will be displayed on blogs, which is not reasonable.
    1610                 $check = @parse_url($uri);
    1611 
    1612                 // Malformed URL, can not process, but this could mean ssl, so let through anyway.
    1613                 if ( $check === false )
    1614                         return true;
    1615 
    1616                 $home = parse_url( get_option('siteurl') );
    1617 
    1618                 if ( $check['host'] == 'localhost' || $check['host'] == $home['host'] )
    1619                         return false;
    1620 
    1621                 if ( !defined('WP_PROXY_BYPASS_HOSTS') )
    1622                         return true;
    1623 
    1624                 static $bypass_hosts;
    1625                 if ( null == $bypass_hosts )
    1626                         $bypass_hosts = preg_split('|,\s*|', WP_PROXY_BYPASS_HOSTS);
    1627 
    1628                 return !in_array( $check['host'], $bypass_hosts );
    1629         }
    1630 }
    1631 /**
    1632  * Internal representation of a single cookie.
    1633  *
    1634  * Returned cookies are represented using this class, and when cookies are set, if they are not
    1635  * already a WP_Http_Cookie() object, then they are turned into one.
    1636  *
    1637  * @todo The WordPress convention is to use underscores instead of camelCase for function and method
    1638  * names. Need to switch to use underscores instead for the methods.
    1639  *
    1640  * @package WordPress
    1641  * @subpackage HTTP
    1642  * @since 2.8.0
    1643  * @author Beau Lebens
    1644  */
    1645 class WP_Http_Cookie {
    1646 
    1647         /**
    1648          * Cookie name.
    1649          *
    1650          * @since 2.8.0
    1651          * @var string
    1652          */
    1653         var $name;
    1654 
    1655         /**
    1656          * Cookie value.
    1657          *
    1658          * @since 2.8.0
    1659          * @var string
    1660          */
    1661         var $value;
    1662 
    1663         /**
    1664          * When the cookie expires.
    1665          *
    1666          * @since 2.8.0
    1667          * @var string
    1668          */
    1669         var $expires;
    1670 
    1671         /**
    1672          * Cookie URL path.
    1673          *
    1674          * @since 2.8.0
    1675          * @var string
    1676          */
    1677         var $path;
    1678 
    1679         /**
    1680          * Cookie Domain.
    1681          *
    1682          * @since 2.8.0
    1683          * @var string
    1684          */
    1685         var $domain;
    1686 
    1687         /**
    1688          * PHP4 style Constructor - Calls PHP5 Style Constructor.
    1689          *
    1690          * @access public
    1691          * @since 2.8.0
    1692          * @param string|array $data Raw cookie data.
    1693          */
    1694         function WP_Http_Cookie( $data ) {
    1695                 $this->__construct( $data );
    1696         }
    1697 
    1698         /**
    1699          * Sets up this cookie object.
    1700          *
    1701          * The parameter $data should be either an associative array containing the indices names below
    1702          * or a header string detailing it.
    1703          *
    1704          * If it's an array, it should include the following elements:
    1705          * <ol>
    1706          * <li>Name</li>
    1707          * <li>Value - should NOT be urlencoded already.</li>
    1708          * <li>Expires - (optional) String or int (UNIX timestamp).</li>
    1709          * <li>Path (optional)</li>
    1710          * <li>Domain (optional)</li>
    1711          * </ol>
    1712          *
    1713          * @access public
    1714          * @since 2.8.0
    1715          *
    1716          * @param string|array $data Raw cookie data.
    1717          */
    1718         function __construct( $data ) {
    1719                 if ( is_string( $data ) ) {
    1720                         // Assume it's a header string direct from a previous request
    1721                         $pairs = explode( ';', $data );
    1722 
    1723                         // Special handling for first pair; name=value. Also be careful of "=" in value
    1724                         $name  = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
    1725                         $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
    1726                         $this->name  = $name;
    1727                         $this->value = urldecode( $value );
    1728                         array_shift( $pairs ); //Removes name=value from items.
    1729 
    1730                         // Set everything else as a property
    1731                         foreach ( $pairs as $pair ) {
    1732                                 if ( empty($pair) ) //Handles the cookie ending in ; which results in a empty final pair
    1733                                         continue;
    1734 
    1735                                 list( $key, $val ) = explode( '=', $pair );
    1736                                 $key = strtolower( trim( $key ) );
    1737                                 if ( 'expires' == $key )
    1738                                         $val = strtotime( $val );
    1739                                 $this->$key = $val;
    1740                         }
    1741                 } else {
    1742                         if ( !isset( $data['name'] ) )
    1743                                 return false;
    1744 
    1745                         // Set properties based directly on parameters
    1746                         $this->name   = $data['name'];
    1747                         $this->value  = isset( $data['value'] ) ? $data['value'] : '';
    1748                         $this->path   = isset( $data['path'] ) ? $data['path'] : '';
    1749                         $this->domain = isset( $data['domain'] ) ? $data['domain'] : '';
    1750 
    1751                         if ( isset( $data['expires'] ) )
    1752                                 $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
    1753                         else
    1754                                 $this->expires = null;
    1755                 }
    1756         }
    1757 
    1758         /**
    1759          * Confirms that it's OK to send this cookie to the URL checked against.
    1760          *
    1761          * Decision is based on RFC 2109/2965, so look there for details on validity.
    1762          *
    1763          * @access public
    1764          * @since 2.8.0
    1765          *
    1766          * @param string $url URL you intend to send this cookie to
    1767          * @return boolean TRUE if allowed, FALSE otherwise.
    1768          */
    1769         function test( $url ) {
    1770                 // Expires - if expired then nothing else matters
    1771                 if ( time() > $this->expires )
    1772                         return false;
    1773 
    1774                 // Get details on the URL we're thinking about sending to
    1775                 $url = parse_url( $url );
    1776                 $url['port'] = isset( $url['port'] ) ? $url['port'] : 80;
    1777                 $url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
    1778 
    1779                 // Values to use for comparison against the URL
    1780                 $path   = isset( $this->path )   ? $this->path   : '/';
    1781                 $port   = isset( $this->port )   ? $this->port   : 80;
    1782                 $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
    1783                 if ( false === stripos( $domain, '.' ) )
    1784                         $domain .= '.local';
    1785 
    1786                 // Host - very basic check that the request URL ends with the domain restriction (minus leading dot)
    1787                 $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
    1788                 if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
    1789                         return false;
    1790 
    1791                 // Port - supports "port-lists" in the format: "80,8000,8080"
    1792                 if ( !in_array( $url['port'], explode( ',', $port) ) )
    1793                         return false;
    1794 
    1795                 // Path - request path must start with path restriction
    1796                 if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
    1797                         return false;
    1798 
    1799                 return true;
    1800         }
    1801 
    1802         /**
    1803          * Convert cookie name and value back to header string.
    1804          *
    1805          * @access public
    1806          * @since 2.8.0
    1807          *
    1808          * @return string Header encoded cookie name and value.
    1809          */
    1810         function getHeaderValue() {
    1811                 if ( empty( $this->name ) || empty( $this->value ) )
    1812                         return '';
    1813 
    1814                 return $this->name . '=' . urlencode( $this->value );
    1815         }
    1816 
    1817         /**
    1818          * Retrieve cookie header for usage in the rest of the WordPress HTTP API.
    1819          *
    1820          * @access public
    1821          * @since 2.8.0
    1822          *
    1823          * @return string
    1824          */
    1825         function getFullHeader() {
    1826                 return 'Cookie: ' . $this->getHeaderValue();
    1827         }
    1828 }
    1829 
    1830 /**
    1831  * Implementation for deflate and gzip transfer encodings.
    1832  *
    1833  * Includes RFC 1950, RFC 1951, and RFC 1952.
    1834  *
    1835  * @since 2.8
    1836  * @package WordPress
    1837  * @subpackage HTTP
    1838  */
    1839 class WP_Http_Encoding {
    1840 
    1841         /**
    1842          * Compress raw string using the deflate format.
    1843          *
    1844          * Supports the RFC 1951 standard.
    1845          *
    1846          * @since 2.8
    1847          *
    1848          * @param string $raw String to compress.
    1849          * @param int $level Optional, default is 9. Compression level, 9 is highest.
    1850          * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports.
    1851          * @return string|bool False on failure.
    1852          */
    1853         function compress( $raw, $level = 9, $supports = null ) {
    1854                 return gzdeflate( $raw, $level );
    1855         }
    1856 
    1857         /**
    1858          * Decompression of deflated string.
    1859          *
    1860          * Will attempt to decompress using the RFC 1950 standard, and if that fails
    1861          * then the RFC 1951 standard deflate will be attempted. Finally, the RFC
    1862          * 1952 standard gzip decode will be attempted. If all fail, then the
    1863          * original compressed string will be returned.
    1864          *
    1865          * @since 2.8
    1866          *
    1867          * @param string $compressed String to decompress.
    1868          * @param int $length The optional length of the compressed data.
    1869          * @return string|bool False on failure.
    1870          */
    1871         function decompress( $compressed, $length = null ) {
    1872 
    1873                 if ( empty($compressed) )
    1874                         return $compressed;
    1875 
    1876                 if ( false !== ( $decompressed = @gzinflate( $compressed ) ) )
    1877                         return $decompressed;
    1878 
    1879                 if ( false !== ( $decompressed = WP_Http_Encoding::compatible_gzinflate( $compressed ) ) )
    1880                         return $decompressed;
    1881 
    1882                 if ( false !== ( $decompressed = @gzuncompress( $compressed ) ) )
    1883                         return $decompressed;
    1884 
    1885                 if ( function_exists('gzdecode') ) {
    1886                         $decompressed = @gzdecode( $compressed );
    1887 
    1888                         if ( false !== $decompressed )
    1889                                 return $decompressed;
    1890                 }
    1891 
    1892                 return $compressed;
    1893         }
    1894 
    1895         /**
    1896          * Decompression of deflated string while staying compatible with the majority of servers.
    1897          *
    1898          * Certain Servers will return deflated data with headers which PHP's gziniflate()
    1899          * function cannot handle out of the box. The following function lifted from
    1900          * http://au2.php.net/manual/en/function.gzinflate.php#77336 will attempt to deflate
    1901          * the various return forms used.
    1902          *
    1903          * @since 2.8.1
    1904          * @link http://au2.php.net/manual/en/function.gzinflate.php#77336
    1905          *
    1906          * @param string $gzData String to decompress.
    1907          * @return string|bool False on failure.
    1908          */
    1909         function compatible_gzinflate($gzData) {
    1910                 if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) {
    1911                         $i = 10;
    1912                         $flg = ord( substr($gzData, 3, 1) );
    1913                         if ( $flg > 0 ) {
    1914                                 if ( $flg & 4 ) {
    1915                                         list($xlen) = unpack('v', substr($gzData, $i, 2) );
    1916                                         $i = $i + 2 + $xlen;
    1917                                 }
    1918                                 if ( $flg & 8 )
    1919                                         $i = strpos($gzData, "\0", $i) + 1;
    1920                                 if ( $flg & 16 )
    1921                                         $i = strpos($gzData, "\0", $i) + 1;
    1922                                 if ( $flg & 2 )
    1923                                         $i = $i + 2;
    1924                         }
    1925                         return gzinflate( substr($gzData, $i, -8) );
    1926                 } else {
    1927                         return false;
    1928                 }
    1929         }
    1930 
    1931         /**
    1932          * What encoding types to accept and their priority values.
    1933          *
    1934          * @since 2.8
    1935          *
    1936          * @return string Types of encoding to accept.
    1937          */
    1938         function accept_encoding() {
    1939                 $type = array();
    1940                 if ( function_exists( 'gzinflate' ) )
    1941                         $type[] = 'deflate;q=1.0';
    1942 
    1943                 if ( function_exists( 'gzuncompress' ) )
    1944                         $type[] = 'compress;q=0.5';
    1945 
    1946                 if ( function_exists( 'gzdecode' ) )
    1947                         $type[] = 'gzip;q=0.5';
    1948 
    1949                 return implode(', ', $type);
    1950         }
    1951 
    1952         /**
    1953          * What enconding the content used when it was compressed to send in the headers.
    1954          *
    1955          * @since 2.8
    1956          *
    1957          * @return string Content-Encoding string to send in the header.
    1958          */
    1959         function content_encoding() {
    1960                 return 'deflate';
    1961         }
    1962 
    1963         /**
    1964          * Whether the content be decoded based on the headers.
    1965          *
    1966          * @since 2.8
    1967          *
    1968          * @param array|string $headers All of the available headers.
    1969          * @return bool
    1970          */
    1971         function should_decode($headers) {
    1972                 if ( is_array( $headers ) ) {
    1973                         if ( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) )
    1974                                 return true;
    1975                 } else if ( is_string( $headers ) ) {
    1976                         return ( stripos($headers, 'content-encoding:') !== false );
    1977                 }
    1978 
    1979                 return false;
    1980         }
    1981 
    1982         /**
    1983          * Whether decompression and compression are supported by the PHP version.
    1984          *
    1985          * Each function is tested instead of checking for the zlib extension, to
    1986          * ensure that the functions all exist in the PHP version and aren't
    1987          * disabled.
    1988          *
    1989          * @since 2.8
    1990          *
    1991          * @return bool
    1992          */
    1993         function is_available() {
    1994                 return ( function_exists('gzuncompress') || function_exists('gzdeflate') || function_exists('gzinflate') );
    1995         }
    1996 }
    1997 
    1998 /**
    199916 * Returns the initialized WP_Http Object
    200017 *
     
    200623function &_wp_http_get_object() {
    200724        static $http;
     25
     26        if ( ! class_exists('WP_Http') )
     27                require( ABSPATH . WPINC . '/class-http.php' );
    200828
    200929        if ( is_null($http) )
Note: See TracChangeset for help on using the changeset viewer.