WordPress.org

Make WordPress Core

Ticket #9049: http-cookie-support-2.diff

File http-cookie-support-2.diff, 16.9 KB (added by beaulebens, 9 years ago)

Changed WP_Http_Fopen() so that it silently ignores cookies, as per other headers (since it can't send request headers)

  • http.php

     
    348348         *
    349349         * @param string $url URI resource.
    350350         * @param str|array $args Optional. Override the defaults.
    351          * @return boolean
     351         * @return array containing 'headers', 'body', 'response', 'cookies'
    352352         */
    353353        function request( $url, $args = array() ) {
    354354                global $wp_version;
     
    386386                        $r['user-agent'] = $r['headers']['user-agent'];
    387387                        unset($r['headers']['user-agent']);
    388388                }
     389               
     390                // Construct Cookie: header if any cookies are set
     391                WP_Http::buildCookieHeader( $r );
    389392
    390393                if( WP_Http_Encoding::is_available() )
    391394                        $r['headers']['Accept-Encoding'] = WP_Http_Encoding::accept_encoding();
     
    411414                if( has_action('http_api_debug') )
    412415                        do_action('http_api_debug', $transports, 'transports_list');
    413416
    414                 $response = array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     417                $response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    415418                foreach( (array) $transports as $transport ) {
    416419                        $response = $transport->request($url, $r);
    417 
     420                       
    418421                        if( has_action('http_api_debug') )
    419422                                do_action( 'http_api_debug', $response, 'response', get_class($transport) );
    420423
     
    506509         * @since 2.7.0
    507510         *
    508511         * @param string|array $headers
    509          * @return array Processed string headers
     512         * @return array Processed string headers. If duplicate headers are encountered,
     513         *                                      Then a numbered array is returned as the value of that header-key.
    510514         */
    511515        function processHeaders($headers) {
    512516                if ( is_string($headers) )
     
    514518
    515519                $response = array('code' => 0, 'message' => '');
    516520
     521                $cookies = array();
    517522                $newheaders = array();
    518523                foreach ( $headers as $tempheader ) {
    519524                        if ( empty($tempheader) )
     
    528533
    529534                        list($key, $value) = explode(':', $tempheader, 2);
    530535
    531                         if ( ! empty($value) )
    532                                 $newheaders[strtolower($key)] = trim($value);
     536                        if ( !empty( $value ) ) {
     537                                $key = strtolower( $key );
     538                                if ( isset( $newheaders[$key] ) ) {
     539                                        $newheaders[$key] = array( $newheaders[$key], trim( $value ) );
     540                                } else {
     541                                        $newheaders[$key] = trim( $value );
     542                                }
     543                                if ( 'set-cookie' == strtolower( $key ) )
     544                                        $cookies[] = new WP_Http_Cookie( $value );
     545                        }
    533546                }
    534547
    535                 return array('response' => $response, 'headers' => $newheaders);
     548                return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies);
    536549        }
    537 
     550       
    538551        /**
     552         * Takes the arguments for a ::request() and checks for the cookie array.
     553         * If it's found, then it's assumed to contain WP_Http_Cookie objects, which
     554         * are each parsed into strings and added to the Cookie: header (within the
     555         * arguments array). Edits the array by reference.
     556         *
     557         * @access public
     558         * @static
     559         *
     560         * @param array $r Full array of args passed into ::request()
     561         */
     562        function buildCookieHeader( &$r ) {
     563                if ( count( $r['cookies'] ) ) {
     564                        $cookies_header = '';
     565                        foreach ( $r['cookies'] as $cookie ) {
     566                                $cookies_header .= $cookie->getHeaderValue() . '; ';
     567                        }
     568                        $cookies_header = substr( $cookies_header, 0, -2 );
     569                        $r['headers']['cookie'] = $cookies_header;
     570                }
     571        }
     572       
     573        /**
    539574         * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
    540575         *
    541576         * Based off the HTTP http_encoding_dechunk function. Does not support
     
    605640         * @access public
    606641         * @param string $url URI resource.
    607642         * @param str|array $args Optional. Override the defaults.
    608          * @return array 'headers', 'body', and 'response' keys.
     643         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    609644         */
    610645        function request($url, $args = array()) {
    611646                $defaults = array(
     
    625660                        unset($r['headers']['user-agent']);
    626661                }
    627662
     663                // Construct Cookie: header if any cookies are set
     664                WP_Http::buildCookieHeader( $r );
     665
    628666                $iError = null; // Store error number
    629667                $strError = null; // Store error string
    630668
     
    697735
    698736                if ( ! $r['blocking'] ) {
    699737                        fclose($handle);
    700                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     738                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    701739                }
    702740
    703741                $strResponse = '';
     
    732770                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders) )
    733771                        $process['body'] = WP_Http_Encoding::decompress( $process['body'] );
    734772
    735                 return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response']);
     773                return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);
    736774        }
    737775
    738776        /**
     
    780818         *
    781819         * @param string $url URI resource.
    782820         * @param str|array $args Optional. Override the defaults.
    783          * @return array 'headers', 'body', and 'response' keys.
     821         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    784822         */
    785823        function request($url, $args = array()) {
    786824                global $http_response_header;
     
    789827                        'method' => 'GET', 'timeout' => 5,
    790828                        'redirection' => 5, 'httpversion' => '1.0',
    791829                        'blocking' => true,
    792                         'headers' => array(), 'body' => null
     830                        'headers' => array(), 'body' => null, 'cookies' => array()
    793831                );
    794832
    795833                $r = wp_parse_args( $args, $defaults );
     
    816854
    817855                if ( ! $r['blocking'] ) {
    818856                        fclose($handle);
    819                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     857                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    820858                }
    821859
    822860                $strResponse = '';
     
    845883                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) )
    846884                        $strResponse = WP_Http_Encoding::decompress( $strResponse );
    847885
    848                 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
     886                return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
    849887        }
    850888
    851889        /**
     
    884922         *
    885923         * @param string $url
    886924         * @param str|array $args Optional. Override the defaults.
    887          * @return array 'headers', 'body', and 'response' keys.
     925         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    888926         */
    889927        function request($url, $args = array()) {
    890928                $defaults = array(
    891929                        'method' => 'GET', 'timeout' => 5,
    892930                        'redirection' => 5, 'httpversion' => '1.0',
    893931                        'blocking' => true,
    894                         'headers' => array(), 'body' => null
     932                        'headers' => array(), 'body' => null, 'cookies' => array()
    895933                );
    896934
    897935                $r = wp_parse_args( $args, $defaults );
     
    903941                        $r['user-agent'] = $r['headers']['user-agent'];
    904942                        unset($r['headers']['user-agent']);
    905943                }
     944               
     945                // Construct Cookie: header if any cookies are set
     946                WP_Http::buildCookieHeader( $r );
    906947
    907948                $arrURL = parse_url($url);
    908949
     
    951992                if ( ! $r['blocking'] ) {
    952993                        stream_set_blocking($handle, 0);
    953994                        fclose($handle);
    954                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     995                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    955996                }
    956997
    957998                $strResponse = stream_get_contents($handle);
     
    9711012                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) )
    9721013                        $strResponse = WP_Http_Encoding::decompress( $strResponse );
    9731014
    974                 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
     1015                return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
    9751016        }
    9761017
    9771018        /**
     
    10171058         *
    10181059         * @param string $url
    10191060         * @param str|array $args Optional. Override the defaults.
    1020          * @return array 'headers', 'body', and 'response' keys.
     1061         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    10211062         */
    10221063        function request($url, $args = array()) {
    10231064                $defaults = array(
    10241065                        'method' => 'GET', 'timeout' => 5,
    10251066                        'redirection' => 5, 'httpversion' => '1.0',
    10261067                        'blocking' => true,
    1027                         'headers' => array(), 'body' => null
     1068                        'headers' => array(), 'body' => null, 'cookies' => array()
    10281069                );
    10291070
    10301071                $r = wp_parse_args( $args, $defaults );
     
    10361077                        $r['user-agent'] = $r['headers']['user-agent'];
    10371078                        unset($r['headers']['user-agent']);
    10381079                }
     1080               
     1081                // Construct Cookie: header if any cookies are set
     1082                WP_Http::buildCookieHeader( $r );
    10391083
    10401084                switch ( $r['method'] ) {
    10411085                        case 'POST':
     
    10711115                        return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
    10721116
    10731117                if ( ! $r['blocking'] )
    1074                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     1118                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    10751119
    10761120                list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
    10771121                $theHeaders = WP_Http::processHeaders($theHeaders);
     
    10901134                $theResponse['code'] = $info['response_code'];
    10911135                $theResponse['message'] = get_status_header_desc($info['response_code']);
    10921136
    1093                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse);
     1137                return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']);
    10941138        }
    10951139
    10961140        /**
     
    11271171         *
    11281172         * @param string $url
    11291173         * @param str|array $args Optional. Override the defaults.
    1130          * @return array 'headers', 'body', and 'response' keys.
     1174         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    11311175         */
    11321176        function request($url, $args = array()) {
    11331177                $defaults = array(
    11341178                        'method' => 'GET', 'timeout' => 5,
    11351179                        'redirection' => 5, 'httpversion' => '1.0',
    11361180                        'blocking' => true,
    1137                         'headers' => array(), 'body' => null
     1181                        'headers' => array(), 'body' => null, 'cookies' => array()
    11381182                );
    11391183
    11401184                $r = wp_parse_args( $args, $defaults );
     
    11461190                        $r['user-agent'] = $r['headers']['user-agent'];
    11471191                        unset($r['headers']['user-agent']);
    11481192                }
     1193               
     1194                // Construct Cookie: header if any cookies are set
     1195                WP_Http::buildCookieHeader( $r );
    11491196
    11501197                // cURL extension will sometimes fail when the timeout is less than 1 as
    11511198                // it may round down to 0, which gives it unlimited timeout.
     
    11541201
    11551202                $handle = curl_init();
    11561203                curl_setopt( $handle, CURLOPT_URL, $url);
    1157 
     1204               
    11581205                // The cURL extension requires that the option be set for the HEAD to
    11591206                // work properly.
    11601207                if ( 'HEAD' === $r['method'] ) {
     
    11791226                if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
    11801227                        curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
    11811228
    1182                 if( ! is_null($r['headers']) )
    1183                         curl_setopt( $handle, CURLOPT_HTTPHEADER, $r['headers'] );
     1229                if ( !empty( $r['headers'] ) ) {
     1230                        // cURL expects full header strings in each element
     1231                        $headers = array();
     1232                        foreach ( $r['headers'] as $name => $value ) {
     1233                                $headers[] = "{$name}: $value";
     1234                        }
     1235                        curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
     1236                }
    11841237
    11851238                if ( $r['httpversion'] == '1.0' )
    11861239                        curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
     
    11921245                // without some reference.
    11931246                do_action_ref_array( 'http_api_curl', array(&$handle) );
    11941247
    1195                 // We don't need to return the body, so don't. Just execution request
     1248                // We don't need to return the body, so don't. Just execute request
    11961249                // and return.
    11971250                if ( ! $r['blocking'] ) {
    11981251                        curl_exec( $handle );
    11991252                        curl_close( $handle );
    1200                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     1253                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    12011254                }
    12021255
    12031256                $theResponse = curl_exec( $handle );
     
    12171270                        if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
    12181271                                return new WP_Error('http_request_failed', __('Too many redirects.'));
    12191272
    1220                         $theHeaders = array( 'headers' => array() );
     1273                        $theHeaders = array( 'headers' => array(), 'cookies' => array() );
    12211274                        $theBody = '';
    12221275                }
    12231276
     
    12301283                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders) )
    12311284                        $theBody = WP_Http_Encoding::decompress( $theBody );
    12321285
    1233                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response);
     1286                return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']);
    12341287        }
    12351288
    12361289        /**
     
    12491302        }
    12501303}
    12511304
     1305
    12521306/**
     1307 * Internal representation of a cookie.
     1308 *
     1309 * Returned cookies are represented using this class, and when cookies are
     1310 * set, if they are not already a WP_Http_Cookie() object, then they are turned
     1311 * into one.
     1312 *
     1313 * @package WordPress
     1314 * @subpackage HTTP
     1315 */
     1316class WP_Http_Cookie {
     1317        var $name,
     1318                $value,
     1319                $expires,
     1320                $path,
     1321                $domain;
     1322       
     1323        /**
     1324         * PHP4 style Constructor - Calls PHP5 Style Constructor
     1325         */
     1326        function WP_Http_Cookie( $data ) {
     1327                return $this->__construct( $data );
     1328        }
     1329       
     1330        /**
     1331         * Sets up this cookie object.
     1332         *
     1333         * @access public
     1334         *
     1335         * @param mixed $data Either an associative array describing the cookie, or a header-string detailing it.
     1336         *              If it's an array, it should include the following elements:
     1337         *                      - name
     1338         *                      - value [should NOT be urlencoded already]
     1339         *                      - expires (optional) String or int (UNIX timestamp)
     1340         *                      - path (optional)
     1341         *                      - domain (optional)
     1342         */
     1343        function __construct( $data ) {
     1344                if ( is_string( $data ) ) {
     1345                        // Assume it's a header string direct from a previous request
     1346                        $pairs = explode( ';', $data );
     1347                       
     1348                        // Special handling for first pair; name=value. Also be careful of "=" in value
     1349                        $name  = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
     1350                        $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
     1351                        $this->name  = $name;
     1352                        $this->value = urldecode( $value );
     1353                        array_shift( $pairs );
     1354                       
     1355                        // Set everything else as a property
     1356                        foreach ( $pairs as $pair ) {
     1357                                list( $key, $val ) = explode( '=', $pair );
     1358                                $key = strtolower( trim( $key ) );
     1359                                if ( 'expires' == $key )
     1360                                        $val = strtotime( $val );
     1361                                $this->$key = $val;
     1362                        }
     1363                } else {
     1364                        // Set properties based directly on parameters
     1365                        $this->name    = $data['name'];
     1366                        $this->value   = $data['value'];
     1367                        $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
     1368                        $this->path    = $data['path'];
     1369                        $this->domain  = $data['domain'];
     1370                }
     1371        }
     1372       
     1373        /**
     1374         * Confirms that it's OK to send this cookie to the URL checked against.
     1375         *
     1376         * Decision is based on RFC 2109/2965, so look there for details on validity.
     1377         *
     1378         * @access public
     1379         *
     1380         * @param string $url URL you intend to send this cookie to
     1381         * @return boolean TRUE if allowed, FALSE otherwise.
     1382         */
     1383        function test( $url ) {
     1384                // Expires - if expired then nothing else matters
     1385                if ( time() > $this->expires )
     1386                        return false;
     1387               
     1388                // Get details on the URL we're thinking about sending to
     1389                $url = parse_url( $url );
     1390                $url['port'] = isset( $url['port'] ) ? $url['port'] : 80;
     1391                $url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
     1392               
     1393                 // Values to use for comparison against the URL
     1394                $path   = isset( $this->path )   ? $this->path   : '/';
     1395                $port   = isset( $this->port )   ? $this->port   : 80;
     1396                $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
     1397                if ( false === stripos( $domain, '.' ) )
     1398                        $domain .= '.local';
     1399               
     1400                // Host - very basic check that the request URL ends with the domain restriction (minus leading dot)
     1401                $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
     1402                if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
     1403                        return false;
     1404               
     1405                // Port - supports "port-lists" in the format: "80,8000,8080"
     1406                if ( !in_array( $url['port'], explode( ',', $port) ) )
     1407                        return false;
     1408               
     1409                // Path - request path must start with path restriction
     1410                if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
     1411                        return false;
     1412               
     1413                return true;
     1414        }
     1415       
     1416        function getHeaderValue() {
     1417                if ( empty( $this->name ) || empty( $this->value ) )
     1418                        return '';
     1419               
     1420                return $this->name . '=' . urlencode( $this->value );
     1421        }
     1422       
     1423        function getFullHeader() {
     1424                return 'Cookie: ' . $this->getHeaderValue();
     1425        }
     1426}
     1427
     1428/**
    12531429 * Returns the initialized WP_Http Object
    12541430 *
    12551431 * @since 2.7.0
     
    12721448 * The array structure is a little complex.
    12731449 *
    12741450 * <code>
    1275  * $res = array( 'headers' => array(), 'response' => array('code', 'message') );
     1451 * $res = array( 'headers' => array(), 'response' => array('code' => int, 'message' => string) );
    12761452 * </code>
    12771453 *
    12781454 * All of the headers in $res['headers'] are with the name as the key and the