X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/9e77185fafaf4e60e2b73821e0e4b9b1a11fb85f..53f4633144ed68c8b8fb5861f992b5489894a940:/wp-includes/class-http.php diff --git a/wp-includes/class-http.php b/wp-includes/class-http.php index 8a238363..5c082970 100644 --- a/wp-includes/class-http.php +++ b/wp-includes/class-http.php @@ -5,7 +5,7 @@ * Standardizes the HTTP requests for WordPress. Handles cookies, gzip encoding and decoding, chunk * decoding, if HTTP 1.1 and various other difficult HTTP protocol implementations. * - * @link http://trac.wordpress.org/ticket/4779 HTTP API Proposal + * @link https://core.trac.wordpress.org/ticket/4779 HTTP API Proposal * * @package WordPress * @subpackage HTTP @@ -36,6 +36,8 @@ class WP_Http { * @access public * @since 2.7.0 * + * @global string $wp_version + * * @param string $url The request URL. * @param string|array $args { * Optional. Array or string of HTTP request arguments. @@ -182,7 +184,9 @@ class WP_Http { if ( function_exists( 'wp_kses_bad_protocol' ) ) { if ( $r['reject_unsafe_urls'] ) $url = wp_http_validate_url( $url ); - $url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) ); + if ( $url ) { + $url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) ); + } } $arrURL = @parse_url( $url ); @@ -208,8 +212,9 @@ class WP_Http { * If we are streaming to a file but no filename was given drop it in the WP temp dir * and pick its name using the basename of the $url. */ - if ( $r['stream'] && empty( $r['filename'] ) ) - $r['filename'] = get_temp_dir() . basename( $url ); + if ( $r['stream'] && empty( $r['filename'] ) ) { + $r['filename'] = get_temp_dir() . wp_unique_filename( get_temp_dir(), basename( $url ) ); + } /* * Force some settings if we are streaming to a file and check for existence and perms @@ -225,7 +230,7 @@ class WP_Http { $r['headers'] = array(); if ( ! is_array( $r['headers'] ) ) { - $processedHeaders = WP_Http::processHeaders( $r['headers'], $url ); + $processedHeaders = self::processHeaders( $r['headers'], $url ); $r['headers'] = $processedHeaders['headers']; } @@ -244,7 +249,7 @@ class WP_Http { } // Construct Cookie: header if any cookies are set. - WP_Http::buildCookieHeader( $r ); + self::buildCookieHeader( $r ); // Avoid issues where mbstring.func_overload is enabled. mbstring_binary_safe_encoding(); @@ -298,7 +303,7 @@ class WP_Http { * @param array $args Request arguments * @param string $url URL to 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. + * @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 ) { /** @@ -336,6 +341,8 @@ class WP_Http { * The order for requests is cURL, and then PHP Streams. * * @since 3.2.0 + * + * @static * @access private * * @param string $url URL to Request @@ -524,6 +531,9 @@ class WP_Http { $cookies[] = new WP_Http_Cookie( $value, $url ); } + // Cast the Response Code to an int + $response['code'] = intval( $response['code'] ); + return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies); } @@ -615,8 +625,11 @@ class WP_Http { * are supported, eg *.wordpress.org will allow for all subdomains of wordpress.org to be contacted. * * @since 2.8.0 - * @link http://core.trac.wordpress.org/ticket/8927 Allow preventing external requests. - * @link http://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_ACCESSIBLE_HOSTS + * @link https://core.trac.wordpress.org/ticket/8927 Allow preventing external requests. + * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_ACCESSIBLE_HOSTS + * + * @staticvar array|null $accessible_hosts + * @staticvar array $wildcard_regex * * @param string $uri URI of url. * @return bool True to block, false to allow. @@ -648,9 +661,9 @@ class WP_Http { if ( !defined('WP_ACCESSIBLE_HOSTS') ) return true; - static $accessible_hosts; - static $wildcard_regex = false; - if ( null == $accessible_hosts ) { + static $accessible_hosts = null; + static $wildcard_regex = array(); + if ( null === $accessible_hosts ) { $accessible_hosts = preg_split('|,\s*|', WP_ACCESSIBLE_HOSTS); if ( false !== strpos(WP_ACCESSIBLE_HOSTS, '*') ) { @@ -668,23 +681,93 @@ class WP_Http { } + /** + * A wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7 + * + * PHP 5.4.7 expanded parse_url()'s ability to handle non-absolute url's, including + * schemeless and relative url's with :// in the path, this works around those + * limitations providing a standard output on PHP 5.2~5.4+. + * + * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated + * when URL parsing failed. + * + * @since 4.1.0 + * + * @static + * @access protected + * + * @param string $url The URL to parse. + * @return bool|array False on failure; Array of URL components on success; + * See parse_url()'s return values. + */ + protected static function parse_url( $url ) { + $parts = @parse_url( $url ); + if ( ! $parts ) { + // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path + if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) { + // Since we know it's a relative path, prefix with a scheme/host placeholder and try again + if ( ! $parts = @parse_url( 'placeholder://placeholder' . $url ) ) { + return $parts; + } + // Remove the placeholder values + unset( $parts['scheme'], $parts['host'] ); + } else { + return $parts; + } + } + + // < PHP 5.4.7 compat, doesn't detect schemeless URL's host field + if ( '//' == substr( $url, 0, 2 ) && ! isset( $parts['host'] ) ) { + list( $parts['host'], $slashless_path ) = explode( '/', substr( $parts['path'], 2 ), 2 ); + $parts['path'] = "/{$slashless_path}"; + } + + return $parts; + } + + /** + * Converts a relative URL to an absolute URL relative to a given URL. + * + * If an Absolute URL is provided, no processing of that URL is done. + * + * @since 3.4.0 + * + * @static + * @access public + * + * @param string $maybe_relative_path The URL which might be relative + * @param string $url The URL which $maybe_relative_path is relative to + * @return string An Absolute URL, in a failure condition where the URL cannot be parsed, the relative URL will be returned. + */ public 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, '://' ) ) + if ( ! $url_parts = WP_HTTP::parse_url( $url ) ) { return $maybe_relative_path; + } - if ( ! $url_parts = @parse_url( $url ) ) + if ( ! $relative_url_parts = WP_HTTP::parse_url( $maybe_relative_path ) ) { return $maybe_relative_path; + } - if ( ! $relative_url_parts = @parse_url( $maybe_relative_path ) ) + // Check for a scheme on the 'relative' url + if ( ! empty( $relative_url_parts['scheme'] ) ) { return $maybe_relative_path; + } + + $absolute_path = $url_parts['scheme'] . '://'; - $absolute_path = $url_parts['scheme'] . '://' . $url_parts['host']; - if ( isset( $url_parts['port'] ) ) - $absolute_path .= ':' . $url_parts['port']; + // Schemeless URL's will make it this far, so we check for a host in the relative url and convert it to a protocol-url + if ( isset( $relative_url_parts['host'] ) ) { + $absolute_path .= $relative_url_parts['host']; + if ( isset( $relative_url_parts['port'] ) ) + $absolute_path .= ':' . $relative_url_parts['port']; + } else { + $absolute_path .= $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'] : '/'; @@ -722,6 +805,8 @@ class WP_Http { * * @since 3.7.0 * + * @static + * * @param string $url The URL which was requested. * @param array $args The Arguments which were used to make the request. * @param array $response The Response of the HTTP request. @@ -823,12 +908,12 @@ class WP_Http_Streams { $r = wp_parse_args( $args, $defaults ); - if ( isset($r['headers']['User-Agent']) ) { + if ( isset( $r['headers']['User-Agent'] ) ) { $r['user-agent'] = $r['headers']['User-Agent']; - unset($r['headers']['User-Agent']); - } else if ( isset($r['headers']['user-agent']) ) { + unset( $r['headers']['User-Agent'] ); + } elseif ( isset( $r['headers']['user-agent'] ) ) { $r['user-agent'] = $r['headers']['user-agent']; - unset($r['headers']['user-agent']); + unset( $r['headers']['user-agent'] ); } // Construct Cookie: header if any cookies are set. @@ -848,6 +933,11 @@ class WP_Http_Streams { } } + // Always pass a Path, defaulting to the root in cases such as http://example.com + if ( ! isset( $arrURL['path'] ) ) { + $arrURL['path'] = '/'; + } + if ( isset( $r['headers']['Host'] ) || isset( $r['headers']['host'] ) ) { if ( isset( $r['headers']['Host'] ) ) $arrURL['host'] = $r['headers']['Host']; @@ -952,15 +1042,19 @@ class WP_Http_Streams { else $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' ); - if ( empty($requestPath) ) - $requestPath .= '/'; - $strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n"; - if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) + $include_port_in_host_header = ( + ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || + ( 'http' == $arrURL['scheme'] && 80 != $arrURL['port'] ) || + ( 'https' == $arrURL['scheme'] && 443 != $arrURL['port'] ) + ); + + if ( $include_port_in_host_header ) { $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; - else + } else { $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; + } if ( isset($r['user-agent']) ) $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n"; @@ -1020,8 +1114,10 @@ class WP_Http_Streams { $this_block_size = strlen( $block ); - if ( isset( $r['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $r['limit_response_size'] ) - $block = substr( $block, 0, ( $r['limit_response_size'] - $bytes_written ) ); + if ( isset( $r['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $r['limit_response_size'] ) { + $this_block_size = ( $r['limit_response_size'] - $bytes_written ); + $block = substr( $block, 0, $this_block_size ); + } $bytes_written_to_file = fwrite( $stream_handle, $block ); @@ -1159,7 +1255,7 @@ class WP_Http_Streams { * @since 2.7.0 * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). * - * @return boolean False means this class can not be used, true means it can. + * @return bool False means this class can not be used, true means it can. */ public static function test( $args = array() ) { if ( ! function_exists( 'stream_socket_client' ) ) @@ -1248,6 +1344,15 @@ class WP_Http_Curl { */ private $stream_handle = false; + /** + * The total bytes written in the current request. + * + * @since 4.1.0 + * @access private + * @var int + */ + private $bytes_written_total = 0; + /** * Send a HTTP request to a URI using cURL extension. * @@ -1268,12 +1373,12 @@ class WP_Http_Curl { $r = wp_parse_args( $args, $defaults ); - if ( isset($r['headers']['User-Agent']) ) { + if ( isset( $r['headers']['User-Agent'] ) ) { $r['user-agent'] = $r['headers']['User-Agent']; - unset($r['headers']['User-Agent']); - } else if ( isset($r['headers']['user-agent']) ) { + unset( $r['headers']['User-Agent'] ); + } elseif ( isset( $r['headers']['user-agent'] ) ) { $r['user-agent'] = $r['headers']['user-agent']; - unset($r['headers']['user-agent']); + unset( $r['headers']['user-agent'] ); } // Construct Cookie: header if any cookies are set. @@ -1420,21 +1525,32 @@ class WP_Http_Curl { curl_exec( $handle ); $theHeaders = WP_Http::processHeaders( $this->headers, $url ); $theBody = $this->body; + $bytes_written_total = $this->bytes_written_total; $this->headers = ''; $this->body = ''; + $this->bytes_written_total = 0; $curl_error = curl_errno( $handle ); // If an error occurred, or, no response. if ( $curl_error || ( 0 == strlen( $theBody ) && empty( $theHeaders['headers'] ) ) ) { - if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error && $r['stream'] ) { - fclose( $this->stream_handle ); - return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); - } - if ( $curl_error = curl_error( $handle ) ) { - curl_close( $handle ); - return new WP_Error( 'http_request_failed', $curl_error ); + if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error ) { + if ( ! $this->max_body_length || $this->max_body_length != $bytes_written_total ) { + if ( $r['stream'] ) { + curl_close( $handle ); + fclose( $this->stream_handle ); + return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); + } else { + curl_close( $handle ); + return new WP_Error( 'http_request_failed', curl_error( $handle ) ); + } + } + } else { + if ( $curl_error = curl_error( $handle ) ) { + curl_close( $handle ); + return new WP_Error( 'http_request_failed', $curl_error ); + } } if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) { curl_close( $handle ); @@ -1442,10 +1558,6 @@ class WP_Http_Curl { } } - $response = array(); - $response['code'] = curl_getinfo( $handle, CURLINFO_HTTP_CODE ); - $response['message'] = get_status_header_desc($response['code']); - curl_close( $handle ); if ( $r['stream'] ) @@ -1454,7 +1566,7 @@ class WP_Http_Curl { $response = array( 'headers' => $theHeaders['headers'], 'body' => null, - 'response' => $response, + 'response' => $theHeaders['response'], 'cookies' => $theHeaders['cookies'], 'filename' => $r['filename'] ); @@ -1489,7 +1601,7 @@ class WP_Http_Curl { * Grab the body of the cURL request * * The contents of the document are passed in chunks, so we append to the $body property for temporary storage. - * Returning a length shorter than the length of $data passed in will cause cURL to abort the request as "completed" + * Returning a length shorter than the length of $data passed in will cause cURL to abort the request with CURLE_WRITE_ERROR * * @since 3.6.0 * @access private @@ -1498,8 +1610,10 @@ class WP_Http_Curl { private function stream_body( $handle, $data ) { $data_length = strlen( $data ); - if ( $this->max_body_length && ( strlen( $this->body ) + $data_length ) > $this->max_body_length ) - $data = substr( $data, 0, ( $this->max_body_length - $data_length ) ); + if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) { + $data_length = ( $this->max_body_length - $this->bytes_written_total ); + $data = substr( $data, 0, $data_length ); + } if ( $this->stream_handle ) { $bytes_written = fwrite( $this->stream_handle, $data ); @@ -1508,6 +1622,8 @@ class WP_Http_Curl { $bytes_written = $data_length; } + $this->bytes_written_total += $bytes_written; + // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR. return $bytes_written; } @@ -1518,7 +1634,7 @@ class WP_Http_Curl { * @static * @since 2.7.0 * - * @return boolean False means this class can not be used, true means it can. + * @return bool False means this class can not be used, true means it can. */ public static function test( $args = array() ) { if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) @@ -1567,14 +1683,13 @@ class WP_Http_Curl { * * * An example can be as seen below. - * - * define('WP_PROXY_HOST', '192.168.84.101'); - * define('WP_PROXY_PORT', '8080'); - * define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com, *.wordpress.org'); - * * - * @link http://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress. - * @link http://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_PROXY_BYPASS_HOSTS + * define('WP_PROXY_HOST', '192.168.84.101'); + * define('WP_PROXY_PORT', '8080'); + * define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com, *.wordpress.org'); + * + * @link https://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress. + * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_PROXY_BYPASS_HOSTS * @since 2.8.0 */ class WP_HTTP_Proxy { @@ -1692,9 +1807,11 @@ class WP_HTTP_Proxy { * some proxies can not handle this. We also have the constant available for defining other * hosts that won't be sent through the proxy. * - * @uses WP_PROXY_BYPASS_HOSTS * @since 2.8.0 * + * @staticvar array|null $bypass_hosts + * @staticvar array $wildcard_regex + * * @param string $uri URI to check. * @return bool True, to send through the proxy and false if, the proxy should not be used. */ @@ -1734,9 +1851,9 @@ class WP_HTTP_Proxy { if ( !defined('WP_PROXY_BYPASS_HOSTS') ) return true; - static $bypass_hosts; - static $wildcard_regex = false; - if ( null == $bypass_hosts ) { + static $bypass_hosts = null; + static $wildcard_regex = array(); + if ( null === $bypass_hosts ) { $bypass_hosts = preg_split('|,\s*|', WP_PROXY_BYPASS_HOSTS); if ( false !== strpos(WP_PROXY_BYPASS_HOSTS, '*') ) { @@ -1868,7 +1985,7 @@ class WP_Http_Cookie { } } else { if ( !isset( $data['name'] ) ) - return false; + return; // Set properties based directly on parameters. foreach ( array( 'name', 'value', 'path', 'domain', 'port' ) as $field ) { @@ -1892,7 +2009,7 @@ 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 bool true if allowed, false otherwise. */ public function test( $url ) { if ( is_null( $this->name ) ) @@ -1984,10 +2101,12 @@ class WP_Http_Encoding { * * @since 2.8.0 * + * @static + * * @param string $raw String to compress. * @param int $level Optional, default is 9. Compression level, 9 is highest. * @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. + * @return string|false False on failure. */ public static function compress( $raw, $level = 9, $supports = null ) { return gzdeflate( $raw, $level ); @@ -2003,6 +2122,8 @@ class WP_Http_Encoding { * * @since 2.8.0 * + * @static + * * @param string $compressed String to decompress. * @param int $length The optional length of the compressed data. * @return string|bool False on failure. @@ -2015,7 +2136,7 @@ class WP_Http_Encoding { if ( false !== ( $decompressed = @gzinflate( $compressed ) ) ) return $decompressed; - if ( false !== ( $decompressed = WP_Http_Encoding::compatible_gzinflate( $compressed ) ) ) + if ( false !== ( $decompressed = self::compatible_gzinflate( $compressed ) ) ) return $decompressed; if ( false !== ( $decompressed = @gzuncompress( $compressed ) ) ) @@ -2041,13 +2162,15 @@ class WP_Http_Encoding { * 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 + * https://core.trac.wordpress.org/ticket/18273 * * @since 2.8.1 - * @link http://core.trac.wordpress.org/ticket/18273 + * @link https://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 * + * @static + * * @param string $gzData String to decompress. * @return string|bool False on failure. */ @@ -2087,11 +2210,15 @@ class WP_Http_Encoding { * * @since 2.8.0 * + * @static + * + * @param string $url + * @param array $args * @return string Types of encoding to accept. */ public static function accept_encoding( $url, $args ) { $type = array(); - $compression_enabled = WP_Http_Encoding::is_available(); + $compression_enabled = self::is_available(); if ( ! $args['decompress'] ) // Decompression specifically disabled. $compression_enabled = false; @@ -2131,6 +2258,8 @@ class WP_Http_Encoding { * * @since 2.8.0 * + * @static + * * @return string Content-Encoding string to send in the header. */ public static function content_encoding() { @@ -2142,6 +2271,8 @@ class WP_Http_Encoding { * * @since 2.8.0 * + * @static + * * @param array|string $headers All of the available headers. * @return bool */ @@ -2149,7 +2280,7 @@ class WP_Http_Encoding { if ( is_array( $headers ) ) { if ( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) ) return true; - } else if ( is_string( $headers ) ) { + } elseif ( is_string( $headers ) ) { return ( stripos($headers, 'content-encoding:') !== false ); } @@ -2165,6 +2296,8 @@ class WP_Http_Encoding { * * @since 2.8.0 * + * @static + * * @return bool */ public static function is_available() {