WordPress.org

Make WordPress Core

Ticket #11559: 11559.diff

File 11559.diff, 145.1 KB (added by sivel, 12 years ago)

First attempt at separation, if indeed we are interested in doing this.

  • wp-includes/class.http.php

     
     1<?php
     2/**
     3 * Simple and uniform HTTP request API.
     4 *
     5 * Will eventually replace and standardize the WordPress HTTP requests made.
     6 *
     7 * @link http://trac.wordpress.org/ticket/4779 HTTP API Proposal
     8 *
     9 * @package WordPress
     10 * @subpackage HTTP
     11 * @since 2.7.0
     12 * @author Jacob Santos <wordpress@santosj.name>
     13 */
     14
     15/**
     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 */
     41class 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 */
     599class 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 */
     804class 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 */
     953class 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 */
     1124class 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 */
     1271class 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 */
     1488class 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 */
     1645class 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 */
     1839class 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?>
  • wp-includes/functions.http.php

     
     1<?php
     2/**
     3 * Returns the initialized WP_Http Object
     4 *
     5 * @since 2.7.0
     6 * @access private
     7 *
     8 * @return WP_Http HTTP Transport object.
     9 */
     10function &_wp_http_get_object() {
     11        static $http;
     12
     13        if ( is_null($http) ) {
     14                require_once(ABSPATH . WPINC . '/class.http.php');
     15                $http = new WP_Http();
     16        }
     17
     18        return $http;
     19}
     20
     21/**
     22 * Retrieve the raw response from the HTTP request.
     23 *
     24 * The array structure is a little complex.
     25 *
     26 * <code>
     27 * $res = array( 'headers' => array(), 'response' => array('code' => int, 'message' => string) );
     28 * </code>
     29 *
     30 * All of the headers in $res['headers'] are with the name as the key and the
     31 * value as the value. So to get the User-Agent, you would do the following.
     32 *
     33 * <code>
     34 * $user_agent = $res['headers']['user-agent'];
     35 * </code>
     36 *
     37 * The body is the raw response content and can be retrieved from $res['body'].
     38 *
     39 * This function is called first to make the request and there are other API
     40 * functions to abstract out the above convoluted setup.
     41 *
     42 * @since 2.7.0
     43 *
     44 * @param string $url Site URL to retrieve.
     45 * @param array $args Optional. Override the defaults.
     46 * @return WP_Error|array The response or WP_Error on failure.
     47 */
     48function wp_remote_request($url, $args = array()) {
     49        $objFetchSite = _wp_http_get_object();
     50        return $objFetchSite->request($url, $args);
     51}
     52
     53/**
     54 * Retrieve the raw response from the HTTP request using the GET method.
     55 *
     56 * @see wp_remote_request() For more information on the response array format.
     57 *
     58 * @since 2.7.0
     59 *
     60 * @param string $url Site URL to retrieve.
     61 * @param array $args Optional. Override the defaults.
     62 * @return WP_Error|array The response or WP_Error on failure.
     63 */
     64function wp_remote_get($url, $args = array()) {
     65        $objFetchSite = _wp_http_get_object();
     66        return $objFetchSite->get($url, $args);
     67}
     68
     69/**
     70 * Retrieve the raw response from the HTTP request using the POST method.
     71 *
     72 * @see wp_remote_request() For more information on the response array format.
     73 *
     74 * @since 2.7.0
     75 *
     76 * @param string $url Site URL to retrieve.
     77 * @param array $args Optional. Override the defaults.
     78 * @return WP_Error|array The response or WP_Error on failure.
     79 */
     80function wp_remote_post($url, $args = array()) {
     81        $objFetchSite = _wp_http_get_object();
     82        return $objFetchSite->post($url, $args);
     83}
     84
     85/**
     86 * Retrieve the raw response from the HTTP request using the HEAD method.
     87 *
     88 * @see wp_remote_request() For more information on the response array format.
     89 *
     90 * @since 2.7.0
     91 *
     92 * @param string $url Site URL to retrieve.
     93 * @param array $args Optional. Override the defaults.
     94 * @return WP_Error|array The response or WP_Error on failure.
     95 */
     96function wp_remote_head($url, $args = array()) {
     97        $objFetchSite = _wp_http_get_object();
     98        return $objFetchSite->head($url, $args);
     99}
     100
     101/**
     102 * Retrieve only the headers from the raw response.
     103 *
     104 * @since 2.7.0
     105 *
     106 * @param array $response HTTP response.
     107 * @return array The headers of the response. Empty array if incorrect parameter given.
     108 */
     109function wp_remote_retrieve_headers(&$response) {
     110        if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
     111                return array();
     112
     113        return $response['headers'];
     114}
     115
     116/**
     117 * Retrieve a single header by name from the raw response.
     118 *
     119 * @since 2.7.0
     120 *
     121 * @param array $response
     122 * @param string $header Header name to retrieve value from.
     123 * @return string The header value. Empty string on if incorrect parameter given, or if the header doesnt exist.
     124 */
     125function wp_remote_retrieve_header(&$response, $header) {
     126        if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
     127                return '';
     128
     129        if ( array_key_exists($header, $response['headers']) )
     130                return $response['headers'][$header];
     131
     132        return '';
     133}
     134
     135/**
     136 * Retrieve only the response code from the raw response.
     137 *
     138 * Will return an empty array if incorrect parameter value is given.
     139 *
     140 * @since 2.7.0
     141 *
     142 * @param array $response HTTP response.
     143 * @return string the response code. Empty string on incorrect parameter given.
     144 */
     145function wp_remote_retrieve_response_code(&$response) {
     146        if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
     147                return '';
     148
     149        return $response['response']['code'];
     150}
     151
     152/**
     153 * Retrieve only the response message from the raw response.
     154 *
     155 * Will return an empty array if incorrect parameter value is given.
     156 *
     157 * @since 2.7.0
     158 *
     159 * @param array $response HTTP response.
     160 * @return string The response message. Empty string on incorrect parameter given.
     161 */
     162function wp_remote_retrieve_response_message(&$response) {
     163        if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
     164                return '';
     165
     166        return $response['response']['message'];
     167}
     168
     169/**
     170 * Retrieve only the body from the raw response.
     171 *
     172 * @since 2.7.0
     173 *
     174 * @param array $response HTTP response.
     175 * @return string The body of the response. Empty string if no body or incorrect parameter given.
     176 */
     177function wp_remote_retrieve_body(&$response) {
     178        if ( is_wp_error($response) || ! isset($response['body']) )
     179                return '';
     180
     181        return $response['body'];
     182}
     183
     184?>
  • wp-settings.php

     
    123123require( ABSPATH . WPINC . '/canonical.php' );
    124124require( ABSPATH . WPINC . '/shortcodes.php' );
    125125require( ABSPATH . WPINC . '/media.php' );
    126 require( ABSPATH . WPINC . '/http.php' );
     126require( ABSPATH . WPINC . '/functions.http.php' );
    127127require( ABSPATH . WPINC . '/widgets.php' );
    128128
    129129// Load multisite-specific files.
  • wp-admin/admin.php

     
    3939                 */
    4040                $c = get_blog_count();
    4141                if ( $c <= 50 || ( $c > 50 && mt_rand( 0, (int)( $c / 50 ) ) == 1 ) ) {
    42                         require_once( ABSPATH . WPINC . '/http.php' );
     42                        require_once( ABSPATH . WPINC . '/functions.http.php' );
    4343                        $response = wp_remote_get( admin_url( 'upgrade.php?step=1' ), array( 'timeout' => 120, 'httpversion' => '1.1' ) );
    4444                        do_action( 'after_mu_upgrade', $response );
    4545                        unset($response);
  • wp-admin/ms-upgrade-site.php

     
    44if ( !is_multisite() )
    55        wp_die( __('Multisite support is not enabled.') );
    66
    7 require_once( ABSPATH . WPINC . '/http.php' );
     7require_once( ABSPATH . WPINC . '/functions.http.php' );
    88
    99$title = __('Upgrade Site');
    1010$parent_file = 'ms-admin.php';
  • wp-admin/setup-config.php

     
    174174                wp_die($wpdb->error->get_error_message());
    175175
    176176        require_once( ABSPATH . WPINC . '/plugin.php' );
    177         require_once( ABSPATH . WPINC . '/http.php' );
     177        require_once( ABSPATH . WPINC . '/functions.http.php' );
    178178        wp_fix_server_vars();
    179179        /**#@+
    180180         * @ignore
  • wp-includes/http.php

     
    1 <?php
    2 /**
    3  * Simple and uniform HTTP request API.
    4  *
    5  * Will eventually replace and standardize the WordPress HTTP requests made.
    6  *
    7  * @link http://trac.wordpress.org/ticket/4779 HTTP API Proposal
    8  *
    9  * @package WordPress
    10  * @subpackage HTTP
    11  * @since 2.7.0
    12  * @author Jacob Santos <wordpress@santosj.name>
    13  */
    14 
    15 /**
    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 /**
    1999  * Returns the initialized WP_Http Object
    2000  *
    2001  * @since 2.7.0
    2002  * @access private
    2003  *
    2004  * @return WP_Http HTTP Transport object.
    2005  */
    2006 function &_wp_http_get_object() {
    2007         static $http;
    2008 
    2009         if ( is_null($http) )
    2010                 $http = new WP_Http();
    2011 
    2012         return $http;
    2013 }
    2014 
    2015 /**
    2016  * Retrieve the raw response from the HTTP request.
    2017  *
    2018  * The array structure is a little complex.
    2019  *
    2020  * <code>
    2021  * $res = array( 'headers' => array(), 'response' => array('code' => int, 'message' => string) );
    2022  * </code>
    2023  *
    2024  * All of the headers in $res['headers'] are with the name as the key and the
    2025  * value as the value. So to get the User-Agent, you would do the following.
    2026  *
    2027  * <code>
    2028  * $user_agent = $res['headers']['user-agent'];
    2029  * </code>
    2030  *
    2031  * The body is the raw response content and can be retrieved from $res['body'].
    2032  *
    2033  * This function is called first to make the request and there are other API
    2034  * functions to abstract out the above convoluted setup.
    2035  *
    2036  * @since 2.7.0
    2037  *
    2038  * @param string $url Site URL to retrieve.
    2039  * @param array $args Optional. Override the defaults.
    2040  * @return WP_Error|array The response or WP_Error on failure.
    2041  */
    2042 function wp_remote_request($url, $args = array()) {
    2043         $objFetchSite = _wp_http_get_object();
    2044         return $objFetchSite->request($url, $args);
    2045 }
    2046 
    2047 /**
    2048  * Retrieve the raw response from the HTTP request using the GET method.
    2049  *
    2050  * @see wp_remote_request() For more information on the response array format.
    2051  *
    2052  * @since 2.7.0
    2053  *
    2054  * @param string $url Site URL to retrieve.
    2055  * @param array $args Optional. Override the defaults.
    2056  * @return WP_Error|array The response or WP_Error on failure.
    2057  */
    2058 function wp_remote_get($url, $args = array()) {
    2059         $objFetchSite = _wp_http_get_object();
    2060         return $objFetchSite->get($url, $args);
    2061 }
    2062 
    2063 /**
    2064  * Retrieve the raw response from the HTTP request using the POST method.
    2065  *
    2066  * @see wp_remote_request() For more information on the response array format.
    2067  *
    2068  * @since 2.7.0
    2069  *
    2070  * @param string $url Site URL to retrieve.
    2071  * @param array $args Optional. Override the defaults.
    2072  * @return WP_Error|array The response or WP_Error on failure.
    2073  */
    2074 function wp_remote_post($url, $args = array()) {
    2075         $objFetchSite = _wp_http_get_object();
    2076         return $objFetchSite->post($url, $args);
    2077 }
    2078 
    2079 /**
    2080  * Retrieve the raw response from the HTTP request using the HEAD method.
    2081  *
    2082  * @see wp_remote_request() For more information on the response array format.
    2083  *
    2084  * @since 2.7.0
    2085  *
    2086  * @param string $url Site URL to retrieve.
    2087  * @param array $args Optional. Override the defaults.
    2088  * @return WP_Error|array The response or WP_Error on failure.
    2089  */
    2090 function wp_remote_head($url, $args = array()) {
    2091         $objFetchSite = _wp_http_get_object();
    2092         return $objFetchSite->head($url, $args);
    2093 }
    2094 
    2095 /**
    2096  * Retrieve only the headers from the raw response.
    2097  *
    2098  * @since 2.7.0
    2099  *
    2100  * @param array $response HTTP response.
    2101  * @return array The headers of the response. Empty array if incorrect parameter given.
    2102  */
    2103 function wp_remote_retrieve_headers(&$response) {
    2104         if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
    2105                 return array();
    2106 
    2107         return $response['headers'];
    2108 }
    2109 
    2110 /**
    2111  * Retrieve a single header by name from the raw response.
    2112  *
    2113  * @since 2.7.0
    2114  *
    2115  * @param array $response
    2116  * @param string $header Header name to retrieve value from.
    2117  * @return string The header value. Empty string on if incorrect parameter given, or if the header doesnt exist.
    2118  */
    2119 function wp_remote_retrieve_header(&$response, $header) {
    2120         if ( is_wp_error($response) || ! isset($response['headers']) || ! is_array($response['headers']))
    2121                 return '';
    2122 
    2123         if ( array_key_exists($header, $response['headers']) )
    2124                 return $response['headers'][$header];
    2125 
    2126         return '';
    2127 }
    2128 
    2129 /**
    2130  * Retrieve only the response code from the raw response.
    2131  *
    2132  * Will return an empty array if incorrect parameter value is given.
    2133  *
    2134  * @since 2.7.0
    2135  *
    2136  * @param array $response HTTP response.
    2137  * @return string the response code. Empty string on incorrect parameter given.
    2138  */
    2139 function wp_remote_retrieve_response_code(&$response) {
    2140         if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
    2141                 return '';
    2142 
    2143         return $response['response']['code'];
    2144 }
    2145 
    2146 /**
    2147  * Retrieve only the response message from the raw response.
    2148  *
    2149  * Will return an empty array if incorrect parameter value is given.
    2150  *
    2151  * @since 2.7.0
    2152  *
    2153  * @param array $response HTTP response.
    2154  * @return string The response message. Empty string on incorrect parameter given.
    2155  */
    2156 function wp_remote_retrieve_response_message(&$response) {
    2157         if ( is_wp_error($response) || ! isset($response['response']) || ! is_array($response['response']))
    2158                 return '';
    2159 
    2160         return $response['response']['message'];
    2161 }
    2162 
    2163 /**
    2164  * Retrieve only the body from the raw response.
    2165  *
    2166  * @since 2.7.0
    2167  *
    2168  * @param array $response HTTP response.
    2169  * @return string The body of the response. Empty string if no body or incorrect parameter given.
    2170  */
    2171 function wp_remote_retrieve_body(&$response) {
    2172         if ( is_wp_error($response) || ! isset($response['body']) )
    2173                 return '';
    2174 
    2175         return $response['body'];
    2176 }
    2177 
    2178 ?>
    2179  No newline at end of file