Ticket #9049: http-cookie-support-2.diff
File http-cookie-support-2.diff, 16.9 KB (added by , 16 years ago) |
---|
-
http.php
348 348 * 349 349 * @param string $url URI resource. 350 350 * @param str|array $args Optional. Override the defaults. 351 * @return boolean351 * @return array containing 'headers', 'body', 'response', 'cookies' 352 352 */ 353 353 function request( $url, $args = array() ) { 354 354 global $wp_version; … … 386 386 $r['user-agent'] = $r['headers']['user-agent']; 387 387 unset($r['headers']['user-agent']); 388 388 } 389 390 // Construct Cookie: header if any cookies are set 391 WP_Http::buildCookieHeader( $r ); 389 392 390 393 if( WP_Http_Encoding::is_available() ) 391 394 $r['headers']['Accept-Encoding'] = WP_Http_Encoding::accept_encoding(); … … 411 414 if( has_action('http_api_debug') ) 412 415 do_action('http_api_debug', $transports, 'transports_list'); 413 416 414 $response = array( 'headers' => array(), 'body' => '', 'response' => array('code' , 'message') );417 $response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); 415 418 foreach( (array) $transports as $transport ) { 416 419 $response = $transport->request($url, $r); 417 420 418 421 if( has_action('http_api_debug') ) 419 422 do_action( 'http_api_debug', $response, 'response', get_class($transport) ); 420 423 … … 506 509 * @since 2.7.0 507 510 * 508 511 * @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. 510 514 */ 511 515 function processHeaders($headers) { 512 516 if ( is_string($headers) ) … … 514 518 515 519 $response = array('code' => 0, 'message' => ''); 516 520 521 $cookies = array(); 517 522 $newheaders = array(); 518 523 foreach ( $headers as $tempheader ) { 519 524 if ( empty($tempheader) ) … … 528 533 529 534 list($key, $value) = explode(':', $tempheader, 2); 530 535 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 } 533 546 } 534 547 535 return array('response' => $response, 'headers' => $newheaders );548 return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies); 536 549 } 537 550 538 551 /** 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 /** 539 574 * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification. 540 575 * 541 576 * Based off the HTTP http_encoding_dechunk function. Does not support … … 605 640 * @access public 606 641 * @param string $url URI resource. 607 642 * @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. 609 644 */ 610 645 function request($url, $args = array()) { 611 646 $defaults = array( … … 625 660 unset($r['headers']['user-agent']); 626 661 } 627 662 663 // Construct Cookie: header if any cookies are set 664 WP_Http::buildCookieHeader( $r ); 665 628 666 $iError = null; // Store error number 629 667 $strError = null; // Store error string 630 668 … … 697 735 698 736 if ( ! $r['blocking'] ) { 699 737 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() ); 701 739 } 702 740 703 741 $strResponse = ''; … … 732 770 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders) ) 733 771 $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); 734 772 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']); 736 774 } 737 775 738 776 /** … … 780 818 * 781 819 * @param string $url URI resource. 782 820 * @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. 784 822 */ 785 823 function request($url, $args = array()) { 786 824 global $http_response_header; … … 789 827 'method' => 'GET', 'timeout' => 5, 790 828 'redirection' => 5, 'httpversion' => '1.0', 791 829 'blocking' => true, 792 'headers' => array(), 'body' => null 830 'headers' => array(), 'body' => null, 'cookies' => array() 793 831 ); 794 832 795 833 $r = wp_parse_args( $args, $defaults ); … … 816 854 817 855 if ( ! $r['blocking'] ) { 818 856 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() ); 820 858 } 821 859 822 860 $strResponse = ''; … … 845 883 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) ) 846 884 $strResponse = WP_Http_Encoding::decompress( $strResponse ); 847 885 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']); 849 887 } 850 888 851 889 /** … … 884 922 * 885 923 * @param string $url 886 924 * @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. 888 926 */ 889 927 function request($url, $args = array()) { 890 928 $defaults = array( 891 929 'method' => 'GET', 'timeout' => 5, 892 930 'redirection' => 5, 'httpversion' => '1.0', 893 931 'blocking' => true, 894 'headers' => array(), 'body' => null 932 'headers' => array(), 'body' => null, 'cookies' => array() 895 933 ); 896 934 897 935 $r = wp_parse_args( $args, $defaults ); … … 903 941 $r['user-agent'] = $r['headers']['user-agent']; 904 942 unset($r['headers']['user-agent']); 905 943 } 944 945 // Construct Cookie: header if any cookies are set 946 WP_Http::buildCookieHeader( $r ); 906 947 907 948 $arrURL = parse_url($url); 908 949 … … 951 992 if ( ! $r['blocking'] ) { 952 993 stream_set_blocking($handle, 0); 953 994 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() ); 955 996 } 956 997 957 998 $strResponse = stream_get_contents($handle); … … 971 1012 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) ) 972 1013 $strResponse = WP_Http_Encoding::decompress( $strResponse ); 973 1014 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']); 975 1016 } 976 1017 977 1018 /** … … 1017 1058 * 1018 1059 * @param string $url 1019 1060 * @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. 1021 1062 */ 1022 1063 function request($url, $args = array()) { 1023 1064 $defaults = array( 1024 1065 'method' => 'GET', 'timeout' => 5, 1025 1066 'redirection' => 5, 'httpversion' => '1.0', 1026 1067 'blocking' => true, 1027 'headers' => array(), 'body' => null 1068 'headers' => array(), 'body' => null, 'cookies' => array() 1028 1069 ); 1029 1070 1030 1071 $r = wp_parse_args( $args, $defaults ); … … 1036 1077 $r['user-agent'] = $r['headers']['user-agent']; 1037 1078 unset($r['headers']['user-agent']); 1038 1079 } 1080 1081 // Construct Cookie: header if any cookies are set 1082 WP_Http::buildCookieHeader( $r ); 1039 1083 1040 1084 switch ( $r['method'] ) { 1041 1085 case 'POST': … … 1071 1115 return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']); 1072 1116 1073 1117 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() ); 1075 1119 1076 1120 list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2); 1077 1121 $theHeaders = WP_Http::processHeaders($theHeaders); … … 1090 1134 $theResponse['code'] = $info['response_code']; 1091 1135 $theResponse['message'] = get_status_header_desc($info['response_code']); 1092 1136 1093 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse );1137 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']); 1094 1138 } 1095 1139 1096 1140 /** … … 1127 1171 * 1128 1172 * @param string $url 1129 1173 * @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. 1131 1175 */ 1132 1176 function request($url, $args = array()) { 1133 1177 $defaults = array( 1134 1178 'method' => 'GET', 'timeout' => 5, 1135 1179 'redirection' => 5, 'httpversion' => '1.0', 1136 1180 'blocking' => true, 1137 'headers' => array(), 'body' => null 1181 'headers' => array(), 'body' => null, 'cookies' => array() 1138 1182 ); 1139 1183 1140 1184 $r = wp_parse_args( $args, $defaults ); … … 1146 1190 $r['user-agent'] = $r['headers']['user-agent']; 1147 1191 unset($r['headers']['user-agent']); 1148 1192 } 1193 1194 // Construct Cookie: header if any cookies are set 1195 WP_Http::buildCookieHeader( $r ); 1149 1196 1150 1197 // cURL extension will sometimes fail when the timeout is less than 1 as 1151 1198 // it may round down to 0, which gives it unlimited timeout. … … 1154 1201 1155 1202 $handle = curl_init(); 1156 1203 curl_setopt( $handle, CURLOPT_URL, $url); 1157 1204 1158 1205 // The cURL extension requires that the option be set for the HEAD to 1159 1206 // work properly. 1160 1207 if ( 'HEAD' === $r['method'] ) { … … 1179 1226 if ( !ini_get('safe_mode') && !ini_get('open_basedir') ) 1180 1227 curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true ); 1181 1228 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 } 1184 1237 1185 1238 if ( $r['httpversion'] == '1.0' ) 1186 1239 curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); … … 1192 1245 // without some reference. 1193 1246 do_action_ref_array( 'http_api_curl', array(&$handle) ); 1194 1247 1195 // We don't need to return the body, so don't. Just execut ionrequest1248 // We don't need to return the body, so don't. Just execute request 1196 1249 // and return. 1197 1250 if ( ! $r['blocking'] ) { 1198 1251 curl_exec( $handle ); 1199 1252 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() ); 1201 1254 } 1202 1255 1203 1256 $theResponse = curl_exec( $handle ); … … 1217 1270 if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) ) 1218 1271 return new WP_Error('http_request_failed', __('Too many redirects.')); 1219 1272 1220 $theHeaders = array( 'headers' => array() );1273 $theHeaders = array( 'headers' => array(), 'cookies' => array() ); 1221 1274 $theBody = ''; 1222 1275 } 1223 1276 … … 1230 1283 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders) ) 1231 1284 $theBody = WP_Http_Encoding::decompress( $theBody ); 1232 1285 1233 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response );1286 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']); 1234 1287 } 1235 1288 1236 1289 /** … … 1249 1302 } 1250 1303 } 1251 1304 1305 1252 1306 /** 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 */ 1316 class 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 /** 1253 1429 * Returns the initialized WP_Http Object 1254 1430 * 1255 1431 * @since 2.7.0 … … 1272 1448 * The array structure is a little complex. 1273 1449 * 1274 1450 * <code> 1275 * $res = array( 'headers' => array(), 'response' => array('code' , 'message') );1451 * $res = array( 'headers' => array(), 'response' => array('code' => int, 'message' => string) ); 1276 1452 * </code> 1277 1453 * 1278 1454 * All of the headers in $res['headers'] are with the name as the key and the