WordPress.org

Make WordPress Core

Ticket #9049: http-cookie-support.diff

File http-cookie-support.diff, 17.2 KB (added by beaulebens, 5 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