Make WordPress Core

Ticket #9049: http-cookie-support.diff

File http-cookie-support.diff, 17.2 KB (added by beaulebens, 15 years ago)
  • 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 );
    796834
     835                // This transport doesn't support cookies, throw an error if they are set
     836                if ( count( $r['cookies'] ) )
     837                        return new WP_Error( 'http_request_failed', __( 'WP_Http_Fopen() does not support sending cookies' ) );
     838
    797839                $arrURL = parse_url($url);
    798840
    799841                if ( false === $arrURL )
     
    816858
    817859                if ( ! $r['blocking'] ) {
    818860                        fclose($handle);
    819                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     861                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    820862                }
    821863
    822864                $strResponse = '';
     
    845887                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) )
    846888                        $strResponse = WP_Http_Encoding::decompress( $strResponse );
    847889
    848                 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
     890                return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
    849891        }
    850892
    851893        /**
     
    884926         *
    885927         * @param string $url
    886928         * @param str|array $args Optional. Override the defaults.
    887          * @return array 'headers', 'body', and 'response' keys.
     929         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    888930         */
    889931        function request($url, $args = array()) {
    890932                $defaults = array(
    891933                        'method' => 'GET', 'timeout' => 5,
    892934                        'redirection' => 5, 'httpversion' => '1.0',
    893935                        'blocking' => true,
    894                         'headers' => array(), 'body' => null
     936                        'headers' => array(), 'body' => null, 'cookies' => array()
    895937                );
    896938
    897939                $r = wp_parse_args( $args, $defaults );
     
    903945                        $r['user-agent'] = $r['headers']['user-agent'];
    904946                        unset($r['headers']['user-agent']);
    905947                }
     948               
     949                // Construct Cookie: header if any cookies are set
     950                WP_Http::buildCookieHeader( $r );
    906951
    907952                $arrURL = parse_url($url);
    908953
     
    951996                if ( ! $r['blocking'] ) {
    952997                        stream_set_blocking($handle, 0);
    953998                        fclose($handle);
    954                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     999                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    9551000                }
    9561001
    9571002                $strResponse = stream_get_contents($handle);
     
    9711016                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) )
    9721017                        $strResponse = WP_Http_Encoding::decompress( $strResponse );
    9731018
    974                 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
     1019                return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
    9751020        }
    9761021
    9771022        /**
     
    10171062         *
    10181063         * @param string $url
    10191064         * @param str|array $args Optional. Override the defaults.
    1020          * @return array 'headers', 'body', and 'response' keys.
     1065         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    10211066         */
    10221067        function request($url, $args = array()) {
    10231068                $defaults = array(
    10241069                        'method' => 'GET', 'timeout' => 5,
    10251070                        'redirection' => 5, 'httpversion' => '1.0',
    10261071                        'blocking' => true,
    1027                         'headers' => array(), 'body' => null
     1072                        'headers' => array(), 'body' => null, 'cookies' => array()
    10281073                );
    10291074
    10301075                $r = wp_parse_args( $args, $defaults );
     
    10361081                        $r['user-agent'] = $r['headers']['user-agent'];
    10371082                        unset($r['headers']['user-agent']);
    10381083                }
     1084               
     1085                // Construct Cookie: header if any cookies are set
     1086                WP_Http::buildCookieHeader( $r );
    10391087
    10401088                switch ( $r['method'] ) {
    10411089                        case 'POST':
     
    10711119                        return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
    10721120
    10731121                if ( ! $r['blocking'] )
    1074                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     1122                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    10751123
    10761124                list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
    10771125                $theHeaders = WP_Http::processHeaders($theHeaders);
     
    10901138                $theResponse['code'] = $info['response_code'];
    10911139                $theResponse['message'] = get_status_header_desc($info['response_code']);
    10921140
    1093                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse);
     1141                return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']);
    10941142        }
    10951143
    10961144        /**
     
    11271175         *
    11281176         * @param string $url
    11291177         * @param str|array $args Optional. Override the defaults.
    1130          * @return array 'headers', 'body', and 'response' keys.
     1178         * @return array 'headers', 'body', 'cookies' and 'response' keys.
    11311179         */
    11321180        function request($url, $args = array()) {
    11331181                $defaults = array(
    11341182                        'method' => 'GET', 'timeout' => 5,
    11351183                        'redirection' => 5, 'httpversion' => '1.0',
    11361184                        'blocking' => true,
    1137                         'headers' => array(), 'body' => null
     1185                        'headers' => array(), 'body' => null, 'cookies' => array()
    11381186                );
    11391187
    11401188                $r = wp_parse_args( $args, $defaults );
     
    11461194                        $r['user-agent'] = $r['headers']['user-agent'];
    11471195                        unset($r['headers']['user-agent']);
    11481196                }
     1197               
     1198                // Construct Cookie: header if any cookies are set
     1199                WP_Http::buildCookieHeader( $r );
    11491200
    11501201                // cURL extension will sometimes fail when the timeout is less than 1 as
    11511202                // it may round down to 0, which gives it unlimited timeout.
     
    11541205
    11551206                $handle = curl_init();
    11561207                curl_setopt( $handle, CURLOPT_URL, $url);
    1157 
     1208               
    11581209                // The cURL extension requires that the option be set for the HEAD to
    11591210                // work properly.
    11601211                if ( 'HEAD' === $r['method'] ) {
     
    11791230                if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
    11801231                        curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
    11811232
    1182                 if( ! is_null($r['headers']) )
    1183                         curl_setopt( $handle, CURLOPT_HTTPHEADER, $r['headers'] );
     1233                if ( !empty( $r['headers'] ) ) {
     1234                        // cURL expects full header strings in each element
     1235                        $headers = array();
     1236                        foreach ( $r['headers'] as $name => $value ) {
     1237                                $headers[] = "{$name}: $value";
     1238                        }
     1239                        curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
     1240                }
    11841241
    11851242                if ( $r['httpversion'] == '1.0' )
    11861243                        curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
     
    11921249                // without some reference.
    11931250                do_action_ref_array( 'http_api_curl', array(&$handle) );
    11941251
    1195                 // We don't need to return the body, so don't. Just execution request
     1252                // We don't need to return the body, so don't. Just execute request
    11961253                // and return.
    11971254                if ( ! $r['blocking'] ) {
    11981255                        curl_exec( $handle );
    11991256                        curl_close( $handle );
    1200                         return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     1257                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
    12011258                }
    12021259
    12031260                $theResponse = curl_exec( $handle );
     
    12171274                        if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
    12181275                                return new WP_Error('http_request_failed', __('Too many redirects.'));
    12191276
    1220                         $theHeaders = array( 'headers' => array() );
     1277                        $theHeaders = array( 'headers' => array(), 'cookies' => array() );
    12211278                        $theBody = '';
    12221279                }
    12231280
     
    12301287                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders) )
    12311288                        $theBody = WP_Http_Encoding::decompress( $theBody );
    12321289
    1233                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response);
     1290                return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']);
    12341291        }
    12351292
    12361293        /**
     
    12491306        }
    12501307}
    12511308
     1309
    12521310/**
     1311 * Internal representation of a cookie.
     1312 *
     1313 * Returned cookies are represented using this class, and when cookies are
     1314 * set, if they are not already a WP_Http_Cookie() object, then they are turned
     1315 * into one.
     1316 *
     1317 * @package WordPress
     1318 * @subpackage HTTP
     1319 */
     1320class WP_Http_Cookie {
     1321        var $name,
     1322                $value,
     1323                $expires,
     1324                $path,
     1325                $domain;
     1326       
     1327        /**
     1328         * PHP4 style Constructor - Calls PHP5 Style Constructor
     1329         */
     1330        function WP_Http_Cookie( $data ) {
     1331                return $this->__construct( $data );
     1332        }
     1333       
     1334        /**
     1335         * Sets up this cookie object.
     1336         *
     1337         * @access public
     1338         *
     1339         * @param mixed $data Either an associative array describing the cookie, or a header-string detailing it.
     1340         *              If it's an array, it should include the following elements:
     1341         *                      - name
     1342         *                      - value [should NOT be urlencoded already]
     1343         *                      - expires (optional) String or int (UNIX timestamp)
     1344         *                      - path (optional)
     1345         *                      - domain (optional)
     1346         */
     1347        function __construct( $data ) {
     1348                if ( is_string( $data ) ) {
     1349                        // Assume it's a header string direct from a previous request
     1350                        $pairs = explode( ';', $data );
     1351                       
     1352                        // Special handling for first pair; name=value. Also be careful of "=" in value
     1353                        $name  = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
     1354                        $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
     1355                        $this->name  = $name;
     1356                        $this->value = urldecode( $value );
     1357                        array_shift( $pairs );
     1358                       
     1359                        // Set everything else as a property
     1360                        foreach ( $pairs as $pair ) {
     1361                                list( $key, $val ) = explode( '=', $pair );
     1362                                $key = strtolower( trim( $key ) );
     1363                                if ( 'expires' == $key )
     1364                                        $val = strtotime( $val );
     1365                                $this->$key = $val;
     1366                        }
     1367                } else {
     1368                        // Set properties based directly on parameters
     1369                        $this->name    = $data['name'];
     1370                        $this->value   = $data['value'];
     1371                        $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
     1372                        $this->path    = $data['path'];
     1373                        $this->domain  = $data['domain'];
     1374                }
     1375        }
     1376       
     1377        /**
     1378         * Confirms that it's OK to send this cookie to the URL checked against.
     1379         *
     1380         * Decision is based on RFC 2109/2965, so look there for details on validity.
     1381         *
     1382         * @access public
     1383         *
     1384         * @param string $url URL you intend to send this cookie to
     1385         * @return boolean TRUE if allowed, FALSE otherwise.
     1386         */
     1387        function test( $url ) {
     1388                // Expires - if expired then nothing else matters
     1389                if ( time() > $this->expires )
     1390                        return false;
     1391               
     1392                // Get details on the URL we're thinking about sending to
     1393                $url = parse_url( $url );
     1394                $url['port'] = isset( $url['port'] ) ? $url['port'] : 80;
     1395                $url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
     1396               
     1397                 // Values to use for comparison against the URL
     1398                $path   = isset( $this->path )   ? $this->path   : '/';
     1399                $port   = isset( $this->port )   ? $this->port   : 80;
     1400                $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
     1401                if ( false === stripos( $domain, '.' ) )
     1402                        $domain .= '.local';
     1403               
     1404                // Host - very basic check that the request URL ends with the domain restriction (minus leading dot)
     1405                $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
     1406                if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
     1407                        return false;
     1408               
     1409                // Port - supports "port-lists" in the format: "80,8000,8080"
     1410                if ( !in_array( $url['port'], explode( ',', $port) ) )
     1411                        return false;
     1412               
     1413                // Path - request path must start with path restriction
     1414                if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
     1415                        return false;
     1416               
     1417                return true;
     1418        }
     1419       
     1420        function getHeaderValue() {
     1421                if ( empty( $this->name ) || empty( $this->value ) )
     1422                        return '';
     1423               
     1424                return $this->name . '=' . urlencode( $this->value );
     1425        }
     1426       
     1427        function getFullHeader() {
     1428                return 'Cookie: ' . $this->getHeaderValue();
     1429        }
     1430}
     1431
     1432/**
    12531433 * Returns the initialized WP_Http Object
    12541434 *
    12551435 * @since 2.7.0
     
    12721452 * The array structure is a little complex.
    12731453 *
    12741454 * <code>
    1275  * $res = array( 'headers' => array(), 'response' => array('code', 'message') );
     1455 * $res = array( 'headers' => array(), 'response' => array('code' => int, 'message' => string) );
    12761456 * </code>
    12771457 *
    12781458 * All of the headers in $res['headers'] are with the name as the key and the