WordPress.org

Make WordPress Core

Changeset 13274


Ignore:
Timestamp:
02/21/10 02:06:24 (4 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.