WordPress.org

Make WordPress Core

Changeset 13274


Ignore:
Timestamp:
02/21/2010 02:06:24 AM (12 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.