Cookie support for HTTP API. Props beaulebens. fixes #9049 #9037 #8727

git-svn-id: http://svn.automattic.com/wordpress/trunk@10512 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
ryan 2009-02-05 21:59:37 +00:00
parent 4118b08835
commit e8dc44ad07
4 changed files with 289 additions and 47 deletions

View File

@ -624,6 +624,80 @@ function delete_option( $name ) {
return true;
}
/**
* Delete a transient
*
* @since 2.8.0
* @package WordPress
* @subpackage Transient
*
* @param string $transient Transient name. Expected to not be SQL-escaped
* @return bool true if successful, false otherwise
*/
function delete_transient($transient) {
global $_wp_using_ext_object_cache, $wpdb;
if ( $_wp_using_ext_object_cache ) {
return wp_cache_delete($transient, 'transient');
} else {
$transient = '_transient_' . $wpdb->escape($transient);
return delete_option($transient);
}
}
/**
* Get the value of a transient
*
* If the transient does not exist or does not have a value, then the return value
* will be false.
*
* @since 2.8.0
* @package WordPress
* @subpackage Transient
*
* @param string $transient Transient name. Expected to not be SQL-escaped
* @return mixed Value of transient
*/
function get_transient($transient) {
global $_wp_using_ext_object_cache, $wpdb;
if ( $_wp_using_ext_object_cache ) {
return wp_cache_get($transient, 'transient');
} else {
$transient = '_transient_' . $wpdb->escape($transient);
return get_option($transient);
}
}
/**
* Set/update the value of a transient
*
* You do not need to serialize values, if the value needs to be serialize, then
* it will be serialized before it is set.
*
* @since 2.8.0
* @package WordPress
* @subpackage Transient
*
* @param string $transient Transient name. Expected to not be SQL-escaped
* @param mixed $value Transient value.
* @return bool False if value was not set and true if value was set.
*/
function set_transient($transient, $value) {
global $_wp_using_ext_object_cache, $wpdb;
if ( $_wp_using_ext_object_cache ) {
return wp_cache_set($transient, $value, 'transient');
} else {
$transient = '_transient_' . $transient;
$safe_transient = $wpdb->escape($transient);
if ( false === get_option( $safe_transient ) )
return add_option($transient, $value, '', 'no');
else
return update_option($transient, $value);
}
}
/**
* Saves and restores user interface settings stored in a cookie.
*

View File

@ -351,7 +351,7 @@ class WP_Http {
*
* @param string $url URI resource.
* @param str|array $args Optional. Override the defaults.
* @return boolean
* @return array containing 'headers', 'body', 'response', 'cookies'
*/
function request( $url, $args = array() ) {
global $wp_version;
@ -399,6 +399,9 @@ class WP_Http {
$r['user-agent'] = $r['headers']['user-agent'];
unset($r['headers']['user-agent']);
}
// Construct Cookie: header if any cookies are set
WP_Http::buildCookieHeader( $r );
if( WP_Http_Encoding::is_available() )
$r['headers']['Accept-Encoding'] = WP_Http_Encoding::accept_encoding();
@ -424,10 +427,10 @@ class WP_Http {
if( has_action('http_api_debug') )
do_action('http_api_debug', $transports, 'transports_list');
$response = array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
$response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
foreach( (array) $transports as $transport ) {
$response = $transport->request($url, $r);
if( has_action('http_api_debug') )
do_action( 'http_api_debug', $response, 'response', get_class($transport) );
@ -519,7 +522,8 @@ class WP_Http {
* @since 2.7.0
*
* @param string|array $headers
* @return array Processed string headers
* @return array Processed string headers. If duplicate headers are encountered,
* Then a numbered array is returned as the value of that header-key.
*/
function processHeaders($headers) {
if ( is_string($headers) )
@ -527,6 +531,7 @@ class WP_Http {
$response = array('code' => 0, 'message' => '');
$cookies = array();
$newheaders = array();
foreach ( $headers as $tempheader ) {
if ( empty($tempheader) )
@ -541,13 +546,43 @@ class WP_Http {
list($key, $value) = explode(':', $tempheader, 2);
if ( ! empty($value) )
$newheaders[strtolower($key)] = trim($value);
if ( !empty( $value ) ) {
$key = strtolower( $key );
if ( isset( $newheaders[$key] ) ) {
$newheaders[$key] = array( $newheaders[$key], trim( $value ) );
} else {
$newheaders[$key] = trim( $value );
}
if ( 'set-cookie' == strtolower( $key ) )
$cookies[] = new WP_Http_Cookie( $value );
}
}
return array('response' => $response, 'headers' => $newheaders);
return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies);
}
/**
* Takes the arguments for a ::request() and checks for the cookie array.
* If it's found, then it's assumed to contain WP_Http_Cookie objects, which
* are each parsed into strings and added to the Cookie: header (within the
* arguments array). Edits the array by reference.
*
* @access public
* @static
*
* @param array $r Full array of args passed into ::request()
*/
function buildCookieHeader( &$r ) {
if ( count( $r['cookies'] ) ) {
$cookies_header = '';
foreach ( $r['cookies'] as $cookie ) {
$cookies_header .= $cookie->getHeaderValue() . '; ';
}
$cookies_header = substr( $cookies_header, 0, -2 );
$r['headers']['cookie'] = $cookies_header;
}
}
/**
* Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
*
@ -618,7 +653,7 @@ class WP_Http_Fsockopen {
* @access public
* @param string $url URI resource.
* @param str|array $args Optional. Override the defaults.
* @return array 'headers', 'body', and 'response' keys.
* @return array 'headers', 'body', 'cookies' and 'response' keys.
*/
function request($url, $args = array()) {
$defaults = array(
@ -638,6 +673,9 @@ class WP_Http_Fsockopen {
unset($r['headers']['user-agent']);
}
// Construct Cookie: header if any cookies are set
WP_Http::buildCookieHeader( $r );
$iError = null; // Store error number
$strError = null; // Store error string
@ -710,7 +748,7 @@ class WP_Http_Fsockopen {
if ( ! $r['blocking'] ) {
fclose($handle);
return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
}
$strResponse = '';
@ -745,7 +783,7 @@ class WP_Http_Fsockopen {
if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders) )
$process['body'] = WP_Http_Encoding::decompress( $process['body'] );
return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response']);
return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);
}
/**
@ -793,7 +831,7 @@ class WP_Http_Fopen {
*
* @param string $url URI resource.
* @param str|array $args Optional. Override the defaults.
* @return array 'headers', 'body', and 'response' keys.
* @return array 'headers', 'body', 'cookies' and 'response' keys.
*/
function request($url, $args = array()) {
global $http_response_header;
@ -802,7 +840,7 @@ class WP_Http_Fopen {
'method' => 'GET', 'timeout' => 5,
'redirection' => 5, 'httpversion' => '1.0',
'blocking' => true,
'headers' => array(), 'body' => null
'headers' => array(), 'body' => null, 'cookies' => array()
);
$r = wp_parse_args( $args, $defaults );
@ -829,7 +867,7 @@ class WP_Http_Fopen {
if ( ! $r['blocking'] ) {
fclose($handle);
return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
}
$strResponse = '';
@ -858,7 +896,7 @@ class WP_Http_Fopen {
if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) )
$strResponse = WP_Http_Encoding::decompress( $strResponse );
return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
}
/**
@ -897,14 +935,14 @@ class WP_Http_Streams {
*
* @param string $url
* @param str|array $args Optional. Override the defaults.
* @return array 'headers', 'body', and 'response' keys.
* @return array 'headers', 'body', 'cookies' and 'response' keys.
*/
function request($url, $args = array()) {
$defaults = array(
'method' => 'GET', 'timeout' => 5,
'redirection' => 5, 'httpversion' => '1.0',
'blocking' => true,
'headers' => array(), 'body' => null
'headers' => array(), 'body' => null, 'cookies' => array()
);
$r = wp_parse_args( $args, $defaults );
@ -916,6 +954,9 @@ class WP_Http_Streams {
$r['user-agent'] = $r['headers']['user-agent'];
unset($r['headers']['user-agent']);
}
// Construct Cookie: header if any cookies are set
WP_Http::buildCookieHeader( $r );
$arrURL = parse_url($url);
@ -968,7 +1009,7 @@ class WP_Http_Streams {
if ( ! $r['blocking'] ) {
stream_set_blocking($handle, 0);
fclose($handle);
return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
}
$strResponse = stream_get_contents($handle);
@ -988,7 +1029,7 @@ class WP_Http_Streams {
if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders) )
$strResponse = WP_Http_Encoding::decompress( $strResponse );
return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
}
/**
@ -1034,14 +1075,14 @@ class WP_Http_ExtHTTP {
*
* @param string $url
* @param str|array $args Optional. Override the defaults.
* @return array 'headers', 'body', and 'response' keys.
* @return array 'headers', 'body', 'cookies' and 'response' keys.
*/
function request($url, $args = array()) {
$defaults = array(
'method' => 'GET', 'timeout' => 5,
'redirection' => 5, 'httpversion' => '1.0',
'blocking' => true,
'headers' => array(), 'body' => null
'headers' => array(), 'body' => null, 'cookies' => array()
);
$r = wp_parse_args( $args, $defaults );
@ -1053,6 +1094,9 @@ class WP_Http_ExtHTTP {
$r['user-agent'] = $r['headers']['user-agent'];
unset($r['headers']['user-agent']);
}
// Construct Cookie: header if any cookies are set
WP_Http::buildCookieHeader( $r );
switch ( $r['method'] ) {
case 'POST':
@ -1092,7 +1136,7 @@ class WP_Http_ExtHTTP {
return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
if ( ! $r['blocking'] )
return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
$theHeaders = WP_Http::processHeaders($theHeaders);
@ -1111,7 +1155,7 @@ class WP_Http_ExtHTTP {
$theResponse['code'] = $info['response_code'];
$theResponse['message'] = get_status_header_desc($info['response_code']);
return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse);
return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']);
}
/**
@ -1148,14 +1192,14 @@ class WP_Http_Curl {
*
* @param string $url
* @param str|array $args Optional. Override the defaults.
* @return array 'headers', 'body', and 'response' keys.
* @return array 'headers', 'body', 'cookies' and 'response' keys.
*/
function request($url, $args = array()) {
$defaults = array(
'method' => 'GET', 'timeout' => 5,
'redirection' => 5, 'httpversion' => '1.0',
'blocking' => true,
'headers' => array(), 'body' => null
'headers' => array(), 'body' => null, 'cookies' => array()
);
$r = wp_parse_args( $args, $defaults );
@ -1167,6 +1211,9 @@ class WP_Http_Curl {
$r['user-agent'] = $r['headers']['user-agent'];
unset($r['headers']['user-agent']);
}
// Construct Cookie: header if any cookies are set
WP_Http::buildCookieHeader( $r );
// cURL extension will sometimes fail when the timeout is less than 1 as
// it may round down to 0, which gives it unlimited timeout.
@ -1203,8 +1250,14 @@ class WP_Http_Curl {
if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
if( ! is_null($r['headers']) )
curl_setopt( $handle, CURLOPT_HTTPHEADER, $r['headers'] );
if ( !empty( $r['headers'] ) ) {
// cURL expects full header strings in each element
$headers = array();
foreach ( $r['headers'] as $name => $value ) {
$headers[] = "{$name}: $value";
}
curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
}
if ( $r['httpversion'] == '1.0' )
curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
@ -1216,12 +1269,12 @@ class WP_Http_Curl {
// without some reference.
do_action_ref_array( 'http_api_curl', array(&$handle) );
// We don't need to return the body, so don't. Just execution request
// We don't need to return the body, so don't. Just execute request
// and return.
if ( ! $r['blocking'] ) {
curl_exec( $handle );
curl_close( $handle );
return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
}
$theResponse = curl_exec( $handle );
@ -1241,7 +1294,7 @@ class WP_Http_Curl {
if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
return new WP_Error('http_request_failed', __('Too many redirects.'));
$theHeaders = array( 'headers' => array() );
$theHeaders = array( 'headers' => array(), 'cookies' => array() );
$theBody = '';
}
@ -1254,7 +1307,7 @@ class WP_Http_Curl {
if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders) )
$theBody = WP_Http_Encoding::decompress( $theBody );
return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response);
return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']);
}
/**
@ -1273,6 +1326,129 @@ class WP_Http_Curl {
}
}
/**
* Internal representation of a cookie.
*
* Returned cookies are represented using this class, and when cookies are
* set, if they are not already a WP_Http_Cookie() object, then they are turned
* into one.
*
* @package WordPress
* @subpackage HTTP
*/
class WP_Http_Cookie {
var $name,
$value,
$expires,
$path,
$domain;
/**
* PHP4 style Constructor - Calls PHP5 Style Constructor
*/
function WP_Http_Cookie( $data ) {
return $this->__construct( $data );
}
/**
* Sets up this cookie object.
*
* @access public
*
* @param mixed $data Either an associative array describing the cookie, or a header-string detailing it.
* If it's an array, it should include the following elements:
* - name
* - value [should NOT be urlencoded already]
* - expires (optional) String or int (UNIX timestamp)
* - path (optional)
* - domain (optional)
*/
function __construct( $data ) {
if ( is_string( $data ) ) {
// Assume it's a header string direct from a previous request
$pairs = explode( ';', $data );
// Special handling for first pair; name=value. Also be careful of "=" in value
$name = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
$value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
$this->name = $name;
$this->value = urldecode( $value );
array_shift( $pairs );
// Set everything else as a property
foreach ( $pairs as $pair ) {
list( $key, $val ) = explode( '=', $pair );
$key = strtolower( trim( $key ) );
if ( 'expires' == $key )
$val = strtotime( $val );
$this->$key = $val;
}
} else {
// Set properties based directly on parameters
$this->name = $data['name'];
$this->value = $data['value'];
$this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
$this->path = $data['path'];
$this->domain = $data['domain'];
}
}
/**
* Confirms that it's OK to send this cookie to the URL checked against.
*
* Decision is based on RFC 2109/2965, so look there for details on validity.
*
* @access public
*
* @param string $url URL you intend to send this cookie to
* @return boolean TRUE if allowed, FALSE otherwise.
*/
function test( $url ) {
// Expires - if expired then nothing else matters
if ( time() > $this->expires )
return false;
// Get details on the URL we're thinking about sending to
$url = parse_url( $url );
$url['port'] = isset( $url['port'] ) ? $url['port'] : 80;
$url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
// Values to use for comparison against the URL
$path = isset( $this->path ) ? $this->path : '/';
$port = isset( $this->port ) ? $this->port : 80;
$domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
if ( false === stripos( $domain, '.' ) )
$domain .= '.local';
// Host - very basic check that the request URL ends with the domain restriction (minus leading dot)
$domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
return false;
// Port - supports "port-lists" in the format: "80,8000,8080"
if ( !in_array( $url['port'], explode( ',', $port) ) )
return false;
// Path - request path must start with path restriction
if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
return false;
return true;
}
function getHeaderValue() {
if ( empty( $this->name ) || empty( $this->value ) )
return '';
return $this->name . '=' . urlencode( $this->value );
}
function getFullHeader() {
return 'Cookie: ' . $this->getHeaderValue();
}
}
/**
* Returns the initialized WP_Http Object
*
@ -1296,7 +1472,7 @@ function &_wp_http_get_object() {
* The array structure is a little complex.
*
* <code>
* $res = array( 'headers' => array(), 'response' => array('code', 'message') );
* $res = array( 'headers' => array(), 'response' => array('code' => int, 'message' => string) );
* </code>
*
* All of the headers in $res['headers'] are with the name as the key and the

View File

@ -1596,11 +1596,11 @@ class WP_Rewrite {
* @return array Rewrite rules.
*/
function wp_rewrite_rules() {
$this->rules = get_option('rewrite_rules');
$this->rules = get_transient('rewrite_rules');
if ( empty($this->rules) ) {
$this->matches = 'matches';
$this->rewrite_rules();
update_option('rewrite_rules', $this->rules);
set_transient('rewrite_rules', $this->rules);
}
return $this->rules;
@ -1783,7 +1783,7 @@ class WP_Rewrite {
* @access public
*/
function flush_rules() {
delete_option('rewrite_rules');
delete_transient('rewrite_rules');
$this->wp_rewrite_rules();
if ( function_exists('save_mod_rewrite_rules') )
save_mod_rewrite_rules();

View File

@ -714,14 +714,8 @@ class RSSCache {
$cache_option = 'rss_' . $this->file_name( $url );
$cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
// shouldn't these be using get_option() ?
if ( !$wpdb->get_var( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name = %s", $cache_option ) ) )
add_option($cache_option, '', '', 'no');
if ( !$wpdb->get_var( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name = %s", $cache_timestamp ) ) )
add_option($cache_timestamp, '', '', 'no');
update_option($cache_option, $rss);
update_option($cache_timestamp, time() );
set_transient($cache_option, $rss);
set_transient($cache_timestamp, time() );
return $cache_option;
}
@ -736,15 +730,13 @@ class RSSCache {
$this->ERROR = "";
$cache_option = 'rss_' . $this->file_name( $url );
if ( ! get_option( $cache_option ) ) {
if ( ! $rss = get_transient( $cache_option ) ) {
$this->debug(
"Cache doesn't contain: $url (cache option: $cache_option)"
);
return 0;
}
$rss = get_option( $cache_option );
return $rss;
}
@ -760,7 +752,7 @@ class RSSCache {
$cache_option = $this->file_name( $url );
$cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
if ( $mtime = get_option($cache_timestamp) ) {
if ( $mtime = get_transient($cache_timestamp) ) {
// find how long ago the file was added to the cache
// and whether that is longer then MAX_AGE
$age = time() - $mtime;