'filename' => null
);
-
// Pre-parse for the HEAD checks.
$args = wp_parse_args( $args );
} else {
if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) {
$r['body'] = http_build_query( $r['body'], null, '&' );
- $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' );
+ if ( ! isset( $r['headers']['Content-Type'] ) )
+ $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' );
$r['headers']['Content-Length'] = strlen( $r['body'] );
}
* @param array $args Request arguments
* @param string $url URL to Request
*
- * @return string|false Class name for the first transport that claims to support the request. False if no transport claims to support the request.
+ * @return string|false Class name for the first transport that claims to support the request. False if no transport claims to support the request.
*/
public function _get_first_available_transport( $args, $url = null ) {
$request_order = array( 'curl', 'streams', 'fsockopen' );
/**
* Dispatches a HTTP request to a supporting transport.
*
- * Tests each transport in order to find a transport which matches the request arguements.
+ * Tests each transport in order to find a transport which matches the request arguments.
* Also caches the transport instance to be used later.
*
* The order for blocking requests is cURL, Streams, and finally Fsockopen.
$response = $transports[$class]->request( $url, $args );
- do_action( 'http_api_debug', $response, 'response', $class );
+ do_action( 'http_api_debug', $response, 'response', $class, $args, $url );
if ( is_wp_error( $response ) )
return $response;
* @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) {
+ public static function processHeaders($headers) {
// split headers, one per array element
if ( is_string($headers) ) {
// tolerate line terminator: CRLF = LF (RFC 2616 19.3)
*
* @param array $r Full array of args passed into ::request()
*/
- function buildCookieHeader( &$r ) {
+ public static function buildCookieHeader( &$r ) {
if ( ! empty($r['cookies']) ) {
$cookies_header = '';
foreach ( (array) $r['cookies'] as $cookie ) {
else
return !in_array( $check['host'], $accessible_hosts ); //Inverse logic, If its in the array, then we can't access it.
+ }
+
+ static function make_absolute_url( $maybe_relative_path, $url ) {
+ if ( empty( $url ) )
+ return $maybe_relative_path;
+
+ // Check for a scheme
+ if ( false !== strpos( $maybe_relative_path, '://' ) )
+ return $maybe_relative_path;
+
+ if ( ! $url_parts = @parse_url( $url ) )
+ return $maybe_relative_path;
+
+ if ( ! $relative_url_parts = @parse_url( $maybe_relative_path ) )
+ return $maybe_relative_path;
+
+ $absolute_path = $url_parts['scheme'] . '://' . $url_parts['host'];
+ if ( isset( $url_parts['port'] ) )
+ $absolute_path .= ':' . $url_parts['port'];
+
+ // Start off with the Absolute URL path
+ $path = ! empty( $url_parts['path'] ) ? $url_parts['path'] : '/';
+ // If the it's a root-relative path, then great
+ if ( ! empty( $relative_url_parts['path'] ) && '/' == $relative_url_parts['path'][0] ) {
+ $path = $relative_url_parts['path'];
+ // Else it's a relative path
+ } elseif ( ! empty( $relative_url_parts['path'] ) ) {
+ // Strip off any file components from the absolute path
+ $path = substr( $path, 0, strrpos( $path, '/' ) + 1 );
+
+ // Build the new path
+ $path .= $relative_url_parts['path'];
+
+ // Strip all /path/../ out of the path
+ while ( strpos( $path, '../' ) > 1 ) {
+ $path = preg_replace( '![^/]+/\.\./!', '', $path );
+ }
+
+ // Strip any final leading ../ from the path
+ $path = preg_replace( '!^/(\.\./)+!', '', $path );
+ }
+
+ // Add the Query string
+ if ( ! empty( $relative_url_parts['query'] ) )
+ $path .= '?' . $relative_url_parts['query'];
+
+ return $absolute_path . '/' . ltrim( $path, '/' );
}
}
$endDelay = time();
- // If the delay is greater than the timeout then fsockopen should't be used, because it will
+ // If the delay is greater than the timeout then fsockopen shouldn't be used, because it will
// cause a long delay.
$elapseDelay = ($endDelay-$startDelay) > $r['timeout'];
if ( true === $elapseDelay )
// If location is found, then assume redirect and redirect to location.
if ( isset($arrHeaders['headers']['location']) && 0 !== $r['_redirection'] ) {
if ( $r['redirection']-- > 0 ) {
- return $this->request($arrHeaders['headers']['location'], $r);
+ return $this->request( WP_HTTP::make_absolute_url( $arrHeaders['headers']['location'], $url ), $r);
} else {
return new WP_Error('http_request_failed', __('Too many redirects.'));
}
* @static
* @return boolean False means this class can not be used, true means it can.
*/
- function test( $args = array() ) {
+ public static function test( $args = array() ) {
if ( ! function_exists( 'fsockopen' ) )
return false;
else
$processedHeaders = WP_Http::processHeaders($meta['wrapper_data']);
- // Streams does not provide an error code which we can use to see why the request stream stoped.
+ // Streams does not provide an error code which we can use to see why the request stream stopped.
// We can however test to see if a location header is present and return based on that.
if ( isset($processedHeaders['headers']['location']) && 0 !== $args['_redirection'] )
return new WP_Error('http_request_failed', __('Too many redirects.'));
*
* @return boolean False means this class can not be used, true means it can.
*/
- function test( $args = array() ) {
+ public static function test( $args = array() ) {
if ( ! function_exists( 'fopen' ) )
return false;
}
}
- $is_local = isset($args['local']) && $args['local'];
- $ssl_verify = isset($args['sslverify']) && $args['sslverify'];
+ $is_local = isset($r['local']) && $r['local'];
+ $ssl_verify = isset($r['sslverify']) && $r['sslverify'];
if ( $is_local )
$ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
elseif ( ! $is_local )
$ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
-
- // CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since
- // a value of 0 will allow an ulimited timeout.
+ // CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since
+ // a value of 0 will allow an unlimited timeout.
$timeout = (int) ceil( $r['timeout'] );
curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout );
curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout );
curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false );
curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
- curl_setopt( $handle, CURLOPT_MAXREDIRS, $r['redirection'] );
+ // The option doesn't work with safe mode or when open_basedir is set, and there's a
+ // bug #17490 with redirected POST requests, so handle redirections outside Curl.
+ curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false );
switch ( $r['method'] ) {
case 'HEAD':
curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
break;
+ default:
+ curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] );
+ if ( ! empty( $r['body'] ) )
+ curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
+ break;
}
if ( true === $r['blocking'] )
curl_setopt( $handle, CURLOPT_FILE, $stream_handle );
}
- // The option doesn't work with safe mode or when open_basedir is set.
- if ( !ini_get('safe_mode') && !ini_get('open_basedir') && 0 !== $r['_redirection'] )
- curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
-
if ( !empty( $r['headers'] ) ) {
// cURL expects full header strings in each element
$headers = array();
if ( strlen($theResponse) > 0 && ! is_bool( $theResponse ) ) // is_bool: when using $args['stream'], curl_exec will return (bool)true
$theBody = $theResponse;
- // If no response, and It's not a HEAD request with valid headers returned
- if ( 0 == strlen($theResponse) && ('HEAD' != $args['method'] || empty($this->headers)) ) {
- if ( $curl_error = curl_error($handle) )
- return new WP_Error('http_request_failed', $curl_error);
- if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
- return new WP_Error('http_request_failed', __('Too many redirects.'));
+ // If no response
+ if ( 0 == strlen( $theResponse ) && empty( $theHeaders['headers'] ) ) {
+ if ( $curl_error = curl_error( $handle ) )
+ return new WP_Error( 'http_request_failed', $curl_error );
+ if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) )
+ return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
}
- unset( $this->headers );
+ $this->headers = '';
$response = array();
$response['code'] = curl_getinfo( $handle, CURLINFO_HTTP_CODE );
fclose( $stream_handle );
// See #11305 - When running under safe mode, redirection is disabled above. Handle it manually.
- if ( ! empty( $theHeaders['headers']['location'] ) && ( ini_get( 'safe_mode' ) || ini_get( 'open_basedir' ) ) && 0 !== $r['_redirection'] ) {
+ if ( ! empty( $theHeaders['headers']['location'] ) && 0 !== $r['_redirection'] ) { // _redirection: The requested number of redirections
if ( $r['redirection']-- > 0 ) {
- return $this->request( $theHeaders['headers']['location'], $r );
+ return $this->request( WP_HTTP::make_absolute_url( $theHeaders['headers']['location'], $url ), $r );
} else {
return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
}
*
* @return boolean False means this class can not be used, true means it can.
*/
- function test( $args = array() ) {
+ public static function test( $args = array() ) {
if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) )
return false;
* @since 2.8.0
*
* @param string $url URL you intend to send this cookie to
- * @return boolean TRUE if allowed, FALSE otherwise.
+ * @return boolean true if allowed, false otherwise.
*/
function test( $url ) {
// Expires - if expired then nothing else matters
if ( empty( $this->name ) || empty( $this->value ) )
return '';
- return $this->name . '=' . urlencode( $this->value );
+ return $this->name . '=' . apply_filters( 'wp_http_cookie_value', $this->value, $this->name );
}
/**
* @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports.
* @return string|bool False on failure.
*/
- function compress( $raw, $level = 9, $supports = null ) {
+ public static function compress( $raw, $level = 9, $supports = null ) {
return gzdeflate( $raw, $level );
}
* @param int $length The optional length of the compressed data.
* @return string|bool False on failure.
*/
- function decompress( $compressed, $length = null ) {
+ public static function decompress( $compressed, $length = null ) {
if ( empty($compressed) )
return $compressed;
* Decompression of deflated string while staying compatible with the majority of servers.
*
* Certain Servers will return deflated data with headers which PHP's gziniflate()
- * function cannot handle out of the box. The following function lifted from
- * http://au2.php.net/manual/en/function.gzinflate.php#77336 will attempt to deflate
- * the various return forms used.
+ * function cannot handle out of the box. The following function has been created from
+ * various snippets on the gzinflate() PHP documentation.
+ *
+ * Warning: Magic numbers within. Due to the potential different formats that the compressed
+ * data may be returned in, some "magic offsets" are needed to ensure proper decompression
+ * takes place. For a simple progmatic way to determine the magic offset in use, see:
+ * http://core.trac.wordpress.org/ticket/18273
*
* @since 2.8.1
+ * @link http://core.trac.wordpress.org/ticket/18273
+ * @link http://au2.php.net/manual/en/function.gzinflate.php#70875
* @link http://au2.php.net/manual/en/function.gzinflate.php#77336
*
* @param string $gzData String to decompress.
* @return string|bool False on failure.
*/
- function compatible_gzinflate($gzData) {
+ public static function compatible_gzinflate($gzData) {
+
+ // Compressed data might contain a full header, if so strip it for gzinflate()
if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) {
$i = 10;
$flg = ord( substr($gzData, 3, 1) );
if ( $flg & 2 )
$i = $i + 2;
}
- return gzinflate( substr($gzData, $i, -8) );
- } else {
- return false;
+ $decompressed = @gzinflate( substr($gzData, $i, -8) );
+ if ( false !== $decompressed )
+ return $decompressed;
}
+
+ // Compressed data from java.util.zip.Deflater amongst others.
+ $decompressed = @gzinflate( substr($gzData, 2) );
+ if ( false !== $decompressed )
+ return $decompressed;
+
+ return false;
}
/**
*
* @return string Types of encoding to accept.
*/
- function accept_encoding() {
+ public static function accept_encoding() {
$type = array();
if ( function_exists( 'gzinflate' ) )
$type[] = 'deflate;q=1.0';
}
/**
- * What enconding the content used when it was compressed to send in the headers.
+ * What encoding the content used when it was compressed to send in the headers.
*
* @since 2.8
*
* @return string Content-Encoding string to send in the header.
*/
- function content_encoding() {
+ public static function content_encoding() {
return 'deflate';
}
* @param array|string $headers All of the available headers.
* @return bool
*/
- function should_decode($headers) {
+ public static function should_decode($headers) {
if ( is_array( $headers ) ) {
if ( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) )
return true;
*
* @return bool
*/
- function is_available() {
+ public static function is_available() {
return ( function_exists('gzuncompress') || function_exists('gzdeflate') || function_exists('gzinflate') );
}
}