X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/38ca813a0e312e2768e5b9519f0415cd0aa84781..6c8f14c09105d0afa4c1574215c59b5021040e76:/wp-includes/class-http.php diff --git a/wp-includes/class-http.php b/wp-includes/class-http.php index 1cf44041..b50909fd 100644 --- a/wp-includes/class-http.php +++ b/wp-includes/class-http.php @@ -98,7 +98,6 @@ class WP_Http { 'filename' => null ); - // Pre-parse for the HEAD checks. $args = wp_parse_args( $args ); @@ -170,20 +169,17 @@ class WP_Http { if ( WP_Http_Encoding::is_available() ) $r['headers']['Accept-Encoding'] = WP_Http_Encoding::accept_encoding(); - if ( empty($r['body']) ) { - $r['body'] = null; - // Some servers fail when sending content without the content-length header being set. - // Also, to fix another bug, we only send when doing POST and PUT and the content-length - // header isn't already set. - if ( ($r['method'] == 'POST' || $r['method'] == 'PUT') && ! isset( $r['headers']['Content-Length'] ) ) - $r['headers']['Content-Length'] = 0; - } else { + if ( ( ! is_null( $r['body'] ) && '' != $r['body'] ) || 'POST' == $r['method'] || 'PUT' == $r['method'] ) { 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' ); - $r['headers']['Content-Length'] = strlen( $r['body'] ); + + if ( ! isset( $r['headers']['Content-Type'] ) ) + $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ); } + if ( '' === $r['body'] ) + $r['body'] = null; + if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) ) $r['headers']['Content-Length'] = strlen( $r['body'] ); } @@ -200,7 +196,7 @@ class WP_Http { * @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|bool 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' ); @@ -222,7 +218,7 @@ class WP_Http { /** * 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. @@ -253,7 +249,7 @@ class WP_Http { $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; @@ -345,7 +341,7 @@ class WP_Http { * @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) @@ -382,18 +378,18 @@ class WP_Http { list($key, $value) = explode(':', $tempheader, 2); - if ( !empty( $value ) ) { - $key = strtolower( $key ); - if ( isset( $newheaders[$key] ) ) { - if ( !is_array($newheaders[$key]) ) - $newheaders[$key] = array($newheaders[$key]); - $newheaders[$key][] = trim( $value ); - } else { - $newheaders[$key] = trim( $value ); - } - if ( 'set-cookie' == $key ) - $cookies[] = new WP_Http_Cookie( $value ); + $key = strtolower( $key ); + $value = trim( $value ); + + if ( isset( $newheaders[ $key ] ) ) { + if ( ! is_array( $newheaders[ $key ] ) ) + $newheaders[$key] = array( $newheaders[ $key ] ); + $newheaders[ $key ][] = $value; + } else { + $newheaders[ $key ] = $value; } + if ( 'set-cookie' == $key ) + $cookies[] = new WP_Http_Cookie( $value ); } return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies); @@ -412,7 +408,7 @@ class WP_Http { * * @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 ) { @@ -429,6 +425,8 @@ class WP_Http { * Based off the HTTP http_encoding_dechunk function. Does not support UTF-8. Does not support * returning footer headers. Shouldn't be too difficult to support it though. * + * @link http://tools.ietf.org/html/rfc2616#section-19.4.6 Process for chunked decoding. + * * @todo Add support for footer chunked headers. * @access public * @since 2.7.0 @@ -534,8 +532,55 @@ class WP_Http { 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, '/' ); } } @@ -631,7 +676,7 @@ class WP_Http_Fsockopen { $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 ) @@ -732,7 +777,7 @@ class WP_Http_Fsockopen { // 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.')); } @@ -755,11 +800,11 @@ class WP_Http_Fsockopen { * @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; - if ( false !== ($option = get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours + if ( false !== ( $option = get_option( 'disable_fsockopen' ) ) && time() - $option < 12 * HOUR_IN_SECONDS ) return false; $is_ssl = isset( $args['ssl'] ) && $args['ssl']; @@ -865,7 +910,7 @@ class WP_Http_Streams { $arrContext['http']['header'] .= $proxy->authentication_header() . "\r\n"; } - if ( ! empty($r['body'] ) ) + if ( ! is_null( $r['body'] ) ) $arrContext['http']['content'] = $r['body']; $context = stream_context_create($arrContext); @@ -915,7 +960,7 @@ class WP_Http_Streams { 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.')); @@ -938,7 +983,7 @@ class WP_Http_Streams { * * @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; @@ -1022,16 +1067,15 @@ class WP_Http_Curl { } } - $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 ); @@ -1041,7 +1085,9 @@ class WP_Http_Curl { 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': @@ -1055,10 +1101,15 @@ class WP_Http_Curl { curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); break; + default: + curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] ); + if ( ! is_null( $r['body'] ) ) + curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); + break; } if ( true === $r['blocking'] ) - curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( &$this, 'stream_headers' ) ); + curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) ); curl_setopt( $handle, CURLOPT_HEADER, false ); @@ -1073,10 +1124,6 @@ class WP_Http_Curl { 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(); @@ -1109,15 +1156,15 @@ class WP_Http_Curl { 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 ); @@ -1129,9 +1176,9 @@ class WP_Http_Curl { 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.' ) ); } @@ -1165,7 +1212,7 @@ class WP_Http_Curl { * * @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; @@ -1343,6 +1390,10 @@ class WP_HTTP_Proxy { $home = parse_url( get_option('siteurl') ); + $result = apply_filters( 'pre_http_send_through_proxy', null, $uri, $check, $home ); + if ( ! is_null( $result ) ) + return $result; + if ( $check['host'] == 'localhost' || $check['host'] == $home['host'] ) return false; @@ -1493,11 +1544,11 @@ class WP_Http_Cookie { * @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 ( time() > $this->expires ) + if ( isset( $this->expires ) && time() > $this->expires ) return false; // Get details on the URL we're thinking about sending to @@ -1537,10 +1588,10 @@ class WP_Http_Cookie { * @return string Header encoded cookie name and value. */ function getHeaderValue() { - if ( empty( $this->name ) || empty( $this->value ) ) + if ( ! isset( $this->name ) || ! isset( $this->value ) ) return ''; - return $this->name . '=' . urlencode( $this->value ); + return $this->name . '=' . apply_filters( 'wp_http_cookie_value', $this->value, $this->name ); } /** @@ -1579,7 +1630,7 @@ class WP_Http_Encoding { * @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 ); } @@ -1597,7 +1648,7 @@ class WP_Http_Encoding { * @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; @@ -1624,18 +1675,26 @@ class WP_Http_Encoding { /** * 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. + * Certain Servers will return deflated data with headers which PHP's gzinflate() + * 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) ); @@ -1651,10 +1710,17 @@ class WP_Http_Encoding { 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; } /** @@ -1664,7 +1730,7 @@ class WP_Http_Encoding { * * @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'; @@ -1679,13 +1745,13 @@ class WP_Http_Encoding { } /** - * 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'; } @@ -1697,7 +1763,7 @@ class WP_Http_Encoding { * @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; @@ -1719,7 +1785,7 @@ class WP_Http_Encoding { * * @return bool */ - function is_available() { + public static function is_available() { return ( function_exists('gzuncompress') || function_exists('gzdeflate') || function_exists('gzinflate') ); } }