diff options
| author | Charles <sircharlesaze@gmail.com> | 2020-01-07 13:06:14 +0100 |
|---|---|---|
| committer | Charles <sircharlesaze@gmail.com> | 2020-01-07 13:06:14 +0100 |
| commit | 7086111ad4dd997e12a3220e1ee60c9b9bcf0bb8 (patch) | |
| tree | f7453d7dd5cbaaab246e23810b02d3edf1e451be /srcs/wordpress/wp-includes/Requests | |
| parent | c59bdcf77c50cbe89b4a93782cdd6d9e7532080e (diff) | |
| download | ft_server-7086111ad4dd997e12a3220e1ee60c9b9bcf0bb8.tar.gz ft_server-7086111ad4dd997e12a3220e1ee60c9b9bcf0bb8.tar.bz2 ft_server-7086111ad4dd997e12a3220e1ee60c9b9bcf0bb8.zip | |
Added wordpress
Diffstat (limited to 'srcs/wordpress/wp-includes/Requests')
57 files changed, 5674 insertions, 0 deletions
diff --git a/srcs/wordpress/wp-includes/Requests/Auth.php b/srcs/wordpress/wp-includes/Requests/Auth.php new file mode 100644 index 0000000..bca4109 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Auth.php @@ -0,0 +1,33 @@ +<?php +/** + * Authentication provider interface + * + * @package Requests + * @subpackage Authentication + */ + +/** + * Authentication provider interface + * + * Implement this interface to act as an authentication provider. + * + * Parameters should be passed via the constructor where possible, as this + * makes it much easier for users to use your provider. + * + * @see Requests_Hooks + * @package Requests + * @subpackage Authentication + */ +interface Requests_Auth { + /** + * Register hooks as needed + * + * This method is called in {@see Requests::request} when the user has set + * an instance as the 'auth' option. Use this callback to register all the + * hooks you'll need. + * + * @see Requests_Hooks::register + * @param Requests_Hooks $hooks Hook system + */ + public function register(Requests_Hooks &$hooks); +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Auth/Basic.php b/srcs/wordpress/wp-includes/Requests/Auth/Basic.php new file mode 100644 index 0000000..a355cfd --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Auth/Basic.php @@ -0,0 +1,88 @@ +<?php +/** + * Basic Authentication provider + * + * @package Requests + * @subpackage Authentication + */ + +/** + * Basic Authentication provider + * + * Provides a handler for Basic HTTP authentication via the Authorization + * header. + * + * @package Requests + * @subpackage Authentication + */ +class Requests_Auth_Basic implements Requests_Auth { + /** + * Username + * + * @var string + */ + public $user; + + /** + * Password + * + * @var string + */ + public $pass; + + /** + * Constructor + * + * @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`) + * @param array|null $args Array of user and password. Must have exactly two elements + */ + public function __construct($args = null) { + if (is_array($args)) { + if (count($args) !== 2) { + throw new Requests_Exception('Invalid number of arguments', 'authbasicbadargs'); + } + + list($this->user, $this->pass) = $args; + } + } + + /** + * Register the necessary callbacks + * + * @see curl_before_send + * @see fsockopen_header + * @param Requests_Hooks $hooks Hook system + */ + public function register(Requests_Hooks &$hooks) { + $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); + $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); + } + + /** + * Set cURL parameters before the data is sent + * + * @param resource $handle cURL resource + */ + public function curl_before_send(&$handle) { + curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString()); + } + + /** + * Add extra headers to the request before sending + * + * @param string $out HTTP header string + */ + public function fsockopen_header(&$out) { + $out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString())); + } + + /** + * Get the authentication string (user:pass) + * + * @return string + */ + public function getAuthString() { + return $this->user . ':' . $this->pass; + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Cookie.php b/srcs/wordpress/wp-includes/Requests/Cookie.php new file mode 100644 index 0000000..6fccec6 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Cookie.php @@ -0,0 +1,505 @@ +<?php +/** + * Cookie storage object + * + * @package Requests + * @subpackage Cookies + */ + +/** + * Cookie storage object + * + * @package Requests + * @subpackage Cookies + */ +class Requests_Cookie { + /** + * Cookie name. + * + * @var string + */ + public $name; + + /** + * Cookie value. + * + * @var string + */ + public $value; + + /** + * Cookie attributes + * + * Valid keys are (currently) path, domain, expires, max-age, secure and + * httponly. + * + * @var Requests_Utility_CaseInsensitiveDictionary|array Array-like object + */ + public $attributes = array(); + + /** + * Cookie flags + * + * Valid keys are (currently) creation, last-access, persistent and + * host-only. + * + * @var array + */ + public $flags = array(); + + /** + * Reference time for relative calculations + * + * This is used in place of `time()` when calculating Max-Age expiration and + * checking time validity. + * + * @var int + */ + public $reference_time = 0; + + /** + * Create a new cookie object + * + * @param string $name + * @param string $value + * @param array|Requests_Utility_CaseInsensitiveDictionary $attributes Associative array of attribute data + */ + public function __construct($name, $value, $attributes = array(), $flags = array(), $reference_time = null) { + $this->name = $name; + $this->value = $value; + $this->attributes = $attributes; + $default_flags = array( + 'creation' => time(), + 'last-access' => time(), + 'persistent' => false, + 'host-only' => true, + ); + $this->flags = array_merge($default_flags, $flags); + + $this->reference_time = time(); + if ($reference_time !== null) { + $this->reference_time = $reference_time; + } + + $this->normalize(); + } + + /** + * Check if a cookie is expired. + * + * Checks the age against $this->reference_time to determine if the cookie + * is expired. + * + * @return boolean True if expired, false if time is valid. + */ + public function is_expired() { + // RFC6265, s. 4.1.2.2: + // If a cookie has both the Max-Age and the Expires attribute, the Max- + // Age attribute has precedence and controls the expiration date of the + // cookie. + if (isset($this->attributes['max-age'])) { + $max_age = $this->attributes['max-age']; + return $max_age < $this->reference_time; + } + + if (isset($this->attributes['expires'])) { + $expires = $this->attributes['expires']; + return $expires < $this->reference_time; + } + + return false; + } + + /** + * Check if a cookie is valid for a given URI + * + * @param Requests_IRI $uri URI to check + * @return boolean Whether the cookie is valid for the given URI + */ + public function uri_matches(Requests_IRI $uri) { + if (!$this->domain_matches($uri->host)) { + return false; + } + + if (!$this->path_matches($uri->path)) { + return false; + } + + return empty($this->attributes['secure']) || $uri->scheme === 'https'; + } + + /** + * Check if a cookie is valid for a given domain + * + * @param string $string Domain to check + * @return boolean Whether the cookie is valid for the given domain + */ + public function domain_matches($string) { + if (!isset($this->attributes['domain'])) { + // Cookies created manually; cookies created by Requests will set + // the domain to the requested domain + return true; + } + + $domain_string = $this->attributes['domain']; + if ($domain_string === $string) { + // The domain string and the string are identical. + return true; + } + + // If the cookie is marked as host-only and we don't have an exact + // match, reject the cookie + if ($this->flags['host-only'] === true) { + return false; + } + + if (strlen($string) <= strlen($domain_string)) { + // For obvious reasons, the string cannot be a suffix if the domain + // is shorter than the domain string + return false; + } + + if (substr($string, -1 * strlen($domain_string)) !== $domain_string) { + // The domain string should be a suffix of the string. + return false; + } + + $prefix = substr($string, 0, strlen($string) - strlen($domain_string)); + if (substr($prefix, -1) !== '.') { + // The last character of the string that is not included in the + // domain string should be a %x2E (".") character. + return false; + } + + // The string should be a host name (i.e., not an IP address). + return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $string); + } + + /** + * Check if a cookie is valid for a given path + * + * From the path-match check in RFC 6265 section 5.1.4 + * + * @param string $request_path Path to check + * @return boolean Whether the cookie is valid for the given path + */ + public function path_matches($request_path) { + if (empty($request_path)) { + // Normalize empty path to root + $request_path = '/'; + } + + if (!isset($this->attributes['path'])) { + // Cookies created manually; cookies created by Requests will set + // the path to the requested path + return true; + } + + $cookie_path = $this->attributes['path']; + + if ($cookie_path === $request_path) { + // The cookie-path and the request-path are identical. + return true; + } + + if (strlen($request_path) > strlen($cookie_path) && substr($request_path, 0, strlen($cookie_path)) === $cookie_path) { + if (substr($cookie_path, -1) === '/') { + // The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + return true; + } + + if (substr($request_path, strlen($cookie_path), 1) === '/') { + // The cookie-path is a prefix of the request-path, and the + // first character of the request-path that is not included in + // the cookie-path is a %x2F ("/") character. + return true; + } + } + + return false; + } + + /** + * Normalize cookie and attributes + * + * @return boolean Whether the cookie was successfully normalized + */ + public function normalize() { + foreach ($this->attributes as $key => $value) { + $orig_value = $value; + $value = $this->normalize_attribute($key, $value); + if ($value === null) { + unset($this->attributes[$key]); + continue; + } + + if ($value !== $orig_value) { + $this->attributes[$key] = $value; + } + } + + return true; + } + + /** + * Parse an individual cookie attribute + * + * Handles parsing individual attributes from the cookie values. + * + * @param string $name Attribute name + * @param string|boolean $value Attribute value (string value, or true if empty/flag) + * @return mixed Value if available, or null if the attribute value is invalid (and should be skipped) + */ + protected function normalize_attribute($name, $value) { + switch (strtolower($name)) { + case 'expires': + // Expiration parsing, as per RFC 6265 section 5.2.1 + if (is_int($value)) { + return $value; + } + + $expiry_time = strtotime($value); + if ($expiry_time === false) { + return null; + } + + return $expiry_time; + + case 'max-age': + // Expiration parsing, as per RFC 6265 section 5.2.2 + if (is_int($value)) { + return $value; + } + + // Check that we have a valid age + if (!preg_match('/^-?\d+$/', $value)) { + return null; + } + + $delta_seconds = (int) $value; + if ($delta_seconds <= 0) { + $expiry_time = 0; + } + else { + $expiry_time = $this->reference_time + $delta_seconds; + } + + return $expiry_time; + + case 'domain': + // Domains are not required as per RFC 6265 section 5.2.3 + if (empty($value)) { + return null; + } + + // Domain normalization, as per RFC 6265 section 5.2.3 + if ($value[0] === '.') { + $value = substr($value, 1); + } + + return $value; + + default: + return $value; + } + } + + /** + * Format a cookie for a Cookie header + * + * This is used when sending cookies to a server. + * + * @return string Cookie formatted for Cookie header + */ + public function format_for_header() { + return sprintf('%s=%s', $this->name, $this->value); + } + + /** + * Format a cookie for a Cookie header + * + * @codeCoverageIgnore + * @deprecated Use {@see Requests_Cookie::format_for_header} + * @return string + */ + public function formatForHeader() { + return $this->format_for_header(); + } + + /** + * Format a cookie for a Set-Cookie header + * + * This is used when sending cookies to clients. This isn't really + * applicable to client-side usage, but might be handy for debugging. + * + * @return string Cookie formatted for Set-Cookie header + */ + public function format_for_set_cookie() { + $header_value = $this->format_for_header(); + if (!empty($this->attributes)) { + $parts = array(); + foreach ($this->attributes as $key => $value) { + // Ignore non-associative attributes + if (is_numeric($key)) { + $parts[] = $value; + } + else { + $parts[] = sprintf('%s=%s', $key, $value); + } + } + + $header_value .= '; ' . implode('; ', $parts); + } + return $header_value; + } + + /** + * Format a cookie for a Set-Cookie header + * + * @codeCoverageIgnore + * @deprecated Use {@see Requests_Cookie::format_for_set_cookie} + * @return string + */ + public function formatForSetCookie() { + return $this->format_for_set_cookie(); + } + + /** + * Get the cookie value + * + * Attributes and other data can be accessed via methods. + */ + public function __toString() { + return $this->value; + } + + /** + * Parse a cookie string into a cookie object + * + * Based on Mozilla's parsing code in Firefox and related projects, which + * is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265 + * specifies some of this handling, but not in a thorough manner. + * + * @param string Cookie header value (from a Set-Cookie header) + * @return Requests_Cookie Parsed cookie object + */ + public static function parse($string, $name = '', $reference_time = null) { + $parts = explode(';', $string); + $kvparts = array_shift($parts); + + if (!empty($name)) { + $value = $string; + } + elseif (strpos($kvparts, '=') === false) { + // Some sites might only have a value without the equals separator. + // Deviate from RFC 6265 and pretend it was actually a blank name + // (`=foo`) + // + // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 + $name = ''; + $value = $kvparts; + } + else { + list($name, $value) = explode('=', $kvparts, 2); + } + $name = trim($name); + $value = trim($value); + + // Attribute key are handled case-insensitively + $attributes = new Requests_Utility_CaseInsensitiveDictionary(); + + if (!empty($parts)) { + foreach ($parts as $part) { + if (strpos($part, '=') === false) { + $part_key = $part; + $part_value = true; + } + else { + list($part_key, $part_value) = explode('=', $part, 2); + $part_value = trim($part_value); + } + + $part_key = trim($part_key); + $attributes[$part_key] = $part_value; + } + } + + return new Requests_Cookie($name, $value, $attributes, array(), $reference_time); + } + + /** + * Parse all Set-Cookie headers from request headers + * + * @param Requests_Response_Headers $headers Headers to parse from + * @param Requests_IRI|null $origin URI for comparing cookie origins + * @param int|null $time Reference time for expiration calculation + * @return array + */ + public static function parse_from_headers(Requests_Response_Headers $headers, Requests_IRI $origin = null, $time = null) { + $cookie_headers = $headers->getValues('Set-Cookie'); + if (empty($cookie_headers)) { + return array(); + } + + $cookies = array(); + foreach ($cookie_headers as $header) { + $parsed = self::parse($header, '', $time); + + // Default domain/path attributes + if (empty($parsed->attributes['domain']) && !empty($origin)) { + $parsed->attributes['domain'] = $origin->host; + $parsed->flags['host-only'] = true; + } + else { + $parsed->flags['host-only'] = false; + } + + $path_is_valid = (!empty($parsed->attributes['path']) && $parsed->attributes['path'][0] === '/'); + if (!$path_is_valid && !empty($origin)) { + $path = $origin->path; + + // Default path normalization as per RFC 6265 section 5.1.4 + if (substr($path, 0, 1) !== '/') { + // If the uri-path is empty or if the first character of + // the uri-path is not a %x2F ("/") character, output + // %x2F ("/") and skip the remaining steps. + $path = '/'; + } + elseif (substr_count($path, '/') === 1) { + // If the uri-path contains no more than one %x2F ("/") + // character, output %x2F ("/") and skip the remaining + // step. + $path = '/'; + } + else { + // Output the characters of the uri-path from the first + // character up to, but not including, the right-most + // %x2F ("/"). + $path = substr($path, 0, strrpos($path, '/')); + } + $parsed->attributes['path'] = $path; + } + + // Reject invalid cookie domains + if (!empty($origin) && !$parsed->domain_matches($origin->host)) { + continue; + } + + $cookies[$parsed->name] = $parsed; + } + + return $cookies; + } + + /** + * Parse all Set-Cookie headers from request headers + * + * @codeCoverageIgnore + * @deprecated Use {@see Requests_Cookie::parse_from_headers} + * @return string + */ + public static function parseFromHeaders(Requests_Response_Headers $headers) { + return self::parse_from_headers($headers); + } +} diff --git a/srcs/wordpress/wp-includes/Requests/Cookie/Jar.php b/srcs/wordpress/wp-includes/Requests/Cookie/Jar.php new file mode 100644 index 0000000..69be0fb --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Cookie/Jar.php @@ -0,0 +1,175 @@ +<?php +/** + * Cookie holder object + * + * @package Requests + * @subpackage Cookies + */ + +/** + * Cookie holder object + * + * @package Requests + * @subpackage Cookies + */ +class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate { + /** + * Actual item data + * + * @var array + */ + protected $cookies = array(); + + /** + * Create a new jar + * + * @param array $cookies Existing cookie values + */ + public function __construct($cookies = array()) { + $this->cookies = $cookies; + } + + /** + * Normalise cookie data into a Requests_Cookie + * + * @param string|Requests_Cookie $cookie + * @return Requests_Cookie + */ + public function normalize_cookie($cookie, $key = null) { + if ($cookie instanceof Requests_Cookie) { + return $cookie; + } + + return Requests_Cookie::parse($cookie, $key); + } + + /** + * Normalise cookie data into a Requests_Cookie + * + * @codeCoverageIgnore + * @deprecated Use {@see Requests_Cookie_Jar::normalize_cookie} + * @return Requests_Cookie + */ + public function normalizeCookie($cookie, $key = null) { + return $this->normalize_cookie($cookie, $key); + } + + /** + * Check if the given item exists + * + * @param string $key Item key + * @return boolean Does the item exist? + */ + public function offsetExists($key) { + return isset($this->cookies[$key]); + } + + /** + * Get the value for the item + * + * @param string $key Item key + * @return string Item value + */ + public function offsetGet($key) { + if (!isset($this->cookies[$key])) { + return null; + } + + return $this->cookies[$key]; + } + + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $this->cookies[$key] = $value; + } + + /** + * Unset the given header + * + * @param string $key + */ + public function offsetUnset($key) { + unset($this->cookies[$key]); + } + + /** + * Get an iterator for the data + * + * @return ArrayIterator + */ + public function getIterator() { + return new ArrayIterator($this->cookies); + } + + /** + * Register the cookie handler with the request's hooking system + * + * @param Requests_Hooker $hooks Hooking system + */ + public function register(Requests_Hooker $hooks) { + $hooks->register('requests.before_request', array($this, 'before_request')); + $hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check')); + } + + /** + * Add Cookie header to a request if we have any + * + * As per RFC 6265, cookies are separated by '; ' + * + * @param string $url + * @param array $headers + * @param array $data + * @param string $type + * @param array $options + */ + public function before_request($url, &$headers, &$data, &$type, &$options) { + if (!$url instanceof Requests_IRI) { + $url = new Requests_IRI($url); + } + + if (!empty($this->cookies)) { + $cookies = array(); + foreach ($this->cookies as $key => $cookie) { + $cookie = $this->normalize_cookie($cookie, $key); + + // Skip expired cookies + if ($cookie->is_expired()) { + continue; + } + + if ($cookie->domain_matches($url->host)) { + $cookies[] = $cookie->format_for_header(); + } + } + + $headers['Cookie'] = implode('; ', $cookies); + } + } + + /** + * Parse all cookies from a response and attach them to the response + * + * @var Requests_Response $response + */ + public function before_redirect_check(Requests_Response &$return) { + $url = $return->url; + if (!$url instanceof Requests_IRI) { + $url = new Requests_IRI($url); + } + + $cookies = Requests_Cookie::parse_from_headers($return->headers, $url); + $this->cookies = array_merge($this->cookies, $cookies); + $return->cookies = $this; + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception.php b/srcs/wordpress/wp-includes/Requests/Exception.php new file mode 100644 index 0000000..37d4711 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception.php @@ -0,0 +1,62 @@ +<?php +/** + * Exception for HTTP requests + * + * @package Requests + */ + +/** + * Exception for HTTP requests + * + * @package Requests + */ +class Requests_Exception extends Exception { + /** + * Type of exception + * + * @var string + */ + protected $type; + + /** + * Data associated with the exception + * + * @var mixed + */ + protected $data; + + /** + * Create a new exception + * + * @param string $message Exception message + * @param string $type Exception type + * @param mixed $data Associated data + * @param integer $code Exception numerical code, if applicable + */ + public function __construct($message, $type, $data = null, $code = 0) { + parent::__construct($message, $code); + + $this->type = $type; + $this->data = $data; + } + + /** + * Like {@see getCode()}, but a string code. + * + * @codeCoverageIgnore + * @return string + */ + public function getType() { + return $this->type; + } + + /** + * Gives any relevant data + * + * @codeCoverageIgnore + * @return mixed + */ + public function getData() { + return $this->data; + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP.php new file mode 100644 index 0000000..9ac6a87 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP.php @@ -0,0 +1,71 @@ +<?php +/** + * Exception based on HTTP response + * + * @package Requests + */ + +/** + * Exception based on HTTP response + * + * @package Requests + */ +class Requests_Exception_HTTP extends Requests_Exception { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 0; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Unknown'; + + /** + * Create a new exception + * + * There is no mechanism to pass in the status code, as this is set by the + * subclass used. Reason phrases can vary, however. + * + * @param string|null $reason Reason phrase + * @param mixed $data Associated data + */ + public function __construct($reason = null, $data = null) { + if ($reason !== null) { + $this->reason = $reason; + } + + $message = sprintf('%d %s', $this->code, $this->reason); + parent::__construct($message, 'httpresponse', $data, $this->code); + } + + /** + * Get the status message + */ + public function getReason() { + return $this->reason; + } + + /** + * Get the correct exception class for a given error code + * + * @param int|bool $code HTTP status code, or false if unavailable + * @return string Exception class name to use + */ + public static function get_class($code) { + if (!$code) { + return 'Requests_Exception_HTTP_Unknown'; + } + + $class = sprintf('Requests_Exception_HTTP_%d', $code); + if (class_exists($class)) { + return $class; + } + + return 'Requests_Exception_HTTP_Unknown'; + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/304.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/304.php new file mode 100644 index 0000000..6799033 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/304.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 304 Not Modified responses + * + * @package Requests + */ + +/** + * Exception for 304 Not Modified responses + * + * @package Requests + */ +class Requests_Exception_HTTP_304 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 304; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Not Modified'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/305.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/305.php new file mode 100644 index 0000000..37d115a --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/305.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 305 Use Proxy responses + * + * @package Requests + */ + +/** + * Exception for 305 Use Proxy responses + * + * @package Requests + */ +class Requests_Exception_HTTP_305 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 305; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Use Proxy'; +} diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/306.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/306.php new file mode 100644 index 0000000..743a4ed --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/306.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 306 Switch Proxy responses + * + * @package Requests + */ + +/** + * Exception for 306 Switch Proxy responses + * + * @package Requests + */ +class Requests_Exception_HTTP_306 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 306; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Switch Proxy'; +} diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/400.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/400.php new file mode 100644 index 0000000..b3ad774 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/400.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 400 Bad Request responses + * + * @package Requests + */ + +/** + * Exception for 400 Bad Request responses + * + * @package Requests + */ +class Requests_Exception_HTTP_400 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 400; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Bad Request'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/401.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/401.php new file mode 100644 index 0000000..2e1e3d0 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/401.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 401 Unauthorized responses + * + * @package Requests + */ + +/** + * Exception for 401 Unauthorized responses + * + * @package Requests + */ +class Requests_Exception_HTTP_401 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 401; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Unauthorized'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/402.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/402.php new file mode 100644 index 0000000..1d965d2 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/402.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 402 Payment Required responses + * + * @package Requests + */ + +/** + * Exception for 402 Payment Required responses + * + * @package Requests + */ +class Requests_Exception_HTTP_402 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 402; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Payment Required'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/403.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/403.php new file mode 100644 index 0000000..5ca3caf --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/403.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 403 Forbidden responses + * + * @package Requests + */ + +/** + * Exception for 403 Forbidden responses + * + * @package Requests + */ +class Requests_Exception_HTTP_403 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 403; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Forbidden'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/404.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/404.php new file mode 100644 index 0000000..f08be2d --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/404.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 404 Not Found responses + * + * @package Requests + */ + +/** + * Exception for 404 Not Found responses + * + * @package Requests + */ +class Requests_Exception_HTTP_404 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 404; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Not Found'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/405.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/405.php new file mode 100644 index 0000000..b8419e1 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/405.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 405 Method Not Allowed responses + * + * @package Requests + */ + +/** + * Exception for 405 Method Not Allowed responses + * + * @package Requests + */ +class Requests_Exception_HTTP_405 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 405; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Method Not Allowed'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/406.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/406.php new file mode 100644 index 0000000..09d0622 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/406.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 406 Not Acceptable responses + * + * @package Requests + */ + +/** + * Exception for 406 Not Acceptable responses + * + * @package Requests + */ +class Requests_Exception_HTTP_406 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 406; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Not Acceptable'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/407.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/407.php new file mode 100644 index 0000000..c21d1ec --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/407.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 407 Proxy Authentication Required responses + * + * @package Requests + */ + +/** + * Exception for 407 Proxy Authentication Required responses + * + * @package Requests + */ +class Requests_Exception_HTTP_407 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 407; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Proxy Authentication Required'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/408.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/408.php new file mode 100644 index 0000000..0691863 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/408.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 408 Request Timeout responses + * + * @package Requests + */ + +/** + * Exception for 408 Request Timeout responses + * + * @package Requests + */ +class Requests_Exception_HTTP_408 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 408; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Request Timeout'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/409.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/409.php new file mode 100644 index 0000000..5a3e0d1 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/409.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 409 Conflict responses + * + * @package Requests + */ + +/** + * Exception for 409 Conflict responses + * + * @package Requests + */ +class Requests_Exception_HTTP_409 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 409; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Conflict'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/410.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/410.php new file mode 100644 index 0000000..8eb3daf --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/410.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 410 Gone responses + * + * @package Requests + */ + +/** + * Exception for 410 Gone responses + * + * @package Requests + */ +class Requests_Exception_HTTP_410 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 410; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Gone'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/411.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/411.php new file mode 100644 index 0000000..cee6496 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/411.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 411 Length Required responses + * + * @package Requests + */ + +/** + * Exception for 411 Length Required responses + * + * @package Requests + */ +class Requests_Exception_HTTP_411 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 411; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Length Required'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/412.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/412.php new file mode 100644 index 0000000..e377d79 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/412.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 412 Precondition Failed responses + * + * @package Requests + */ + +/** + * Exception for 412 Precondition Failed responses + * + * @package Requests + */ +class Requests_Exception_HTTP_412 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 412; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Precondition Failed'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/413.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/413.php new file mode 100644 index 0000000..3b5fe27 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/413.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 413 Request Entity Too Large responses + * + * @package Requests + */ + +/** + * Exception for 413 Request Entity Too Large responses + * + * @package Requests + */ +class Requests_Exception_HTTP_413 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 413; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Request Entity Too Large'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/414.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/414.php new file mode 100644 index 0000000..14d5a59 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/414.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 414 Request-URI Too Large responses + * + * @package Requests + */ + +/** + * Exception for 414 Request-URI Too Large responses + * + * @package Requests + */ +class Requests_Exception_HTTP_414 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 414; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Request-URI Too Large'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/415.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/415.php new file mode 100644 index 0000000..32446c8 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/415.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 415 Unsupported Media Type responses + * + * @package Requests + */ + +/** + * Exception for 415 Unsupported Media Type responses + * + * @package Requests + */ +class Requests_Exception_HTTP_415 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 415; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Unsupported Media Type'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/416.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/416.php new file mode 100644 index 0000000..8c5f833 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/416.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 416 Requested Range Not Satisfiable responses + * + * @package Requests + */ + +/** + * Exception for 416 Requested Range Not Satisfiable responses + * + * @package Requests + */ +class Requests_Exception_HTTP_416 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 416; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Requested Range Not Satisfiable'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/417.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/417.php new file mode 100644 index 0000000..8807c71 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/417.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 417 Expectation Failed responses + * + * @package Requests + */ + +/** + * Exception for 417 Expectation Failed responses + * + * @package Requests + */ +class Requests_Exception_HTTP_417 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 417; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Expectation Failed'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/418.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/418.php new file mode 100644 index 0000000..d6af806 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/418.php @@ -0,0 +1,29 @@ +<?php +/** + * Exception for 418 I'm A Teapot responses + * + * @see https://tools.ietf.org/html/rfc2324 + * @package Requests + */ + +/** + * Exception for 418 I'm A Teapot responses + * + * @see https://tools.ietf.org/html/rfc2324 + * @package Requests + */ +class Requests_Exception_HTTP_418 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 418; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = "I'm A Teapot"; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/428.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/428.php new file mode 100644 index 0000000..469e954 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/428.php @@ -0,0 +1,29 @@ +<?php +/** + * Exception for 428 Precondition Required responses + * + * @see https://tools.ietf.org/html/rfc6585 + * @package Requests + */ + +/** + * Exception for 428 Precondition Required responses + * + * @see https://tools.ietf.org/html/rfc6585 + * @package Requests + */ +class Requests_Exception_HTTP_428 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 428; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Precondition Required'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/429.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/429.php new file mode 100644 index 0000000..2a21fb3 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/429.php @@ -0,0 +1,29 @@ +<?php +/** + * Exception for 429 Too Many Requests responses + * + * @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04 + * @package Requests + */ + +/** + * Exception for 429 Too Many Requests responses + * + * @see https://tools.ietf.org/html/draft-nottingham-http-new-status-04 + * @package Requests + */ +class Requests_Exception_HTTP_429 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 429; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Too Many Requests'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/431.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/431.php new file mode 100644 index 0000000..ba1294e --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/431.php @@ -0,0 +1,29 @@ +<?php +/** + * Exception for 431 Request Header Fields Too Large responses + * + * @see https://tools.ietf.org/html/rfc6585 + * @package Requests + */ + +/** + * Exception for 431 Request Header Fields Too Large responses + * + * @see https://tools.ietf.org/html/rfc6585 + * @package Requests + */ +class Requests_Exception_HTTP_431 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 431; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Request Header Fields Too Large'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/500.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/500.php new file mode 100644 index 0000000..5165e35 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/500.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 500 Internal Server Error responses + * + * @package Requests + */ + +/** + * Exception for 500 Internal Server Error responses + * + * @package Requests + */ +class Requests_Exception_HTTP_500 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 500; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Internal Server Error'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/501.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/501.php new file mode 100644 index 0000000..3dd6946 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/501.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 501 Not Implemented responses + * + * @package Requests + */ + +/** + * Exception for 501 Not Implemented responses + * + * @package Requests + */ +class Requests_Exception_HTTP_501 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 501; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Not Implemented'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/502.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/502.php new file mode 100644 index 0000000..50c72d4 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/502.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 502 Bad Gateway responses + * + * @package Requests + */ + +/** + * Exception for 502 Bad Gateway responses + * + * @package Requests + */ +class Requests_Exception_HTTP_502 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 502; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Bad Gateway'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/503.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/503.php new file mode 100644 index 0000000..9304ecc --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/503.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 503 Service Unavailable responses + * + * @package Requests + */ + +/** + * Exception for 503 Service Unavailable responses + * + * @package Requests + */ +class Requests_Exception_HTTP_503 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 503; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Service Unavailable'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/504.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/504.php new file mode 100644 index 0000000..f46696b --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/504.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 504 Gateway Timeout responses + * + * @package Requests + */ + +/** + * Exception for 504 Gateway Timeout responses + * + * @package Requests + */ +class Requests_Exception_HTTP_504 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 504; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Gateway Timeout'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/505.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/505.php new file mode 100644 index 0000000..1f80294 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/505.php @@ -0,0 +1,27 @@ +<?php +/** + * Exception for 505 HTTP Version Not Supported responses + * + * @package Requests + */ + +/** + * Exception for 505 HTTP Version Not Supported responses + * + * @package Requests + */ +class Requests_Exception_HTTP_505 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 505; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'HTTP Version Not Supported'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/511.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/511.php new file mode 100644 index 0000000..920d334 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/511.php @@ -0,0 +1,29 @@ +<?php +/** + * Exception for 511 Network Authentication Required responses + * + * @see https://tools.ietf.org/html/rfc6585 + * @package Requests + */ + +/** + * Exception for 511 Network Authentication Required responses + * + * @see https://tools.ietf.org/html/rfc6585 + * @package Requests + */ +class Requests_Exception_HTTP_511 extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer + */ + protected $code = 511; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Network Authentication Required'; +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/HTTP/Unknown.php b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/Unknown.php new file mode 100644 index 0000000..c70f589 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/HTTP/Unknown.php @@ -0,0 +1,44 @@ +<?php +/** + * Exception for unknown status responses + * + * @package Requests + */ + +/** + * Exception for unknown status responses + * + * @package Requests + */ +class Requests_Exception_HTTP_Unknown extends Requests_Exception_HTTP { + /** + * HTTP status code + * + * @var integer|bool Code if available, false if an error occurred + */ + protected $code = 0; + + /** + * Reason phrase + * + * @var string + */ + protected $reason = 'Unknown'; + + /** + * Create a new exception + * + * If `$data` is an instance of {@see Requests_Response}, uses the status + * code from it. Otherwise, sets as 0 + * + * @param string|null $reason Reason phrase + * @param mixed $data Associated data + */ + public function __construct($reason = null, $data = null) { + if ($data instanceof Requests_Response) { + $this->code = $data->status_code; + } + + parent::__construct($reason, $data); + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Exception/Transport.php b/srcs/wordpress/wp-includes/Requests/Exception/Transport.php new file mode 100644 index 0000000..e60b488 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/Transport.php @@ -0,0 +1,5 @@ +<?php + +class Requests_Exception_Transport extends Requests_Exception { + +} diff --git a/srcs/wordpress/wp-includes/Requests/Exception/Transport/cURL.php b/srcs/wordpress/wp-includes/Requests/Exception/Transport/cURL.php new file mode 100644 index 0000000..67d157b --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Exception/Transport/cURL.php @@ -0,0 +1,56 @@ +<?php + +class Requests_Exception_Transport_cURL extends Requests_Exception_Transport { + + const EASY = 'cURLEasy'; + const MULTI = 'cURLMulti'; + const SHARE = 'cURLShare'; + + /** + * cURL error code + * + * @var integer + */ + protected $code = -1; + + /** + * Which type of cURL error + * + * EASY|MULTI|SHARE + * + * @var string + */ + protected $type = 'Unknown'; + + /** + * Clear text error message + * + * @var string + */ + protected $reason = 'Unknown'; + + public function __construct($message, $type, $data = null, $code = 0) { + if ($type !== null) { + $this->type = $type; + } + + if ($code !== null) { + $this->code = $code; + } + + if ($message !== null) { + $this->reason = $message; + } + + $message = sprintf('%d %s', $this->code, $this->reason); + parent::__construct($message, $this->type, $data, $this->code); + } + + /** + * Get the error message + */ + public function getReason() { + return $this->reason; + } + +} diff --git a/srcs/wordpress/wp-includes/Requests/Hooker.php b/srcs/wordpress/wp-includes/Requests/Hooker.php new file mode 100644 index 0000000..f667ae9 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Hooker.php @@ -0,0 +1,33 @@ +<?php +/** + * Event dispatcher + * + * @package Requests + * @subpackage Utilities + */ + +/** + * Event dispatcher + * + * @package Requests + * @subpackage Utilities + */ +interface Requests_Hooker { + /** + * Register a callback for a hook + * + * @param string $hook Hook name + * @param callback $callback Function/method to call on event + * @param int $priority Priority number. <0 is executed earlier, >0 is executed later + */ + public function register($hook, $callback, $priority = 0); + + /** + * Dispatch a message + * + * @param string $hook Hook name + * @param array $parameters Parameters to pass to callbacks + * @return boolean Successfulness + */ + public function dispatch($hook, $parameters = array()); +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Hooks.php b/srcs/wordpress/wp-includes/Requests/Hooks.php new file mode 100644 index 0000000..2e61c73 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Hooks.php @@ -0,0 +1,68 @@ +<?php +/** + * Handles adding and dispatching events + * + * @package Requests + * @subpackage Utilities + */ + +/** + * Handles adding and dispatching events + * + * @package Requests + * @subpackage Utilities + */ +class Requests_Hooks implements Requests_Hooker { + /** + * Registered callbacks for each hook + * + * @var array + */ + protected $hooks = array(); + + /** + * Constructor + */ + public function __construct() { + // pass + } + + /** + * Register a callback for a hook + * + * @param string $hook Hook name + * @param callback $callback Function/method to call on event + * @param int $priority Priority number. <0 is executed earlier, >0 is executed later + */ + public function register($hook, $callback, $priority = 0) { + if (!isset($this->hooks[$hook])) { + $this->hooks[$hook] = array(); + } + if (!isset($this->hooks[$hook][$priority])) { + $this->hooks[$hook][$priority] = array(); + } + + $this->hooks[$hook][$priority][] = $callback; + } + + /** + * Dispatch a message + * + * @param string $hook Hook name + * @param array $parameters Parameters to pass to callbacks + * @return boolean Successfulness + */ + public function dispatch($hook, $parameters = array()) { + if (empty($this->hooks[$hook])) { + return false; + } + + foreach ($this->hooks[$hook] as $priority => $hooked) { + foreach ($hooked as $callback) { + call_user_func_array($callback, $parameters); + } + } + + return true; + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/IDNAEncoder.php b/srcs/wordpress/wp-includes/Requests/IDNAEncoder.php new file mode 100644 index 0000000..ebbe211 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/IDNAEncoder.php @@ -0,0 +1,388 @@ +<?php + +/** + * IDNA URL encoder + * + * Note: Not fully compliant, as nameprep does nothing yet. + * + * @package Requests + * @subpackage Utilities + * @see https://tools.ietf.org/html/rfc3490 IDNA specification + * @see https://tools.ietf.org/html/rfc3492 Punycode/Bootstrap specification + */ +class Requests_IDNAEncoder { + /** + * ACE prefix used for IDNA + * + * @see https://tools.ietf.org/html/rfc3490#section-5 + * @var string + */ + const ACE_PREFIX = 'xn--'; + + /**#@+ + * Bootstrap constant for Punycode + * + * @see https://tools.ietf.org/html/rfc3492#section-5 + * @var int + */ + const BOOTSTRAP_BASE = 36; + const BOOTSTRAP_TMIN = 1; + const BOOTSTRAP_TMAX = 26; + const BOOTSTRAP_SKEW = 38; + const BOOTSTRAP_DAMP = 700; + const BOOTSTRAP_INITIAL_BIAS = 72; + const BOOTSTRAP_INITIAL_N = 128; + /**#@-*/ + + /** + * Encode a hostname using Punycode + * + * @param string $string Hostname + * @return string Punycode-encoded hostname + */ + public static function encode($string) { + $parts = explode('.', $string); + foreach ($parts as &$part) { + $part = self::to_ascii($part); + } + return implode('.', $parts); + } + + /** + * Convert a UTF-8 string to an ASCII string using Punycode + * + * @throws Requests_Exception Provided string longer than 64 ASCII characters (`idna.provided_too_long`) + * @throws Requests_Exception Prepared string longer than 64 ASCII characters (`idna.prepared_too_long`) + * @throws Requests_Exception Provided string already begins with xn-- (`idna.provided_is_prefixed`) + * @throws Requests_Exception Encoded string longer than 64 ASCII characters (`idna.encoded_too_long`) + * + * @param string $string ASCII or UTF-8 string (max length 64 characters) + * @return string ASCII string + */ + public static function to_ascii($string) { + // Step 1: Check if the string is already ASCII + if (self::is_ascii($string)) { + // Skip to step 7 + if (strlen($string) < 64) { + return $string; + } + + throw new Requests_Exception('Provided string is too long', 'idna.provided_too_long', $string); + } + + // Step 2: nameprep + $string = self::nameprep($string); + + // Step 3: UseSTD3ASCIIRules is false, continue + // Step 4: Check if it's ASCII now + if (self::is_ascii($string)) { + // Skip to step 7 + if (strlen($string) < 64) { + return $string; + } + + throw new Requests_Exception('Prepared string is too long', 'idna.prepared_too_long', $string); + } + + // Step 5: Check ACE prefix + if (strpos($string, self::ACE_PREFIX) === 0) { + throw new Requests_Exception('Provided string begins with ACE prefix', 'idna.provided_is_prefixed', $string); + } + + // Step 6: Encode with Punycode + $string = self::punycode_encode($string); + + // Step 7: Prepend ACE prefix + $string = self::ACE_PREFIX . $string; + + // Step 8: Check size + if (strlen($string) < 64) { + return $string; + } + + throw new Requests_Exception('Encoded string is too long', 'idna.encoded_too_long', $string); + } + + /** + * Check whether a given string contains only ASCII characters + * + * @internal (Testing found regex was the fastest implementation) + * + * @param string $string + * @return bool Is the string ASCII-only? + */ + protected static function is_ascii($string) { + return (preg_match('/(?:[^\x00-\x7F])/', $string) !== 1); + } + + /** + * Prepare a string for use as an IDNA name + * + * @todo Implement this based on RFC 3491 and the newer 5891 + * @param string $string + * @return string Prepared string + */ + protected static function nameprep($string) { + return $string; + } + + /** + * Convert a UTF-8 string to a UCS-4 codepoint array + * + * Based on Requests_IRI::replace_invalid_with_pct_encoding() + * + * @throws Requests_Exception Invalid UTF-8 codepoint (`idna.invalidcodepoint`) + * @param string $input + * @return array Unicode code points + */ + protected static function utf8_to_codepoints($input) { + $codepoints = array(); + + // Get number of bytes + $strlen = strlen($input); + + for ($position = 0; $position < $strlen; $position++) { + $value = ord($input[$position]); + + // One byte sequence: + if ((~$value & 0x80) === 0x80) { + $character = $value; + $length = 1; + $remaining = 0; + } + // Two byte sequence: + elseif (($value & 0xE0) === 0xC0) { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else { + throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $value); + } + + if ($remaining > 0) { + if ($position + $length > $strlen) { + throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); + } + for ($position++; $remaining > 0; $position++) { + $value = ord($input[$position]); + + // If it is invalid, count the sequence as invalid and reprocess the current byte: + if (($value & 0xC0) !== 0x80) { + throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); + } + + $character |= ($value & 0x3F) << (--$remaining * 6); + } + $position--; + } + + if ( + // Non-shortest form sequences are invalid + $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of ucschar codepoints + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + || ( + // Everything else not in ucschar + $character > 0xD7FF && $character < 0xF900 + || $character < 0x20 + || $character > 0x7E && $character < 0xA0 + || $character > 0xEFFFD + ) + ) { + throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); + } + + $codepoints[] = $character; + } + + return $codepoints; + } + + /** + * RFC3492-compliant encoder + * + * @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code + * @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`) + * + * @param string $input UTF-8 encoded string to encode + * @return string Punycode-encoded string + */ + public static function punycode_encode($input) { + $output = ''; +# let n = initial_n + $n = self::BOOTSTRAP_INITIAL_N; +# let delta = 0 + $delta = 0; +# let bias = initial_bias + $bias = self::BOOTSTRAP_INITIAL_BIAS; +# let h = b = the number of basic code points in the input + $h = $b = 0; // see loop +# copy them to the output in order + $codepoints = self::utf8_to_codepoints($input); + $extended = array(); + + foreach ($codepoints as $char) { + if ($char < 128) { + // Character is valid ASCII + // TODO: this should also check if it's valid for a URL + $output .= chr($char); + $h++; + } + // Check if the character is non-ASCII, but below initial n + // This never occurs for Punycode, so ignore in coverage + // @codeCoverageIgnoreStart + elseif ($char < $n) { + throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char); + } + // @codeCoverageIgnoreEnd + else { + $extended[$char] = true; + } + } + $extended = array_keys($extended); + sort($extended); + $b = $h; +# [copy them] followed by a delimiter if b > 0 + if (strlen($output) > 0) { + $output .= '-'; + } +# {if the input contains a non-basic code point < n then fail} +# while h < length(input) do begin + while ($h < count($codepoints)) { +# let m = the minimum code point >= n in the input + $m = array_shift($extended); + //printf('next code point to insert is %s' . PHP_EOL, dechex($m)); +# let delta = delta + (m - n) * (h + 1), fail on overflow + $delta += ($m - $n) * ($h + 1); +# let n = m + $n = $m; +# for each code point c in the input (in order) do begin + for ($num = 0; $num < count($codepoints); $num++) { + $c = $codepoints[$num]; +# if c < n then increment delta, fail on overflow + if ($c < $n) { + $delta++; + } +# if c == n then begin + elseif ($c === $n) { +# let q = delta + $q = $delta; +# for k = base to infinity in steps of base do begin + for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) { +# let t = tmin if k <= bias {+ tmin}, or +# tmax if k >= bias + tmax, or k - bias otherwise + if ($k <= ($bias + self::BOOTSTRAP_TMIN)) { + $t = self::BOOTSTRAP_TMIN; + } + elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) { + $t = self::BOOTSTRAP_TMAX; + } + else { + $t = $k - $bias; + } +# if q < t then break + if ($q < $t) { + break; + } +# output the code point for digit t + ((q - t) mod (base - t)) + $digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)); + $output .= self::digit_to_char($digit); +# let q = (q - t) div (base - t) + $q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t)); +# end + } +# output the code point for digit q + $output .= self::digit_to_char($q); +# let bias = adapt(delta, h + 1, test h equals b?) + $bias = self::adapt($delta, $h + 1, $h === $b); +# let delta = 0 + $delta = 0; +# increment h + $h++; +# end + } +# end + } +# increment delta and n + $delta++; + $n++; +# end + } + + return $output; + } + + /** + * Convert a digit to its respective character + * + * @see https://tools.ietf.org/html/rfc3492#section-5 + * @throws Requests_Exception On invalid digit (`idna.invalid_digit`) + * + * @param int $digit Digit in the range 0-35 + * @return string Single character corresponding to digit + */ + protected static function digit_to_char($digit) { + // @codeCoverageIgnoreStart + // As far as I know, this never happens, but still good to be sure. + if ($digit < 0 || $digit > 35) { + throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit); + } + // @codeCoverageIgnoreEnd + $digits = 'abcdefghijklmnopqrstuvwxyz0123456789'; + return substr($digits, $digit, 1); + } + + /** + * Adapt the bias + * + * @see https://tools.ietf.org/html/rfc3492#section-6.1 + * @param int $delta + * @param int $numpoints + * @param bool $firsttime + * @return int New bias + */ + protected static function adapt($delta, $numpoints, $firsttime) { +# function adapt(delta,numpoints,firsttime): +# if firsttime then let delta = delta div damp + if ($firsttime) { + $delta = floor($delta / self::BOOTSTRAP_DAMP); + } +# else let delta = delta div 2 + else { + $delta = floor($delta / 2); + } +# let delta = delta + (delta div numpoints) + $delta += floor($delta / $numpoints); +# let k = 0 + $k = 0; +# while delta > ((base - tmin) * tmax) div 2 do begin + $max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2); + while ($delta > $max) { +# let delta = delta div (base - tmin) + $delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN)); +# let k = k + base + $k += self::BOOTSTRAP_BASE; +# end + } +# return k + (((base - tmin + 1) * delta) div (delta + skew)) + return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW)); + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/IPv6.php b/srcs/wordpress/wp-includes/Requests/IPv6.php new file mode 100644 index 0000000..204dbd7 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/IPv6.php @@ -0,0 +1,190 @@ +<?php +/** + * Class to validate and to work with IPv6 addresses + * + * @package Requests + * @subpackage Utilities + */ + +/** + * Class to validate and to work with IPv6 addresses + * + * This was originally based on the PEAR class of the same name, but has been + * entirely rewritten. + * + * @package Requests + * @subpackage Utilities + */ +class Requests_IPv6 { + /** + * Uncompresses an IPv6 address + * + * RFC 4291 allows you to compress consecutive zero pieces in an address to + * '::'. This method expects a valid IPv6 address and expands the '::' to + * the required number of zero pieces. + * + * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 + * ::1 -> 0:0:0:0:0:0:0:1 + * + * @author Alexander Merz <alexander.merz@web.de> + * @author elfrink at introweb dot nl + * @author Josh Peck <jmp at joshpeck dot org> + * @copyright 2003-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php + * @param string $ip An IPv6 address + * @return string The uncompressed IPv6 address + */ + public static function uncompress($ip) { + if (substr_count($ip, '::') !== 1) { + return $ip; + } + + list($ip1, $ip2) = explode('::', $ip); + $c1 = ($ip1 === '') ? -1 : substr_count($ip1, ':'); + $c2 = ($ip2 === '') ? -1 : substr_count($ip2, ':'); + + if (strpos($ip2, '.') !== false) { + $c2++; + } + // :: + if ($c1 === -1 && $c2 === -1) { + $ip = '0:0:0:0:0:0:0:0'; + } + // ::xxx + else if ($c1 === -1) { + $fill = str_repeat('0:', 7 - $c2); + $ip = str_replace('::', $fill, $ip); + } + // xxx:: + else if ($c2 === -1) { + $fill = str_repeat(':0', 7 - $c1); + $ip = str_replace('::', $fill, $ip); + } + // xxx::xxx + else { + $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); + $ip = str_replace('::', $fill, $ip); + } + return $ip; + } + + /** + * Compresses an IPv6 address + * + * RFC 4291 allows you to compress consecutive zero pieces in an address to + * '::'. This method expects a valid IPv6 address and compresses consecutive + * zero pieces to '::'. + * + * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 + * 0:0:0:0:0:0:0:1 -> ::1 + * + * @see uncompress() + * @param string $ip An IPv6 address + * @return string The compressed IPv6 address + */ + public static function compress($ip) { + // Prepare the IP to be compressed + $ip = self::uncompress($ip); + $ip_parts = self::split_v6_v4($ip); + + // Replace all leading zeros + $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); + + // Find bunches of zeros + if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) { + $max = 0; + $pos = null; + foreach ($matches[0] as $match) { + if (strlen($match[0]) > $max) { + $max = strlen($match[0]); + $pos = $match[1]; + } + } + + $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); + } + + if ($ip_parts[1] !== '') { + return implode(':', $ip_parts); + } + else { + return $ip_parts[0]; + } + } + + /** + * Splits an IPv6 address into the IPv6 and IPv4 representation parts + * + * RFC 4291 allows you to represent the last two parts of an IPv6 address + * using the standard IPv4 representation + * + * Example: 0:0:0:0:0:0:13.1.68.3 + * 0:0:0:0:0:FFFF:129.144.52.38 + * + * @param string $ip An IPv6 address + * @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part + */ + protected static function split_v6_v4($ip) { + if (strpos($ip, '.') !== false) { + $pos = strrpos($ip, ':'); + $ipv6_part = substr($ip, 0, $pos); + $ipv4_part = substr($ip, $pos + 1); + return array($ipv6_part, $ipv4_part); + } + else { + return array($ip, ''); + } + } + + /** + * Checks an IPv6 address + * + * Checks if the given IP is a valid IPv6 address + * + * @param string $ip An IPv6 address + * @return bool true if $ip is a valid IPv6 address + */ + public static function check_ipv6($ip) { + $ip = self::uncompress($ip); + list($ipv6, $ipv4) = self::split_v6_v4($ip); + $ipv6 = explode(':', $ipv6); + $ipv4 = explode('.', $ipv4); + if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) { + foreach ($ipv6 as $ipv6_part) { + // The section can't be empty + if ($ipv6_part === '') { + return false; + } + + // Nor can it be over four characters + if (strlen($ipv6_part) > 4) { + return false; + } + + // Remove leading zeros (this is safe because of the above) + $ipv6_part = ltrim($ipv6_part, '0'); + if ($ipv6_part === '') { + $ipv6_part = '0'; + } + + // Check the value is valid + $value = hexdec($ipv6_part); + if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) { + return false; + } + } + if (count($ipv4) === 4) { + foreach ($ipv4 as $ipv4_part) { + $value = (int) $ipv4_part; + if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) { + return false; + } + } + } + return true; + } + else { + return false; + } + } +} diff --git a/srcs/wordpress/wp-includes/Requests/IRI.php b/srcs/wordpress/wp-includes/Requests/IRI.php new file mode 100644 index 0000000..8dc2fa2 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/IRI.php @@ -0,0 +1,1084 @@ +<?php +/** + * IRI parser/serialiser/normaliser + * + * @package Requests + * @subpackage Utilities + */ + +/** + * IRI parser/serialiser/normaliser + * + * Copyright (c) 2007-2010, Geoffrey Sneddon and Steve Minutillo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package Requests + * @subpackage Utilities + * @author Geoffrey Sneddon + * @author Steve Minutillo + * @copyright 2007-2009 Geoffrey Sneddon and Steve Minutillo + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://hg.gsnedders.com/iri/ + * + * @property string $iri IRI we're working with + * @property-read string $uri IRI in URI form, {@see to_uri} + * @property string $scheme Scheme part of the IRI + * @property string $authority Authority part, formatted for a URI (userinfo + host + port) + * @property string $iauthority Authority part of the IRI (userinfo + host + port) + * @property string $userinfo Userinfo part, formatted for a URI (after '://' and before '@') + * @property string $iuserinfo Userinfo part of the IRI (after '://' and before '@') + * @property string $host Host part, formatted for a URI + * @property string $ihost Host part of the IRI + * @property string $port Port part of the IRI (after ':') + * @property string $path Path part, formatted for a URI (after first '/') + * @property string $ipath Path part of the IRI (after first '/') + * @property string $query Query part, formatted for a URI (after '?') + * @property string $iquery Query part of the IRI (after '?') + * @property string $fragment Fragment, formatted for a URI (after '#') + * @property string $ifragment Fragment part of the IRI (after '#') + */ +class Requests_IRI { + /** + * Scheme + * + * @var string + */ + protected $scheme = null; + + /** + * User Information + * + * @var string + */ + protected $iuserinfo = null; + + /** + * ihost + * + * @var string + */ + protected $ihost = null; + + /** + * Port + * + * @var string + */ + protected $port = null; + + /** + * ipath + * + * @var string + */ + protected $ipath = ''; + + /** + * iquery + * + * @var string + */ + protected $iquery = null; + + /** + * ifragment + * + * @var string + */ + protected $ifragment = null; + + /** + * Normalization database + * + * Each key is the scheme, each value is an array with each key as the IRI + * part and value as the default value for that part. + */ + protected $normalization = array( + 'acap' => array( + 'port' => 674 + ), + 'dict' => array( + 'port' => 2628 + ), + 'file' => array( + 'ihost' => 'localhost' + ), + 'http' => array( + 'port' => 80, + ), + 'https' => array( + 'port' => 443, + ), + ); + + /** + * Return the entire IRI when you try and read the object as a string + * + * @return string + */ + public function __toString() { + return $this->get_iri(); + } + + /** + * Overload __set() to provide access via properties + * + * @param string $name Property name + * @param mixed $value Property value + */ + public function __set($name, $value) { + if (method_exists($this, 'set_' . $name)) { + call_user_func(array($this, 'set_' . $name), $value); + } + elseif ( + $name === 'iauthority' + || $name === 'iuserinfo' + || $name === 'ihost' + || $name === 'ipath' + || $name === 'iquery' + || $name === 'ifragment' + ) { + call_user_func(array($this, 'set_' . substr($name, 1)), $value); + } + } + + /** + * Overload __get() to provide access via properties + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) { + // isset() returns false for null, we don't want to do that + // Also why we use array_key_exists below instead of isset() + $props = get_object_vars($this); + + if ( + $name === 'iri' || + $name === 'uri' || + $name === 'iauthority' || + $name === 'authority' + ) { + $method = 'get_' . $name; + $return = $this->$method(); + } + elseif (array_key_exists($name, $props)) { + $return = $this->$name; + } + // host -> ihost + elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) { + $name = $prop; + $return = $this->$prop; + } + // ischeme -> scheme + elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) { + $name = $prop; + $return = $this->$prop; + } + else { + trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); + $return = null; + } + + if ($return === null && isset($this->normalization[$this->scheme][$name])) { + return $this->normalization[$this->scheme][$name]; + } + else { + return $return; + } + } + + /** + * Overload __isset() to provide access via properties + * + * @param string $name Property name + * @return bool + */ + public function __isset($name) { + return (method_exists($this, 'get_' . $name) || isset($this->$name)); + } + + /** + * Overload __unset() to provide access via properties + * + * @param string $name Property name + */ + public function __unset($name) { + if (method_exists($this, 'set_' . $name)) { + call_user_func(array($this, 'set_' . $name), ''); + } + } + + /** + * Create a new IRI object, from a specified string + * + * @param string|null $iri + */ + public function __construct($iri = null) { + $this->set_iri($iri); + } + + /** + * Create a new IRI object by resolving a relative IRI + * + * Returns false if $base is not absolute, otherwise an IRI. + * + * @param IRI|string $base (Absolute) Base IRI + * @param IRI|string $relative Relative IRI + * @return IRI|false + */ + public static function absolutize($base, $relative) { + if (!($relative instanceof Requests_IRI)) { + $relative = new Requests_IRI($relative); + } + if (!$relative->is_valid()) { + return false; + } + elseif ($relative->scheme !== null) { + return clone $relative; + } + + if (!($base instanceof Requests_IRI)) { + $base = new Requests_IRI($base); + } + if ($base->scheme === null || !$base->is_valid()) { + return false; + } + + if ($relative->get_iri() !== '') { + if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) { + $target = clone $relative; + $target->scheme = $base->scheme; + } + else { + $target = new Requests_IRI; + $target->scheme = $base->scheme; + $target->iuserinfo = $base->iuserinfo; + $target->ihost = $base->ihost; + $target->port = $base->port; + if ($relative->ipath !== '') { + if ($relative->ipath[0] === '/') { + $target->ipath = $relative->ipath; + } + elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') { + $target->ipath = '/' . $relative->ipath; + } + elseif (($last_segment = strrpos($base->ipath, '/')) !== false) { + $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; + } + else { + $target->ipath = $relative->ipath; + } + $target->ipath = $target->remove_dot_segments($target->ipath); + $target->iquery = $relative->iquery; + } + else { + $target->ipath = $base->ipath; + if ($relative->iquery !== null) { + $target->iquery = $relative->iquery; + } + elseif ($base->iquery !== null) { + $target->iquery = $base->iquery; + } + } + $target->ifragment = $relative->ifragment; + } + } + else { + $target = clone $base; + $target->ifragment = null; + } + $target->scheme_normalization(); + return $target; + } + + /** + * Parse an IRI into scheme/authority/path/query/fragment segments + * + * @param string $iri + * @return array + */ + protected function parse_iri($iri) { + $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); + $has_match = preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match); + if (!$has_match) { + throw new Requests_Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri); + } + + if ($match[1] === '') { + $match['scheme'] = null; + } + if (!isset($match[3]) || $match[3] === '') { + $match['authority'] = null; + } + if (!isset($match[5])) { + $match['path'] = ''; + } + if (!isset($match[6]) || $match[6] === '') { + $match['query'] = null; + } + if (!isset($match[8]) || $match[8] === '') { + $match['fragment'] = null; + } + return $match; + } + + /** + * Remove dot segments from a path + * + * @param string $input + * @return string + */ + protected function remove_dot_segments($input) { + $output = ''; + while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') { + // A: If the input buffer begins with a prefix of "../" or "./", + // then remove that prefix from the input buffer; otherwise, + if (strpos($input, '../') === 0) { + $input = substr($input, 3); + } + elseif (strpos($input, './') === 0) { + $input = substr($input, 2); + } + // B: if the input buffer begins with a prefix of "/./" or "/.", + // where "." is a complete path segment, then replace that prefix + // with "/" in the input buffer; otherwise, + elseif (strpos($input, '/./') === 0) { + $input = substr($input, 2); + } + elseif ($input === '/.') { + $input = '/'; + } + // C: if the input buffer begins with a prefix of "/../" or "/..", + // where ".." is a complete path segment, then replace that prefix + // with "/" in the input buffer and remove the last segment and its + // preceding "/" (if any) from the output buffer; otherwise, + elseif (strpos($input, '/../') === 0) { + $input = substr($input, 3); + $output = substr_replace($output, '', strrpos($output, '/')); + } + elseif ($input === '/..') { + $input = '/'; + $output = substr_replace($output, '', strrpos($output, '/')); + } + // D: if the input buffer consists only of "." or "..", then remove + // that from the input buffer; otherwise, + elseif ($input === '.' || $input === '..') { + $input = ''; + } + // E: move the first path segment in the input buffer to the end of + // the output buffer, including the initial "/" character (if any) + // and any subsequent characters up to, but not including, the next + // "/" character or the end of the input buffer + elseif (($pos = strpos($input, '/', 1)) !== false) { + $output .= substr($input, 0, $pos); + $input = substr_replace($input, '', 0, $pos); + } + else { + $output .= $input; + $input = ''; + } + } + return $output . $input; + } + + /** + * Replace invalid character with percent encoding + * + * @param string $string Input string + * @param string $extra_chars Valid characters not in iunreserved or + * iprivate (this is ASCII-only) + * @param bool $iprivate Allow iprivate + * @return string + */ + protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) { + // Normalize as many pct-encoded sections as possible + $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string); + + // Replace invalid percent characters + $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); + + // Add unreserved and % to $extra_chars (the latter is safe because all + // pct-encoded sections are now valid). + $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; + + // Now replace any bytes that aren't allowed with their pct-encoded versions + $position = 0; + $strlen = strlen($string); + while (($position += strspn($string, $extra_chars, $position)) < $strlen) { + $value = ord($string[$position]); + + // Start position + $start = $position; + + // By default we are valid + $valid = true; + + // No one byte sequences are valid due to the while. + // Two byte sequence: + if (($value & 0xE0) === 0xC0) { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else { + $valid = false; + $length = 1; + $remaining = 0; + } + + if ($remaining) { + if ($position + $length <= $strlen) { + for ($position++; $remaining; $position++) { + $value = ord($string[$position]); + + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) { + $character |= ($value & 0x3F) << (--$remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte: + else { + $valid = false; + $position--; + break; + } + } + } + else { + $position = $strlen - 1; + $valid = false; + } + } + + // Percent encode anything invalid or not in ucschar + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of ucschar codepoints + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + || ( + // Everything else not in ucschar + $character > 0xD7FF && $character < 0xF900 + || $character < 0xA0 + || $character > 0xEFFFD + ) + && ( + // Everything not in iprivate, if it applies + !$iprivate + || $character < 0xE000 + || $character > 0x10FFFD + ) + ) { + // If we were a character, pretend we weren't, but rather an error. + if ($valid) { + $position--; + } + + for ($j = $start; $j <= $position; $j++) { + $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); + $j += 2; + $position += 2; + $strlen += 2; + } + } + } + + return $string; + } + + /** + * Callback function for preg_replace_callback. + * + * Removes sequences of percent encoded bytes that represent UTF-8 + * encoded characters in iunreserved + * + * @param array $match PCRE match + * @return string Replacement + */ + protected function remove_iunreserved_percent_encoded($match) { + // As we just have valid percent encoded sequences we can just explode + // and ignore the first member of the returned array (an empty string). + $bytes = explode('%', $match[0]); + + // Initialize the new string (this is what will be returned) and that + // there are no bytes remaining in the current sequence (unsurprising + // at the first byte!). + $string = ''; + $remaining = 0; + + // Loop over each and every byte, and set $value to its value + for ($i = 1, $len = count($bytes); $i < $len; $i++) { + $value = hexdec($bytes[$i]); + + // If we're the first byte of sequence: + if (!$remaining) { + // Start position + $start = $i; + + // By default we are valid + $valid = true; + + // One byte sequence: + if ($value <= 0x7F) { + $character = $value; + $length = 1; + } + // Two byte sequence: + elseif (($value & 0xE0) === 0xC0) { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else { + $valid = false; + $remaining = 0; + } + } + // Continuation byte: + else { + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) { + $remaining--; + $character |= ($value & 0x3F) << ($remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: + else { + $valid = false; + $remaining = 0; + $i--; + } + } + + // If we've reached the end of the current byte sequence, append it to Unicode::$data + if (!$remaining) { + // Percent encode anything invalid or not in iunreserved + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of iunreserved codepoints + || $character < 0x2D + || $character > 0xEFFFD + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + // Everything else not in iunreserved (this is all BMP) + || $character === 0x2F + || $character > 0x39 && $character < 0x41 + || $character > 0x5A && $character < 0x61 + || $character > 0x7A && $character < 0x7E + || $character > 0x7E && $character < 0xA0 + || $character > 0xD7FF && $character < 0xF900 + ) { + for ($j = $start; $j <= $i; $j++) { + $string .= '%' . strtoupper($bytes[$j]); + } + } + else { + for ($j = $start; $j <= $i; $j++) { + $string .= chr(hexdec($bytes[$j])); + } + } + } + } + + // If we have any bytes left over they are invalid (i.e., we are + // mid-way through a multi-byte sequence) + if ($remaining) { + for ($j = $start; $j < $len; $j++) { + $string .= '%' . strtoupper($bytes[$j]); + } + } + + return $string; + } + + protected function scheme_normalization() { + if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) { + $this->iuserinfo = null; + } + if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) { + $this->ihost = null; + } + if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) { + $this->port = null; + } + if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) { + $this->ipath = ''; + } + if (isset($this->ihost) && empty($this->ipath)) { + $this->ipath = '/'; + } + if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) { + $this->iquery = null; + } + if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) { + $this->ifragment = null; + } + } + + /** + * Check if the object represents a valid IRI. This needs to be done on each + * call as some things change depending on another part of the IRI. + * + * @return bool + */ + public function is_valid() { + $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; + if ($this->ipath !== '' && + ( + $isauthority && $this->ipath[0] !== '/' || + ( + $this->scheme === null && + !$isauthority && + strpos($this->ipath, ':') !== false && + (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) + ) + ) + ) { + return false; + } + + return true; + } + + /** + * Set the entire IRI. Returns true on success, false on failure (if there + * are any invalid characters). + * + * @param string $iri + * @return bool + */ + protected function set_iri($iri) { + static $cache; + if (!$cache) { + $cache = array(); + } + + if ($iri === null) { + return true; + } + if (isset($cache[$iri])) { + list($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return) = $cache[$iri]; + return $return; + } + + $parsed = $this->parse_iri((string) $iri); + + $return = $this->set_scheme($parsed['scheme']) + && $this->set_authority($parsed['authority']) + && $this->set_path($parsed['path']) + && $this->set_query($parsed['query']) + && $this->set_fragment($parsed['fragment']); + + $cache[$iri] = array($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return); + return $return; + } + + /** + * Set the scheme. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $scheme + * @return bool + */ + protected function set_scheme($scheme) { + if ($scheme === null) { + $this->scheme = null; + } + elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) { + $this->scheme = null; + return false; + } + else { + $this->scheme = strtolower($scheme); + } + return true; + } + + /** + * Set the authority. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $authority + * @return bool + */ + protected function set_authority($authority) { + static $cache; + if (!$cache) { + $cache = array(); + } + + if ($authority === null) { + $this->iuserinfo = null; + $this->ihost = null; + $this->port = null; + return true; + } + if (isset($cache[$authority])) { + list($this->iuserinfo, + $this->ihost, + $this->port, + $return) = $cache[$authority]; + + return $return; + } + + $remaining = $authority; + if (($iuserinfo_end = strrpos($remaining, '@')) !== false) { + $iuserinfo = substr($remaining, 0, $iuserinfo_end); + $remaining = substr($remaining, $iuserinfo_end + 1); + } + else { + $iuserinfo = null; + } + if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) { + $port = substr($remaining, $port_start + 1); + if ($port === false || $port === '') { + $port = null; + } + $remaining = substr($remaining, 0, $port_start); + } + else { + $port = null; + } + + $return = $this->set_userinfo($iuserinfo) && + $this->set_host($remaining) && + $this->set_port($port); + + $cache[$authority] = array($this->iuserinfo, + $this->ihost, + $this->port, + $return); + + return $return; + } + + /** + * Set the iuserinfo. + * + * @param string $iuserinfo + * @return bool + */ + protected function set_userinfo($iuserinfo) { + if ($iuserinfo === null) { + $this->iuserinfo = null; + } + else { + $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); + $this->scheme_normalization(); + } + + return true; + } + + /** + * Set the ihost. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $ihost + * @return bool + */ + protected function set_host($ihost) { + if ($ihost === null) { + $this->ihost = null; + return true; + } + if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') { + if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) { + $this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']'; + } + else { + $this->ihost = null; + return false; + } + } + else { + $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); + + // Lowercase, but ignore pct-encoded sections (as they should + // remain uppercase). This must be done after the previous step + // as that can add unescaped characters. + $position = 0; + $strlen = strlen($ihost); + while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) { + if ($ihost[$position] === '%') { + $position += 3; + } + else { + $ihost[$position] = strtolower($ihost[$position]); + $position++; + } + } + + $this->ihost = $ihost; + } + + $this->scheme_normalization(); + + return true; + } + + /** + * Set the port. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $port + * @return bool + */ + protected function set_port($port) { + if ($port === null) { + $this->port = null; + return true; + } + + if (strspn($port, '0123456789') === strlen($port)) { + $this->port = (int) $port; + $this->scheme_normalization(); + return true; + } + + $this->port = null; + return false; + } + + /** + * Set the ipath. + * + * @param string $ipath + * @return bool + */ + protected function set_path($ipath) { + static $cache; + if (!$cache) { + $cache = array(); + } + + $ipath = (string) $ipath; + + if (isset($cache[$ipath])) { + $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; + } + else { + $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); + $removed = $this->remove_dot_segments($valid); + + $cache[$ipath] = array($valid, $removed); + $this->ipath = ($this->scheme !== null) ? $removed : $valid; + } + $this->scheme_normalization(); + return true; + } + + /** + * Set the iquery. + * + * @param string $iquery + * @return bool + */ + protected function set_query($iquery) { + if ($iquery === null) { + $this->iquery = null; + } + else { + $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); + $this->scheme_normalization(); + } + return true; + } + + /** + * Set the ifragment. + * + * @param string $ifragment + * @return bool + */ + protected function set_fragment($ifragment) { + if ($ifragment === null) { + $this->ifragment = null; + } + else { + $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); + $this->scheme_normalization(); + } + return true; + } + + /** + * Convert an IRI to a URI (or parts thereof) + * + * @param string|bool IRI to convert (or false from {@see get_iri}) + * @return string|false URI if IRI is valid, false otherwise. + */ + protected function to_uri($string) { + if (!is_string($string)) { + return false; + } + + static $non_ascii; + if (!$non_ascii) { + $non_ascii = implode('', range("\x80", "\xFF")); + } + + $position = 0; + $strlen = strlen($string); + while (($position += strcspn($string, $non_ascii, $position)) < $strlen) { + $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); + $position += 3; + $strlen += 2; + } + + return $string; + } + + /** + * Get the complete IRI + * + * @return string + */ + protected function get_iri() { + if (!$this->is_valid()) { + return false; + } + + $iri = ''; + if ($this->scheme !== null) { + $iri .= $this->scheme . ':'; + } + if (($iauthority = $this->get_iauthority()) !== null) { + $iri .= '//' . $iauthority; + } + $iri .= $this->ipath; + if ($this->iquery !== null) { + $iri .= '?' . $this->iquery; + } + if ($this->ifragment !== null) { + $iri .= '#' . $this->ifragment; + } + + return $iri; + } + + /** + * Get the complete URI + * + * @return string + */ + protected function get_uri() { + return $this->to_uri($this->get_iri()); + } + + /** + * Get the complete iauthority + * + * @return string + */ + protected function get_iauthority() { + if ($this->iuserinfo === null && $this->ihost === null && $this->port === null) { + return null; + } + + $iauthority = ''; + if ($this->iuserinfo !== null) { + $iauthority .= $this->iuserinfo . '@'; + } + if ($this->ihost !== null) { + $iauthority .= $this->ihost; + } + if ($this->port !== null) { + $iauthority .= ':' . $this->port; + } + return $iauthority; + } + + /** + * Get the complete authority + * + * @return string + */ + protected function get_authority() { + $iauthority = $this->get_iauthority(); + if (is_string($iauthority)) { + return $this->to_uri($iauthority); + } + else { + return $iauthority; + } + } +} diff --git a/srcs/wordpress/wp-includes/Requests/Proxy.php b/srcs/wordpress/wp-includes/Requests/Proxy.php new file mode 100644 index 0000000..ac7c1d6 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Proxy.php @@ -0,0 +1,35 @@ +<?php +/** + * Proxy connection interface + * + * @package Requests + * @subpackage Proxy + * @since 1.6 + */ + +/** + * Proxy connection interface + * + * Implement this interface to handle proxy settings and authentication + * + * Parameters should be passed via the constructor where possible, as this + * makes it much easier for users to use your provider. + * + * @see Requests_Hooks + * @package Requests + * @subpackage Proxy + * @since 1.6 + */ +interface Requests_Proxy { + /** + * Register hooks as needed + * + * This method is called in {@see Requests::request} when the user has set + * an instance as the 'auth' option. Use this callback to register all the + * hooks you'll need. + * + * @see Requests_Hooks::register + * @param Requests_Hooks $hooks Hook system + */ + public function register(Requests_Hooks &$hooks); +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Proxy/HTTP.php b/srcs/wordpress/wp-includes/Requests/Proxy/HTTP.php new file mode 100644 index 0000000..6b4aba8 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Proxy/HTTP.php @@ -0,0 +1,151 @@ +<?php +/** + * HTTP Proxy connection interface + * + * @package Requests + * @subpackage Proxy + * @since 1.6 + */ + +/** + * HTTP Proxy connection interface + * + * Provides a handler for connection via an HTTP proxy + * + * @package Requests + * @subpackage Proxy + * @since 1.6 + */ +class Requests_Proxy_HTTP implements Requests_Proxy { + /** + * Proxy host and port + * + * Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128) + * + * @var string + */ + public $proxy; + + /** + * Username + * + * @var string + */ + public $user; + + /** + * Password + * + * @var string + */ + public $pass; + + /** + * Do we need to authenticate? (ie username & password have been provided) + * + * @var boolean + */ + public $use_authentication; + + /** + * Constructor + * + * @since 1.6 + * @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`) + * @param array|null $args Array of user and password. Must have exactly two elements + */ + public function __construct($args = null) { + if (is_string($args)) { + $this->proxy = $args; + } + elseif (is_array($args)) { + if (count($args) == 1) { + list($this->proxy) = $args; + } + elseif (count($args) == 3) { + list($this->proxy, $this->user, $this->pass) = $args; + $this->use_authentication = true; + } + else { + throw new Requests_Exception('Invalid number of arguments', 'proxyhttpbadargs'); + } + } + } + + /** + * Register the necessary callbacks + * + * @since 1.6 + * @see curl_before_send + * @see fsockopen_remote_socket + * @see fsockopen_remote_host_path + * @see fsockopen_header + * @param Requests_Hooks $hooks Hook system + */ + public function register(Requests_Hooks &$hooks) { + $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); + + $hooks->register('fsockopen.remote_socket', array(&$this, 'fsockopen_remote_socket')); + $hooks->register('fsockopen.remote_host_path', array(&$this, 'fsockopen_remote_host_path')); + if ($this->use_authentication) { + $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); + } + } + + /** + * Set cURL parameters before the data is sent + * + * @since 1.6 + * @param resource $handle cURL resource + */ + public function curl_before_send(&$handle) { + curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + curl_setopt($handle, CURLOPT_PROXY, $this->proxy); + + if ($this->use_authentication) { + curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string()); + } + } + + /** + * Alter remote socket information before opening socket connection + * + * @since 1.6 + * @param string $remote_socket Socket connection string + */ + public function fsockopen_remote_socket(&$remote_socket) { + $remote_socket = $this->proxy; + } + + /** + * Alter remote path before getting stream data + * + * @since 1.6 + * @param string $path Path to send in HTTP request string ("GET ...") + * @param string $url Full URL we're requesting + */ + public function fsockopen_remote_host_path(&$path, $url) { + $path = $url; + } + + /** + * Add extra headers to the request before sending + * + * @since 1.6 + * @param string $out HTTP header string + */ + public function fsockopen_header(&$out) { + $out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string())); + } + + /** + * Get the authentication string (user:pass) + * + * @since 1.6 + * @return string + */ + public function get_auth_string() { + return $this->user . ':' . $this->pass; + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Response.php b/srcs/wordpress/wp-includes/Requests/Response.php new file mode 100644 index 0000000..3152fb6 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Response.php @@ -0,0 +1,121 @@ +<?php +/** + * HTTP response class + * + * Contains a response from Requests::request() + * @package Requests + */ + +/** + * HTTP response class + * + * Contains a response from Requests::request() + * @package Requests + */ +class Requests_Response { + /** + * Constructor + */ + public function __construct() { + $this->headers = new Requests_Response_Headers(); + $this->cookies = new Requests_Cookie_Jar(); + } + + /** + * Response body + * + * @var string + */ + public $body = ''; + + /** + * Raw HTTP data from the transport + * + * @var string + */ + public $raw = ''; + + /** + * Headers, as an associative array + * + * @var Requests_Response_Headers Array-like object representing headers + */ + public $headers = array(); + + /** + * Status code, false if non-blocking + * + * @var integer|boolean + */ + public $status_code = false; + + /** + * Protocol version, false if non-blocking + * @var float|boolean + */ + public $protocol_version = false; + + /** + * Whether the request succeeded or not + * + * @var boolean + */ + public $success = false; + + /** + * Number of redirects the request used + * + * @var integer + */ + public $redirects = 0; + + /** + * URL requested + * + * @var string + */ + public $url = ''; + + /** + * Previous requests (from redirects) + * + * @var array Array of Requests_Response objects + */ + public $history = array(); + + /** + * Cookies from the request + * + * @var Requests_Cookie_Jar Array-like object representing a cookie jar + */ + public $cookies = array(); + + /** + * Is the response a redirect? + * + * @return boolean True if redirect (3xx status), false if not. + */ + public function is_redirect() { + $code = $this->status_code; + return in_array($code, array(300, 301, 302, 303, 307)) || $code > 307 && $code < 400; + } + + /** + * Throws an exception if the request was not successful + * + * @throws Requests_Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`) + * @throws Requests_Exception_HTTP On non-successful status code. Exception class corresponds to code (e.g. {@see Requests_Exception_HTTP_404}) + * @param boolean $allow_redirects Set to false to throw on a 3xx as well + */ + public function throw_for_status($allow_redirects = true) { + if ($this->is_redirect()) { + if (!$allow_redirects) { + throw new Requests_Exception('Redirection not allowed', 'response.no_redirects', $this); + } + } + elseif (!$this->success) { + $exception = Requests_Exception_HTTP::get_class($this->status_code); + throw new $exception(null, $this); + } + } +} diff --git a/srcs/wordpress/wp-includes/Requests/Response/Headers.php b/srcs/wordpress/wp-includes/Requests/Response/Headers.php new file mode 100644 index 0000000..cc6a208 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Response/Headers.php @@ -0,0 +1,98 @@ +<?php +/** + * Case-insensitive dictionary, suitable for HTTP headers + * + * @package Requests + */ + +/** + * Case-insensitive dictionary, suitable for HTTP headers + * + * @package Requests + */ +class Requests_Response_Headers extends Requests_Utility_CaseInsensitiveDictionary { + /** + * Get the given header + * + * Unlike {@see self::getValues()}, this returns a string. If there are + * multiple values, it concatenates them with a comma as per RFC2616. + * + * Avoid using this where commas may be used unquoted in values, such as + * Set-Cookie headers. + * + * @param string $key + * @return string Header value + */ + public function offsetGet($key) { + $key = strtolower($key); + if (!isset($this->data[$key])) { + return null; + } + + return $this->flatten($this->data[$key]); + } + + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $key = strtolower($key); + + if (!isset($this->data[$key])) { + $this->data[$key] = array(); + } + + $this->data[$key][] = $value; + } + + /** + * Get all values for a given header + * + * @param string $key + * @return array Header values + */ + public function getValues($key) { + $key = strtolower($key); + if (!isset($this->data[$key])) { + return null; + } + + return $this->data[$key]; + } + + /** + * Flattens a value into a string + * + * Converts an array into a string by imploding values with a comma, as per + * RFC2616's rules for folding headers. + * + * @param string|array $value Value to flatten + * @return string Flattened value + */ + public function flatten($value) { + if (is_array($value)) { + $value = implode(',', $value); + } + + return $value; + } + + /** + * Get an iterator for the data + * + * Converts the internal + * @return ArrayIterator + */ + public function getIterator() { + return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten')); + } +} diff --git a/srcs/wordpress/wp-includes/Requests/SSL.php b/srcs/wordpress/wp-includes/Requests/SSL.php new file mode 100644 index 0000000..2b03768 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/SSL.php @@ -0,0 +1,152 @@ +<?php +/** + * SSL utilities for Requests + * + * @package Requests + * @subpackage Utilities + */ + +/** + * SSL utilities for Requests + * + * Collection of utilities for working with and verifying SSL certificates. + * + * @package Requests + * @subpackage Utilities + */ +class Requests_SSL { + /** + * Verify the certificate against common name and subject alternative names + * + * Unfortunately, PHP doesn't check the certificate against the alternative + * names, leading things like 'https://www.github.com/' to be invalid. + * Instead + * + * @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 + * + * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) + * @param string $host Host name to verify against + * @param array $cert Certificate data from openssl_x509_parse() + * @return bool + */ + public static function verify_certificate($host, $cert) { + // Calculate the valid wildcard match if the host is not an IP address + $parts = explode('.', $host); + if (ip2long($host) === false) { + $parts[0] = '*'; + } + $wildcard = implode('.', $parts); + + $has_dns_alt = false; + + // Check the subjectAltName + if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) { + $altnames = explode(',', $cert['extensions']['subjectAltName']); + foreach ($altnames as $altname) { + $altname = trim($altname); + if (strpos($altname, 'DNS:') !== 0) { + continue; + } + + $has_dns_alt = true; + + // Strip the 'DNS:' prefix and trim whitespace + $altname = trim(substr($altname, 4)); + + // Check for a match + if (self::match_domain($host, $altname) === true) { + return true; + } + } + } + + // Fall back to checking the common name if we didn't get any dNSName + // alt names, as per RFC2818 + if (!$has_dns_alt && !empty($cert['subject']['CN'])) { + // Check for a match + if (self::match_domain($host, $cert['subject']['CN']) === true) { + return true; + } + } + + return false; + } + + /** + * Verify that a reference name is valid + * + * Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules: + * - Wildcards can only occur in a name with more than 3 components + * - Wildcards can only occur as the last character in the first + * component + * - Wildcards may be preceded by additional characters + * + * We modify these rules to be a bit stricter and only allow the wildcard + * character to be the full first component; that is, with the exclusion of + * the third rule. + * + * @param string $reference Reference dNSName + * @return boolean Is the name valid? + */ + public static function verify_reference_name($reference) { + $parts = explode('.', $reference); + + // Check the first part of the name + $first = array_shift($parts); + + if (strpos($first, '*') !== false) { + // Check that the wildcard is the full part + if ($first !== '*') { + return false; + } + + // Check that we have at least 3 components (including first) + if (count($parts) < 2) { + return false; + } + } + + // Check the remaining parts + foreach ($parts as $part) { + if (strpos($part, '*') !== false) { + return false; + } + } + + // Nothing found, verified! + return true; + } + + /** + * Match a hostname against a dNSName reference + * + * @param string $host Requested host + * @param string $reference dNSName to match against + * @return boolean Does the domain match? + */ + public static function match_domain($host, $reference) { + // Check if the reference is blacklisted first + if (self::verify_reference_name($reference) !== true) { + return false; + } + + // Check for a direct match + if ($host === $reference) { + return true; + } + + // Calculate the valid wildcard match if the host is not an IP address + // Also validates that the host has 3 parts or more, as per Firefox's + // ruleset. + if (ip2long($host) === false) { + $parts = explode('.', $host); + $parts[0] = '*'; + $wildcard = implode('.', $parts); + if ($wildcard === $reference) { + return true; + } + } + + return false; + } +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Session.php b/srcs/wordpress/wp-includes/Requests/Session.php new file mode 100644 index 0000000..af14bbe --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Session.php @@ -0,0 +1,266 @@ +<?php +/** + * Session handler for persistent requests and default parameters + * + * @package Requests + * @subpackage Session Handler + */ + +/** + * Session handler for persistent requests and default parameters + * + * Allows various options to be set as default values, and merges both the + * options and URL properties together. A base URL can be set for all requests, + * with all subrequests resolved from this. Base options can be set (including + * a shared cookie jar), then overridden for individual requests. + * + * @package Requests + * @subpackage Session Handler + */ +class Requests_Session { + /** + * Base URL for requests + * + * URLs will be made absolute using this as the base + * @var string|null + */ + public $url = null; + + /** + * Base headers for requests + * @var array + */ + public $headers = array(); + + /** + * Base data for requests + * + * If both the base data and the per-request data are arrays, the data will + * be merged before sending the request. + * + * @var array + */ + public $data = array(); + + /** + * Base options for requests + * + * The base options are merged with the per-request data for each request. + * The only default option is a shared cookie jar between requests. + * + * Values here can also be set directly via properties on the Session + * object, e.g. `$session->useragent = 'X';` + * + * @var array + */ + public $options = array(); + + /** + * Create a new session + * + * @param string|null $url Base URL for requests + * @param array $headers Default headers for requests + * @param array $data Default data for requests + * @param array $options Default options for requests + */ + public function __construct($url = null, $headers = array(), $data = array(), $options = array()) { + $this->url = $url; + $this->headers = $headers; + $this->data = $data; + $this->options = $options; + + if (empty($this->options['cookies'])) { + $this->options['cookies'] = new Requests_Cookie_Jar(); + } + } + + /** + * Get a property's value + * + * @param string $key Property key + * @return mixed|null Property value, null if none found + */ + public function __get($key) { + if (isset($this->options[$key])) { + return $this->options[$key]; + } + + return null; + } + + /** + * Set a property's value + * + * @param string $key Property key + * @param mixed $value Property value + */ + public function __set($key, $value) { + $this->options[$key] = $value; + } + + /** + * Remove a property's value + * + * @param string $key Property key + */ + public function __isset($key) { + return isset($this->options[$key]); + } + + /** + * Remove a property's value + * + * @param string $key Property key + */ + public function __unset($key) { + if (isset($this->options[$key])) { + unset($this->options[$key]); + } + } + + /**#@+ + * @see request() + * @param string $url + * @param array $headers + * @param array $options + * @return Requests_Response + */ + /** + * Send a GET request + */ + public function get($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::GET, $options); + } + + /** + * Send a HEAD request + */ + public function head($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::HEAD, $options); + } + + /** + * Send a DELETE request + */ + public function delete($url, $headers = array(), $options = array()) { + return $this->request($url, $headers, null, Requests::DELETE, $options); + } + /**#@-*/ + + /**#@+ + * @see request() + * @param string $url + * @param array $headers + * @param array $data + * @param array $options + * @return Requests_Response + */ + /** + * Send a POST request + */ + public function post($url, $headers = array(), $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::POST, $options); + } + + /** + * Send a PUT request + */ + public function put($url, $headers = array(), $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::PUT, $options); + } + + /** + * Send a PATCH request + * + * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the + * specification recommends that should send an ETag + * + * @link https://tools.ietf.org/html/rfc5789 + */ + public function patch($url, $headers, $data = array(), $options = array()) { + return $this->request($url, $headers, $data, Requests::PATCH, $options); + } + /**#@-*/ + + /** + * Main interface for HTTP requests + * + * This method initiates a request and sends it via a transport before + * parsing. + * + * @see Requests::request() + * + * @throws Requests_Exception On invalid URLs (`nonhttp`) + * + * @param string $url URL to request + * @param array $headers Extra headers to send with the request + * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests + * @param string $type HTTP request type (use Requests constants) + * @param array $options Options for the request (see {@see Requests::request}) + * @return Requests_Response + */ + public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) { + $request = $this->merge_request(compact('url', 'headers', 'data', 'options')); + + return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']); + } + + /** + * Send multiple HTTP requests simultaneously + * + * @see Requests::request_multiple() + * + * @param array $requests Requests data (see {@see Requests::request_multiple}) + * @param array $options Global and default options (see {@see Requests::request}) + * @return array Responses (either Requests_Response or a Requests_Exception object) + */ + public function request_multiple($requests, $options = array()) { + foreach ($requests as $key => $request) { + $requests[$key] = $this->merge_request($request, false); + } + + $options = array_merge($this->options, $options); + + // Disallow forcing the type, as that's a per request setting + unset($options['type']); + + return Requests::request_multiple($requests, $options); + } + + /** + * Merge a request's data with the default data + * + * @param array $request Request data (same form as {@see request_multiple}) + * @param boolean $merge_options Should we merge options as well? + * @return array Request data + */ + protected function merge_request($request, $merge_options = true) { + if ($this->url !== null) { + $request['url'] = Requests_IRI::absolutize($this->url, $request['url']); + $request['url'] = $request['url']->uri; + } + + if (empty($request['headers'])) { + $request['headers'] = array(); + } + $request['headers'] = array_merge($this->headers, $request['headers']); + + if (empty($request['data'])) { + if (is_array($this->data)) { + $request['data'] = $this->data; + } + } + elseif (is_array($request['data']) && is_array($this->data)) { + $request['data'] = array_merge($this->data, $request['data']); + } + + if ($merge_options !== false) { + $request['options'] = array_merge($this->options, $request['options']); + + // Disallow forcing the type, as that's a per request setting + unset($request['options']['type']); + } + + return $request; + } +} diff --git a/srcs/wordpress/wp-includes/Requests/Transport.php b/srcs/wordpress/wp-includes/Requests/Transport.php new file mode 100644 index 0000000..7e4a262 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Transport.php @@ -0,0 +1,41 @@ +<?php +/** + * Base HTTP transport + * + * @package Requests + * @subpackage Transport + */ + +/** + * Base HTTP transport + * + * @package Requests + * @subpackage Transport + */ +interface Requests_Transport { + /** + * Perform a request + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + * @return string Raw HTTP result + */ + public function request($url, $headers = array(), $data = array(), $options = array()); + + /** + * Send multiple requests simultaneously + * + * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request} + * @param array $options Global options, see {@see Requests::response()} for documentation + * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) + */ + public function request_multiple($requests, $options); + + /** + * Self-test whether the transport can be used + * @return bool + */ + public static function test(); +}
\ No newline at end of file diff --git a/srcs/wordpress/wp-includes/Requests/Transport/cURL.php b/srcs/wordpress/wp-includes/Requests/Transport/cURL.php new file mode 100644 index 0000000..4429edb --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Transport/cURL.php @@ -0,0 +1,542 @@ +<?php +/** + * cURL HTTP transport + * + * @package Requests + * @subpackage Transport + */ + +/** + * cURL HTTP transport + * + * @package Requests + * @subpackage Transport + */ +class Requests_Transport_cURL implements Requests_Transport { + const CURL_7_10_5 = 0x070A05; + const CURL_7_16_2 = 0x071002; + + /** + * Raw HTTP data + * + * @var string + */ + public $headers = ''; + + /** + * Raw body data + * + * @var string + */ + public $response_data = ''; + + /** + * Information on the current request + * + * @var array cURL information array, see {@see https://secure.php.net/curl_getinfo} + */ + public $info; + + /** + * Version string + * + * @var long + */ + public $version; + + /** + * cURL handle + * + * @var resource + */ + protected $handle; + + /** + * Hook dispatcher instance + * + * @var Requests_Hooks + */ + protected $hooks; + + /** + * Have we finished the headers yet? + * + * @var boolean + */ + protected $done_headers = false; + + /** + * If streaming to a file, keep the file pointer + * + * @var resource + */ + protected $stream_handle; + + /** + * How many bytes are in the response body? + * + * @var int + */ + protected $response_bytes; + + /** + * What's the maximum number of bytes we should keep? + * + * @var int|bool Byte count, or false if no limit. + */ + protected $response_byte_limit; + + /** + * Constructor + */ + public function __construct() { + $curl = curl_version(); + $this->version = $curl['version_number']; + $this->handle = curl_init(); + + curl_setopt($this->handle, CURLOPT_HEADER, false); + curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1); + if ($this->version >= self::CURL_7_10_5) { + curl_setopt($this->handle, CURLOPT_ENCODING, ''); + } + if (defined('CURLOPT_PROTOCOLS')) { + curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + } + if (defined('CURLOPT_REDIR_PROTOCOLS')) { + curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + } + } + + /** + * Destructor + */ + public function __destruct() { + if (is_resource($this->handle)) { + curl_close($this->handle); + } + } + + /** + * Perform a request + * + * @throws Requests_Exception On a cURL error (`curlerror`) + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + * @return string Raw HTTP result + */ + public function request($url, $headers = array(), $data = array(), $options = array()) { + $this->hooks = $options['hooks']; + + $this->setup_handle($url, $headers, $data, $options); + + $options['hooks']->dispatch('curl.before_send', array(&$this->handle)); + + if ($options['filename'] !== false) { + $this->stream_handle = fopen($options['filename'], 'wb'); + } + + $this->response_data = ''; + $this->response_bytes = 0; + $this->response_byte_limit = false; + if ($options['max_bytes'] !== false) { + $this->response_byte_limit = $options['max_bytes']; + } + + if (isset($options['verify'])) { + if ($options['verify'] === false) { + curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0); + } + elseif (is_string($options['verify'])) { + curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']); + } + } + + if (isset($options['verifyname']) && $options['verifyname'] === false) { + curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0); + } + + curl_exec($this->handle); + $response = $this->response_data; + + $options['hooks']->dispatch('curl.after_send', array()); + + if (curl_errno($this->handle) === 23 || curl_errno($this->handle) === 61) { + // Reset encoding and try again + curl_setopt($this->handle, CURLOPT_ENCODING, 'none'); + + $this->response_data = ''; + $this->response_bytes = 0; + curl_exec($this->handle); + $response = $this->response_data; + } + + $this->process_response($response, $options); + + // Need to remove the $this reference from the curl handle. + // Otherwise Requests_Transport_cURL wont be garbage collected and the curl_close() will never be called. + curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, null); + curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, null); + + return $this->headers; + } + + /** + * Send multiple requests simultaneously + * + * @param array $requests Request data + * @param array $options Global options + * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) + */ + public function request_multiple($requests, $options) { + // If you're not requesting, we can't get any responses ¯\_(ツ)_/¯ + if (empty($requests)) { + return array(); + } + + $multihandle = curl_multi_init(); + $subrequests = array(); + $subhandles = array(); + + $class = get_class($this); + foreach ($requests as $id => $request) { + $subrequests[$id] = new $class(); + $subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']); + $request['options']['hooks']->dispatch('curl.before_multi_add', array(&$subhandles[$id])); + curl_multi_add_handle($multihandle, $subhandles[$id]); + } + + $completed = 0; + $responses = array(); + + $request['options']['hooks']->dispatch('curl.before_multi_exec', array(&$multihandle)); + + do { + $active = false; + + do { + $status = curl_multi_exec($multihandle, $active); + } + while ($status === CURLM_CALL_MULTI_PERFORM); + + $to_process = array(); + + // Read the information as needed + while ($done = curl_multi_info_read($multihandle)) { + $key = array_search($done['handle'], $subhandles, true); + if (!isset($to_process[$key])) { + $to_process[$key] = $done; + } + } + + // Parse the finished requests before we start getting the new ones + foreach ($to_process as $key => $done) { + $options = $requests[$key]['options']; + if (CURLE_OK !== $done['result']) { + //get error string for handle. + $reason = curl_error($done['handle']); + $exception = new Requests_Exception_Transport_cURL( + $reason, + Requests_Exception_Transport_cURL::EASY, + $done['handle'], + $done['result'] + ); + $responses[$key] = $exception; + $options['hooks']->dispatch('transport.internal.parse_error', array(&$responses[$key], $requests[$key])); + } + else { + $responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options); + + $options['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$key], $requests[$key])); + } + + curl_multi_remove_handle($multihandle, $done['handle']); + curl_close($done['handle']); + + if (!is_string($responses[$key])) { + $options['hooks']->dispatch('multiple.request.complete', array(&$responses[$key], $key)); + } + $completed++; + } + } + while ($active || $completed < count($subrequests)); + + $request['options']['hooks']->dispatch('curl.after_multi_exec', array(&$multihandle)); + + curl_multi_close($multihandle); + + return $responses; + } + + /** + * Get the cURL handle for use in a multi-request + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + * @return resource Subrequest's cURL handle + */ + public function &get_subrequest_handle($url, $headers, $data, $options) { + $this->setup_handle($url, $headers, $data, $options); + + if ($options['filename'] !== false) { + $this->stream_handle = fopen($options['filename'], 'wb'); + } + + $this->response_data = ''; + $this->response_bytes = 0; + $this->response_byte_limit = false; + if ($options['max_bytes'] !== false) { + $this->response_byte_limit = $options['max_bytes']; + } + $this->hooks = $options['hooks']; + + return $this->handle; + } + + /** + * Setup the cURL handle for the given data + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + */ + protected function setup_handle($url, $headers, $data, $options) { + $options['hooks']->dispatch('curl.before_request', array(&$this->handle)); + + // Force closing the connection for old versions of cURL (<7.22). + if ( ! isset( $headers['Connection'] ) ) { + $headers['Connection'] = 'close'; + } + + $headers = Requests::flatten($headers); + + if (!empty($data)) { + $data_format = $options['data_format']; + + if ($data_format === 'query') { + $url = self::format_get($url, $data); + $data = ''; + } + elseif (!is_string($data)) { + $data = http_build_query($data, null, '&'); + } + } + + switch ($options['type']) { + case Requests::POST: + curl_setopt($this->handle, CURLOPT_POST, true); + curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data); + break; + case Requests::HEAD: + curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']); + curl_setopt($this->handle, CURLOPT_NOBODY, true); + break; + case Requests::TRACE: + curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']); + break; + case Requests::PATCH: + case Requests::PUT: + case Requests::DELETE: + case Requests::OPTIONS: + default: + curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']); + if (!empty($data)) { + curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data); + } + } + + // cURL requires a minimum timeout of 1 second when using the system + // DNS resolver, as it uses `alarm()`, which is second resolution only. + // There's no way to detect which DNS resolver is being used from our + // end, so we need to round up regardless of the supplied timeout. + // + // https://github.com/curl/curl/blob/4f45240bc84a9aa648c8f7243be7b79e9f9323a5/lib/hostip.c#L606-L609 + $timeout = max($options['timeout'], 1); + + if (is_int($timeout) || $this->version < self::CURL_7_16_2) { + curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout)); + } + else { + curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000)); + } + + if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) { + curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout'])); + } + else { + curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000)); + } + curl_setopt($this->handle, CURLOPT_URL, $url); + curl_setopt($this->handle, CURLOPT_REFERER, $url); + curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']); + if (!empty($headers)) { + curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers); + } + if ($options['protocol_version'] === 1.1) { + curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + } + else { + curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + } + + if (true === $options['blocking']) { + curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, array(&$this, 'stream_headers')); + curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, array(&$this, 'stream_body')); + curl_setopt($this->handle, CURLOPT_BUFFERSIZE, Requests::BUFFER_SIZE); + } + } + + /** + * Process a response + * + * @param string $response Response data from the body + * @param array $options Request options + * @return string HTTP response data including headers + */ + public function process_response($response, $options) { + if ($options['blocking'] === false) { + $fake_headers = ''; + $options['hooks']->dispatch('curl.after_request', array(&$fake_headers)); + return false; + } + if ($options['filename'] !== false) { + fclose($this->stream_handle); + $this->headers = trim($this->headers); + } + else { + $this->headers .= $response; + } + + if (curl_errno($this->handle)) { + $error = sprintf( + 'cURL error %s: %s', + curl_errno($this->handle), + curl_error($this->handle) + ); + throw new Requests_Exception($error, 'curlerror', $this->handle); + } + $this->info = curl_getinfo($this->handle); + + $options['hooks']->dispatch('curl.after_request', array(&$this->headers, &$this->info)); + return $this->headers; + } + + /** + * Collect the headers as they are received + * + * @param resource $handle cURL resource + * @param string $headers Header string + * @return integer Length of provided header + */ + public function stream_headers($handle, $headers) { + // Why do we do this? cURL will send both the final response and any + // interim responses, such as a 100 Continue. We don't need that. + // (We may want to keep this somewhere just in case) + if ($this->done_headers) { + $this->headers = ''; + $this->done_headers = false; + } + $this->headers .= $headers; + + if ($headers === "\r\n") { + $this->done_headers = true; + } + return strlen($headers); + } + + /** + * Collect data as it's received + * + * @since 1.6.1 + * + * @param resource $handle cURL resource + * @param string $data Body data + * @return integer Length of provided data + */ + public function stream_body($handle, $data) { + $this->hooks->dispatch('request.progress', array($data, $this->response_bytes, $this->response_byte_limit)); + $data_length = strlen($data); + + // Are we limiting the response size? + if ($this->response_byte_limit) { + if ($this->response_bytes === $this->response_byte_limit) { + // Already at maximum, move on + return $data_length; + } + + if (($this->response_bytes + $data_length) > $this->response_byte_limit) { + // Limit the length + $limited_length = ($this->response_byte_limit - $this->response_bytes); + $data = substr($data, 0, $limited_length); + } + } + + if ($this->stream_handle) { + fwrite($this->stream_handle, $data); + } + else { + $this->response_data .= $data; + } + + $this->response_bytes += strlen($data); + return $data_length; + } + + /** + * Format a URL given GET data + * + * @param string $url + * @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query} + * @return string URL with data + */ + protected static function format_get($url, $data) { + if (!empty($data)) { + $url_parts = parse_url($url); + if (empty($url_parts['query'])) { + $query = $url_parts['query'] = ''; + } + else { + $query = $url_parts['query']; + } + + $query .= '&' . http_build_query($data, null, '&'); + $query = trim($query, '&'); + + if (empty($url_parts['query'])) { + $url .= '?' . $query; + } + else { + $url = str_replace($url_parts['query'], $query, $url); + } + } + return $url; + } + + /** + * Whether this transport is valid + * + * @codeCoverageIgnore + * @return boolean True if the transport is valid, false otherwise. + */ + public static function test($capabilities = array()) { + if (!function_exists('curl_init') || !function_exists('curl_exec')) { + return false; + } + + // If needed, check that our installed curl version supports SSL + if (isset($capabilities['ssl']) && $capabilities['ssl']) { + $curl_version = curl_version(); + if (!(CURL_VERSION_SSL & $curl_version['features'])) { + return false; + } + } + + return true; + } +} diff --git a/srcs/wordpress/wp-includes/Requests/Transport/fsockopen.php b/srcs/wordpress/wp-includes/Requests/Transport/fsockopen.php new file mode 100644 index 0000000..c7c61d3 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Transport/fsockopen.php @@ -0,0 +1,444 @@ +<?php +/** + * fsockopen HTTP transport + * + * @package Requests + * @subpackage Transport + */ + +/** + * fsockopen HTTP transport + * + * @package Requests + * @subpackage Transport + */ +class Requests_Transport_fsockopen implements Requests_Transport { + /** + * Second to microsecond conversion + * + * @var integer + */ + const SECOND_IN_MICROSECONDS = 1000000; + + /** + * Raw HTTP data + * + * @var string + */ + public $headers = ''; + + /** + * Stream metadata + * + * @var array Associative array of properties, see {@see https://secure.php.net/stream_get_meta_data} + */ + public $info; + + /** + * What's the maximum number of bytes we should keep? + * + * @var int|bool Byte count, or false if no limit. + */ + protected $max_bytes = false; + + protected $connect_error = ''; + + /** + * Perform a request + * + * @throws Requests_Exception On failure to connect to socket (`fsockopenerror`) + * @throws Requests_Exception On socket timeout (`timeout`) + * + * @param string $url URL to request + * @param array $headers Associative array of request headers + * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD + * @param array $options Request options, see {@see Requests::response()} for documentation + * @return string Raw HTTP result + */ + public function request($url, $headers = array(), $data = array(), $options = array()) { + $options['hooks']->dispatch('fsockopen.before_request'); + + $url_parts = parse_url($url); + if (empty($url_parts)) { + throw new Requests_Exception('Invalid URL.', 'invalidurl', $url); + } + $host = $url_parts['host']; + $context = stream_context_create(); + $verifyname = false; + $case_insensitive_headers = new Requests_Utility_CaseInsensitiveDictionary($headers); + + // HTTPS support + if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') { + $remote_socket = 'ssl://' . $host; + if (!isset($url_parts['port'])) { + $url_parts['port'] = 443; + } + + $context_options = array( + 'verify_peer' => true, + // 'CN_match' => $host, + 'capture_peer_cert' => true + ); + $verifyname = true; + + // SNI, if enabled (OpenSSL >=0.9.8j) + if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) { + $context_options['SNI_enabled'] = true; + if (isset($options['verifyname']) && $options['verifyname'] === false) { + $context_options['SNI_enabled'] = false; + } + } + + if (isset($options['verify'])) { + if ($options['verify'] === false) { + $context_options['verify_peer'] = false; + } + elseif (is_string($options['verify'])) { + $context_options['cafile'] = $options['verify']; + } + } + + if (isset($options['verifyname']) && $options['verifyname'] === false) { + $context_options['verify_peer_name'] = false; + $verifyname = false; + } + + stream_context_set_option($context, array('ssl' => $context_options)); + } + else { + $remote_socket = 'tcp://' . $host; + } + + $this->max_bytes = $options['max_bytes']; + + if (!isset($url_parts['port'])) { + $url_parts['port'] = 80; + } + $remote_socket .= ':' . $url_parts['port']; + + set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE); + + $options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket)); + + $socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context); + + restore_error_handler(); + + if ($verifyname && !$this->verify_certificate_from_context($host, $context)) { + throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match'); + } + + if (!$socket) { + if ($errno === 0) { + // Connection issue + throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error'); + } + + throw new Requests_Exception($errstr, 'fsockopenerror', null, $errno); + } + + $data_format = $options['data_format']; + + if ($data_format === 'query') { + $path = self::format_get($url_parts, $data); + $data = ''; + } + else { + $path = self::format_get($url_parts, array()); + } + + $options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url)); + + $request_body = ''; + $out = sprintf("%s %s HTTP/%.1F\r\n", $options['type'], $path, $options['protocol_version']); + + if ($options['type'] !== Requests::TRACE) { + if (is_array($data)) { + $request_body = http_build_query($data, null, '&'); + } + else { + $request_body = $data; + } + + if (!empty($data)) { + if (!isset($case_insensitive_headers['Content-Length'])) { + $headers['Content-Length'] = strlen($request_body); + } + + if (!isset($case_insensitive_headers['Content-Type'])) { + $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; + } + } + } + + if (!isset($case_insensitive_headers['Host'])) { + $out .= sprintf('Host: %s', $url_parts['host']); + + if (( 'http' === strtolower($url_parts['scheme']) && $url_parts['port'] !== 80 ) || ( 'https' === strtolower($url_parts['scheme']) && $url_parts['port'] !== 443 )) { + $out .= ':' . $url_parts['port']; + } + $out .= "\r\n"; + } + + if (!isset($case_insensitive_headers['User-Agent'])) { + $out .= sprintf("User-Agent: %s\r\n", $options['useragent']); + } + + $accept_encoding = $this->accept_encoding(); + if (!isset($case_insensitive_headers['Accept-Encoding']) && !empty($accept_encoding)) { + $out .= sprintf("Accept-Encoding: %s\r\n", $accept_encoding); + } + + $headers = Requests::flatten($headers); + + if (!empty($headers)) { + $out .= implode("\r\n", $headers) . "\r\n"; + } + + $options['hooks']->dispatch('fsockopen.after_headers', array(&$out)); + + if (substr($out, -2) !== "\r\n") { + $out .= "\r\n"; + } + + if (!isset($case_insensitive_headers['Connection'])) { + $out .= "Connection: Close\r\n"; + } + + $out .= "\r\n" . $request_body; + + $options['hooks']->dispatch('fsockopen.before_send', array(&$out)); + + fwrite($socket, $out); + $options['hooks']->dispatch('fsockopen.after_send', array($out)); + + if (!$options['blocking']) { + fclose($socket); + $fake_headers = ''; + $options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers)); + return ''; + } + + $timeout_sec = (int) floor($options['timeout']); + if ($timeout_sec == $options['timeout']) { + $timeout_msec = 0; + } + else { + $timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS; + } + stream_set_timeout($socket, $timeout_sec, $timeout_msec); + + $response = $body = $headers = ''; + $this->info = stream_get_meta_data($socket); + $size = 0; + $doingbody = false; + $download = false; + if ($options['filename']) { + $download = fopen($options['filename'], 'wb'); + } + + while (!feof($socket)) { + $this->info = stream_get_meta_data($socket); + if ($this->info['timed_out']) { + throw new Requests_Exception('fsocket timed out', 'timeout'); + } + + $block = fread($socket, Requests::BUFFER_SIZE); + if (!$doingbody) { + $response .= $block; + if (strpos($response, "\r\n\r\n")) { + list($headers, $block) = explode("\r\n\r\n", $response, 2); + $doingbody = true; + } + } + + // Are we in body mode now? + if ($doingbody) { + $options['hooks']->dispatch('request.progress', array($block, $size, $this->max_bytes)); + $data_length = strlen($block); + if ($this->max_bytes) { + // Have we already hit a limit? + if ($size === $this->max_bytes) { + continue; + } + if (($size + $data_length) > $this->max_bytes) { + // Limit the length + $limited_length = ($this->max_bytes - $size); + $block = substr($block, 0, $limited_length); + } + } + + $size += strlen($block); + if ($download) { + fwrite($download, $block); + } + else { + $body .= $block; + } + } + } + $this->headers = $headers; + + if ($download) { + fclose($download); + } + else { + $this->headers .= "\r\n\r\n" . $body; + } + fclose($socket); + + $options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers, &$this->info)); + return $this->headers; + } + + /** + * Send multiple requests simultaneously + * + * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request} + * @param array $options Global options, see {@see Requests::response()} for documentation + * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) + */ + public function request_multiple($requests, $options) { + $responses = array(); + $class = get_class($this); + foreach ($requests as $id => $request) { + try { + $handler = new $class(); + $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); + + $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request)); + } + catch (Requests_Exception $e) { + $responses[$id] = $e; + } + + if (!is_string($responses[$id])) { + $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id)); + } + } + + return $responses; + } + + /** + * Retrieve the encodings we can accept + * + * @return string Accept-Encoding header value + */ + protected static function accept_encoding() { + $type = array(); + if (function_exists('gzinflate')) { + $type[] = 'deflate;q=1.0'; + } + + if (function_exists('gzuncompress')) { + $type[] = 'compress;q=0.5'; + } + + $type[] = 'gzip;q=0.5'; + + return implode(', ', $type); + } + + /** + * Format a URL given GET data + * + * @param array $url_parts + * @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query} + * @return string URL with data + */ + protected static function format_get($url_parts, $data) { + if (!empty($data)) { + if (empty($url_parts['query'])) { + $url_parts['query'] = ''; + } + + $url_parts['query'] .= '&' . http_build_query($data, null, '&'); + $url_parts['query'] = trim($url_parts['query'], '&'); + } + if (isset($url_parts['path'])) { + if (isset($url_parts['query'])) { + $get = $url_parts['path'] . '?' . $url_parts['query']; + } + else { + $get = $url_parts['path']; + } + } + else { + $get = '/'; + } + return $get; + } + + /** + * Error handler for stream_socket_client() + * + * @param int $errno Error number (e.g. E_WARNING) + * @param string $errstr Error message + */ + public function connect_error_handler($errno, $errstr) { + // Double-check we can handle it + if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) { + // Return false to indicate the default error handler should engage + return false; + } + + $this->connect_error .= $errstr . "\n"; + return true; + } + + /** + * Verify the certificate against common name and subject alternative names + * + * Unfortunately, PHP doesn't check the certificate against the alternative + * names, leading things like 'https://www.github.com/' to be invalid. + * Instead + * + * @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 + * + * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`) + * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) + * @param string $host Host name to verify against + * @param resource $context Stream context + * @return bool + */ + public function verify_certificate_from_context($host, $context) { + $meta = stream_context_get_options($context); + + // If we don't have SSL options, then we couldn't make the connection at + // all + if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { + throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error'); + } + + $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); + + return Requests_SSL::verify_certificate($host, $cert); + } + + /** + * Whether this transport is valid + * + * @codeCoverageIgnore + * @return boolean True if the transport is valid, false otherwise. + */ + public static function test($capabilities = array()) { + if (!function_exists('fsockopen')) { + return false; + } + + // If needed, check that streams support SSL + if (isset($capabilities['ssl']) && $capabilities['ssl']) { + if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) { + return false; + } + + // Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156 + if (defined('HHVM_VERSION')) { + return false; + } + } + + return true; + } +} diff --git a/srcs/wordpress/wp-includes/Requests/Utility/CaseInsensitiveDictionary.php b/srcs/wordpress/wp-includes/Requests/Utility/CaseInsensitiveDictionary.php new file mode 100644 index 0000000..2c97893 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Utility/CaseInsensitiveDictionary.php @@ -0,0 +1,103 @@ +<?php +/** + * Case-insensitive dictionary, suitable for HTTP headers + * + * @package Requests + * @subpackage Utilities + */ + +/** + * Case-insensitive dictionary, suitable for HTTP headers + * + * @package Requests + * @subpackage Utilities + */ +class Requests_Utility_CaseInsensitiveDictionary implements ArrayAccess, IteratorAggregate { + /** + * Actual item data + * + * @var array + */ + protected $data = array(); + + /** + * Creates a case insensitive dictionary. + * + * @param array $data Dictionary/map to convert to case-insensitive + */ + public function __construct(array $data = array()) { + foreach ($data as $key => $value) { + $this->offsetSet($key, $value); + } + } + + /** + * Check if the given item exists + * + * @param string $key Item key + * @return boolean Does the item exist? + */ + public function offsetExists($key) { + $key = strtolower($key); + return isset($this->data[$key]); + } + + /** + * Get the value for the item + * + * @param string $key Item key + * @return string Item value + */ + public function offsetGet($key) { + $key = strtolower($key); + if (!isset($this->data[$key])) { + return null; + } + + return $this->data[$key]; + } + + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $key = strtolower($key); + $this->data[$key] = $value; + } + + /** + * Unset the given header + * + * @param string $key + */ + public function offsetUnset($key) { + unset($this->data[strtolower($key)]); + } + + /** + * Get an iterator for the data + * + * @return ArrayIterator + */ + public function getIterator() { + return new ArrayIterator($this->data); + } + + /** + * Get the headers as an array + * + * @return array Header data + */ + public function getAll() { + return $this->data; + } +} diff --git a/srcs/wordpress/wp-includes/Requests/Utility/FilteredIterator.php b/srcs/wordpress/wp-includes/Requests/Utility/FilteredIterator.php new file mode 100644 index 0000000..76a29e7 --- /dev/null +++ b/srcs/wordpress/wp-includes/Requests/Utility/FilteredIterator.php @@ -0,0 +1,45 @@ +<?php +/** + * Iterator for arrays requiring filtered values + * + * @package Requests + * @subpackage Utilities + */ + +/** + * Iterator for arrays requiring filtered values + * + * @package Requests + * @subpackage Utilities + */ +class Requests_Utility_FilteredIterator extends ArrayIterator { + /** + * Callback to run as a filter + * + * @var callable + */ + protected $callback; + + /** + * Create a new iterator + * + * @param array $data + * @param callable $callback Callback to be called on each value + */ + public function __construct($data, $callback) { + parent::__construct($data); + + $this->callback = $callback; + } + + /** + * Get the current item's value after filtering + * + * @return string + */ + public function current() { + $value = parent::current(); + $value = call_user_func($this->callback, $value); + return $value; + } +} |
