WordPress.org

Make WordPress Core

Ticket #8927: 8927.diff

File 8927.diff, 16.2 KB (added by jacobsantos, 9 years ago)

Has HTTP API cleanup and adds capability to block requests through use of constants.

  • http.php

     
    228228                static $working_transport, $blocking_transport, $nonblocking_transport;
    229229
    230230                if ( is_null($working_transport) ) {
    231                         if ( true === WP_Http_ExtHttp::test() && apply_filters('use_http_extension_transport', true) ) {
     231                        if ( true === WP_Http_ExtHttp::test() ) {
    232232                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
    233233                                $blocking_transport[] = &$working_transport['exthttp'];
    234                         } else if ( true === WP_Http_Curl::test() && apply_filters('use_curl_transport', true) ) {
     234                        } else if ( true === WP_Http_Curl::test() ) {
    235235                                $working_transport['curl'] = new WP_Http_Curl();
    236236                                $blocking_transport[] = &$working_transport['curl'];
    237                         } else if ( true === WP_Http_Streams::test() && apply_filters('use_streams_transport', true) ) {
     237                        } else if ( true === WP_Http_Streams::test() ) {
    238238                                $working_transport['streams'] = new WP_Http_Streams();
    239239                                $blocking_transport[] = &$working_transport['streams'];
    240                         } else if ( true === WP_Http_Fopen::test() && apply_filters('use_fopen_transport', true) && ( isset($args['ssl']) && !$args['ssl'] ) ) {
     240                        } else if ( true === WP_Http_Fopen::test() && ( isset($args['ssl']) && !$args['ssl'] ) ) {
    241241                                $working_transport['fopen'] = new WP_Http_Fopen();
    242242                                $blocking_transport[] = &$working_transport['fopen'];
    243                         } else if ( true === WP_Http_Fsockopen::test() && apply_filters('use_fsockopen_transport', true) && ( isset($args['ssl']) && !$args['ssl'] ) ) {
     243                        } else if ( true === WP_Http_Fsockopen::test() && ( isset($args['ssl']) && !$args['ssl'] ) ) {
    244244                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    245245                                $blocking_transport[] = &$working_transport['fsockopen'];
    246246                        }
     
    279279                static $working_transport, $blocking_transport, $nonblocking_transport;
    280280
    281281                if ( is_null($working_transport) ) {
    282                         if ( true === WP_Http_ExtHttp::test() && apply_filters('use_http_extension_transport', true) ) {
     282                        if ( true === WP_Http_ExtHttp::test() ) {
    283283                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
    284284                                $blocking_transport[] = &$working_transport['exthttp'];
    285                         } else if ( true === WP_Http_Curl::test() && apply_filters('use_curl_transport', true) ) {
     285                        } else if ( true === WP_Http_Curl::test() ) {
    286286                                $working_transport['curl'] = new WP_Http_Curl();
    287287                                $blocking_transport[] = &$working_transport['curl'];
    288                         } else if ( true === WP_Http_Streams::test() && apply_filters('use_streams_transport', true) ) {
     288                        } else if ( true === WP_Http_Streams::test() ) {
    289289                                $working_transport['streams'] = new WP_Http_Streams();
    290290                                $blocking_transport[] = &$working_transport['streams'];
    291                         } else if ( true === WP_Http_Fsockopen::test() && apply_filters('use_fsockopen_transport', true) && ( isset($args['ssl']) && !$args['ssl'] ) ) {
     291                        } else if ( true === WP_Http_Fsockopen::test() && ( isset($args['ssl']) && !$args['ssl'] ) ) {
    292292                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    293293                                $blocking_transport[] = &$working_transport['fsockopen'];
    294294                        }
     
    376376
    377377                $arrURL = parse_url($url);
    378378
     379                if ( $this->block_request( $url ) )
     380                        return new WP_Error('http_request_failed', 'User has blocked requests through HTTP.');
     381
    379382                // Determine if this is a https call and pass that on to the transport functions
    380383                // so that we can blacklist the transports that do not support ssl verification
    381384                if ( $arrURL['scheme'] == 'https' || $arrURL['scheme'] == 'ssl' )
     
    400403                        $r['user-agent'] = $r['headers']['user-agent'];
    401404                        unset($r['headers']['user-agent']);
    402405                }
    403                
     406
    404407                // Construct Cookie: header if any cookies are set
    405408                WP_Http::buildCookieHeader( $r );
    406409
     
    514517        /**
    515518         * Transform header string into an array.
    516519         *
    517          * If an array is given then it is assumed to be raw header data with
    518          * numeric keys with the headers as the values. No headers must be passed
    519          * that were already processed.
     520         * If an array is given then it is assumed to be raw header data with numeric keys with the
     521         * headers as the values. No headers must be passed that were already processed.
    520522         *
    521523         * @access public
    522524         * @static
     
    564566       
    565567        /**
    566568         * Takes the arguments for a ::request() and checks for the cookie array.
    567          * If it's found, then it's assumed to contain WP_Http_Cookie objects, which
    568          * are each parsed into strings and added to the Cookie: header (within the
    569          * arguments array). Edits the array by reference.
    570569         *
     570         * If it's found, then it's assumed to contain WP_Http_Cookie objects, which are each parsed
     571         * into strings and added to the Cookie: header (within the arguments array). Edits the array by
     572         * reference.
     573         *
    571574         * @access public
     575         * @version 2.8.0
    572576         * @static
    573577         *
    574578         * @param array $r Full array of args passed into ::request()
     
    583587                        $r['headers']['cookie'] = $cookies_header;
    584588                }
    585589        }
    586        
     590
    587591        /**
    588592         * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
    589593         *
     
    630634                        }
    631635                }
    632636        }
     637
     638        /**
     639         * Block requests through the proxy.
     640         *
     641         * Those who are behind a proxy and want to prevent access to certain hosts may do so. This will
     642         * prevent plugins from working and core functionality, if you don't include api.wordpress.org.
     643         *
     644         * You block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL in your wp-config.php file
     645         * and this will only allow localhost and your blog to make requests. The constant
     646         * WP_ACCESSABLE_HOSTS will allow additional hosts to go through for requests.
     647         *
     648         * @since unknown
     649         * @link http://core.trac.wordpress.org/ticket/8927 Allow preventing external requests.
     650         *
     651         * @param string $uri URI of url.
     652         * @return bool True to block, false to allow.
     653         */
     654        function block_request($uri) {
     655                // We don't need to block requests, because nothing is blocked.
     656                if ( ! defined('WP_HTTP_BLOCK_EXTERNAL') || ( defined('WP_HTTP_BLOCK_EXTERNAL') && WP_HTTP_BLOCK_EXTERNAL == false ) )
     657                        return false;
     658
     659                // parse_url() only handles http, https type URLs, and will emit E_WARNING on failure.
     660                // This will be displayed on blogs, which is not reasonable.
     661                $check = @parse_url($uri);
     662
     663                /* Malformed URL, can not process, but this could mean ssl, so let through anyway.
     664                 *
     665                 * This isn't very security sound. There are instances where a hacker might attempt
     666                 * to bypass the proxy and this check. However, the reason for this behavior is that
     667                 * WordPress does not do any checking currently for non-proxy requests, so it is keeps with
     668                 * the default unsecure nature of the HTTP request.
     669                 */
     670                if ( $check === false )
     671                        return false;
     672
     673                $home = parse_url( get_bloginfo('site_url') );
     674
     675                if ( $uri == 'localhost' || $uri == $home['host'] )
     676                        return false;
     677
     678                if ( defined('WP_ACCESSABLE_HOSTS') && is_array( WP_ACCESSABLE_HOSTS ) && in_array( $check['host'], WP_ACCESSABLE_HOSTS ) ) {
     679                                return false;
     680                }
     681
     682                return true;
     683        }
    633684}
    634685
    635686/**
     
    798849                if ( false !== ($option = get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours
    799850                        return false;
    800851
    801                 if ( function_exists( 'fsockopen' ) )
     852                if ( function_exists( 'fsockopen' ) && apply_filters('use_fsockopen_transport', true) )
    802853                        return true;
    803854
    804855                return false;
     
    911962                if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )
    912963                        return false;
    913964
    914                 return true;
     965                return apply_filters('use_fopen_transport', true);
    915966        }
    916967}
    917968
     
    9551006                        $r['user-agent'] = $r['headers']['user-agent'];
    9561007                        unset($r['headers']['user-agent']);
    9571008                }
    958                
     1009
    9591010                // Construct Cookie: header if any cookies are set
    9601011                WP_Http::buildCookieHeader( $r );
    9611012
     
    9841035                                'header' => $strHeaders,
    9851036                                'timeout' => $r['timeout'],
    9861037                                'ssl' => array(
    987                                         'verify_peer' => apply_filters('https_ssl_verify', $r['sslverify']),
    988                                         'verify_host' => apply_filters('https_ssl_verify', $r['sslverify'])
    989                                 )
     1038                                                'verify_peer' => apply_filters('https_ssl_verify', $r['sslverify']),
     1039                                                'verify_host' => apply_filters('https_ssl_verify', $r['sslverify'])
     1040                                )
    9901041                        )
    9911042                );
    9921043
     
    10491100                if ( version_compare(PHP_VERSION, '5.0', '<') )
    10501101                        return false;
    10511102
    1052                 return true;
     1103                return apply_filters('use_streams_transport', true);
    10531104        }
    10541105}
    10551106
     
    10991150                // Construct Cookie: header if any cookies are set
    11001151                WP_Http::buildCookieHeader( $r );
    11011152
     1153                // Construct Cookie: header if any cookies are set
     1154                WP_Http::buildCookieHeader( $r );
     1155
    11021156                switch ( $r['method'] ) {
    11031157                        case 'POST':
    11041158                                $r['method'] = HTTP_METH_POST;
     
    11331187                else
    11341188                        $strResponse = http_request($r['method'], $url, $r['body'], $options, $info); //Emits warning level notices for max redirects and timeouts
    11351189
    1136                 if ( false === $strResponse || ! empty($info['error']) ) //Error may still be set, Response may return headers or partial document, and error contains a reason the request was aborted, eg, timeout expired or max-redirects reached
     1190                // Error may still be set, Response may return headers or partial document, and error
     1191                // contains a reason the request was aborted, eg, timeout expired or max-redirects reached.
     1192                if ( false === $strResponse || ! empty($info['error']) )
    11371193                        return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
    11381194
    11391195                if ( ! $r['blocking'] )
     
    11681224         * @return boolean False means this class can not be used, true means it can.
    11691225         */
    11701226        function test() {
    1171                 if ( function_exists('http_request') )
     1227                if ( function_exists('http_request') && apply_filters('use_http_extension_transport', true) )
    11721228                        return true;
    11731229
    11741230                return false;
     
    11851241 * @since 2.7
    11861242 */
    11871243class WP_Http_Curl {
     1244
    11881245        /**
    11891246         * Send a HTTP request to a URI using cURL extension.
    11901247         *
     
    12121269                        $r['user-agent'] = $r['headers']['user-agent'];
    12131270                        unset($r['headers']['user-agent']);
    12141271                }
    1215                
     1272
    12161273                // Construct Cookie: header if any cookies are set
    12171274                WP_Http::buildCookieHeader( $r );
    12181275
     
    13201377         * @return boolean False means this class can not be used, true means it can.
    13211378         */
    13221379        function test() {
    1323                 if ( function_exists('curl_init') && function_exists('curl_exec') )
     1380                if ( function_exists('curl_init') && function_exists('curl_exec') && apply_filters('use_curl_transport', true) )
    13241381                        return true;
    13251382
    13261383                return false;
     
    13291386
    13301387
    13311388/**
    1332  * Internal representation of a cookie.
     1389 * Internal representation of a single cookie.
    13331390 *
    13341391 * Returned cookies are represented using this class, and when cookies are
    13351392 * set, if they are not already a WP_Http_Cookie() object, then they are turned
    13361393 * into one.
    13371394 *
     1395 * @todo The WordPress convention is to use underscores instead of camelCase for function and method
     1396 * names. Need to switch to use underscores instead for the methods.
     1397 *
    13381398 * @package WordPress
    13391399 * @subpackage HTTP
     1400 * @since 2.8.0
     1401 * @author Beau Lebens
    13401402 */
    13411403class WP_Http_Cookie {
    1342         var $name,
    1343                 $value,
    1344                 $expires,
    1345                 $path,
    1346                 $domain;
    1347        
     1404
    13481405        /**
    1349          * PHP4 style Constructor - Calls PHP5 Style Constructor
     1406         * Cookie name.
     1407         *
     1408         * @since 2.8.0
     1409         * @var string
    13501410         */
     1411        var $name;
     1412
     1413        /**
     1414         * Cookie value.
     1415         *
     1416         * @since 2.8.0
     1417         * @var string
     1418         */
     1419        var $value;
     1420
     1421        /**
     1422         * When the cookie expires.
     1423         *
     1424         * @since 2.8.0
     1425         * @var string
     1426         */
     1427        var $expires;
     1428
     1429        /**
     1430         * Cookie URL path.
     1431         *
     1432         * @since 2.8.0
     1433         * @var string
     1434         */
     1435        var $path;
     1436
     1437        /**
     1438         * Cookie Domain.
     1439         *
     1440         * @since 2.8.0
     1441         * @var string
     1442         */
     1443        var $domain;
     1444
     1445        /**
     1446         * PHP4 style Constructor - Calls PHP5 Style Constructor.
     1447         *
     1448         * @access public
     1449         * @since 2.8.0
     1450         * @param string|array $data Raw cookie data.
     1451         */
    13511452        function WP_Http_Cookie( $data ) {
    1352                 return $this->__construct( $data );
     1453                $this->__construct( $data );
    13531454        }
    1354        
     1455
    13551456        /**
    13561457         * Sets up this cookie object.
    13571458         *
     1459         * The parameter $data should be either an associative array containing the indices names below
     1460         * or a header string detailing it.
     1461         *
     1462         * If it's an array, it should include the following elements:
     1463         * <ol>
     1464         * <li>Name</li>
     1465         * <li>Value - should NOT be urlencoded already.</li>
     1466         * <li>Expires - (optional) String or int (UNIX timestamp).</li>
     1467         * <li>Path (optional)</li>
     1468         * <li>Domain (optional)</li>
     1469         * </ol>
     1470         *
    13581471         * @access public
     1472         * @since 2.8.0
    13591473         *
    1360          * @param mixed $data Either an associative array describing the cookie, or a header-string detailing it.
    1361          *              If it's an array, it should include the following elements:
    1362          *                      - name
    1363          *                      - value [should NOT be urlencoded already]
    1364          *                      - expires (optional) String or int (UNIX timestamp)
    1365          *                      - path (optional)
    1366          *                      - domain (optional)
     1474         * @param string|array $data Raw cookie data.
    13671475         */
    13681476        function __construct( $data ) {
    13691477                if ( is_string( $data ) ) {
    13701478                        // Assume it's a header string direct from a previous request
    13711479                        $pairs = explode( ';', $data );
    1372                        
     1480
    13731481                        // Special handling for first pair; name=value. Also be careful of "=" in value
    13741482                        $name  = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
    13751483                        $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
    13761484                        $this->name  = $name;
    13771485                        $this->value = urldecode( $value );
    13781486                        array_shift( $pairs ); //Removes name=value from items.
    1379                        
     1487
    13801488                        // Set everything else as a property
    13811489                        foreach ( $pairs as $pair ) {
    13821490                                if ( empty($pair) ) //Handles the cookie ending in ; which results in a empty final pair
    13831491                                        continue;
     1492
    13841493                                list( $key, $val ) = explode( '=', $pair );
    13851494                                $key = strtolower( trim( $key ) );
    13861495                                if ( 'expires' == $key )
     
    13901499                } else {
    13911500                        if ( !isset( $data['name'] ) )
    13921501                                return false;
    1393                        
     1502
    13941503                        // Set properties based directly on parameters
    13951504                        $this->name   = $data['name'];
    13961505                        $this->value  = isset( $data['value'] ) ? $data['value'] : '';
    13971506                        $this->path   = isset( $data['path'] ) ? $data['path'] : '';
    13981507                        $this->domain = isset( $data['domain'] ) ? $data['domain'] : '';
     1508
    13991509                        if ( isset( $data['expires'] ) )
    14001510                                $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
    14011511                        else
    14021512                                $this->expires = null;
    14031513                }
    14041514        }
    1405        
     1515
    14061516        /**
    14071517         * Confirms that it's OK to send this cookie to the URL checked against.
    14081518         *
    14091519         * Decision is based on RFC 2109/2965, so look there for details on validity.
    14101520         *
    14111521         * @access public
     1522         * @since 2.8.0
    14121523         *
    14131524         * @param string $url URL you intend to send this cookie to
    14141525         * @return boolean TRUE if allowed, FALSE otherwise.
     
    14171528                // Expires - if expired then nothing else matters
    14181529                if ( time() > $this->expires )
    14191530                        return false;
    1420                
     1531
    14211532                // Get details on the URL we're thinking about sending to
    14221533                $url = parse_url( $url );
    14231534                $url['port'] = isset( $url['port'] ) ? $url['port'] : 80;
    14241535                $url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
    1425                
    1426                  // Values to use for comparison against the URL
     1536
     1537                // Values to use for comparison against the URL
    14271538                $path   = isset( $this->path )   ? $this->path   : '/';
    14281539                $port   = isset( $this->port )   ? $this->port   : 80;
    14291540                $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
    14301541                if ( false === stripos( $domain, '.' ) )
    14311542                        $domain .= '.local';
    1432                
     1543
    14331544                // Host - very basic check that the request URL ends with the domain restriction (minus leading dot)
    14341545                $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
    14351546                if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
    14361547                        return false;
    1437                
     1548
    14381549                // Port - supports "port-lists" in the format: "80,8000,8080"
    14391550                if ( !in_array( $url['port'], explode( ',', $port) ) )
    14401551                        return false;
    1441                
     1552
    14421553                // Path - request path must start with path restriction
    14431554                if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
    14441555                        return false;
    1445                
     1556
    14461557                return true;
    14471558        }
    1448        
     1559
     1560        /**
     1561         * Convert cookie name and value back to header string.
     1562         *
     1563         * @access public
     1564         * @since 2.8.0
     1565         *
     1566         * @return string Header encoded cookie name and value.
     1567         */
    14491568        function getHeaderValue() {
    14501569                if ( empty( $this->name ) || empty( $this->value ) )
    14511570                        return '';
    14521571               
    14531572                return $this->name . '=' . urlencode( $this->value );
    14541573        }
    1455        
     1574
     1575        /**
     1576         * Retrieve cookie header for usage in the rest of the WordPress HTTP API.
     1577         *
     1578         * @access public
     1579         * @since 2.8.0
     1580         *
     1581         * @return string
     1582         */
    14561583        function getFullHeader() {
    14571584                return 'Cookie: ' . $this->getHeaderValue();
    14581585        }