Ampache PHP Cross Reference Groupware Applications

Source: /modules/requests/Requests.php - 863 lines - 26794 bytes - Summary - Text - Print

Description: Requests for PHP Inspired by Requests for Python.

   1  <?php
   2  /**
   3   * Requests for PHP
   4   *
   5   * Inspired by Requests for Python.
   6   *
   7   * Based on concepts from SimplePie_File, RequestCore and WP_Http.
   8   *
   9   * @package Requests
  10   */
  11  
  12  /**
  13   * Requests for PHP
  14   *
  15   * Inspired by Requests for Python.
  16   *
  17   * Based on concepts from SimplePie_File, RequestCore and WP_Http.
  18   *
  19   * @package Requests
  20   */
  21  class Requests {
  22      /**
  23       * POST method
  24       *
  25       * @var string
  26       */
  27      const POST = 'POST';
  28  
  29      /**
  30       * PUT method
  31       *
  32       * @var string
  33       */
  34      const PUT = 'PUT';
  35  
  36      /**
  37       * GET method
  38       *
  39       * @var string
  40       */
  41      const GET = 'GET';
  42  
  43      /**
  44       * HEAD method
  45       *
  46       * @var string
  47       */
  48      const HEAD = 'HEAD';
  49  
  50      /**
  51       * DELETE method
  52       *
  53       * @var string
  54       */
  55      const DELETE = 'DELETE';
  56  
  57      /**
  58       * PATCH method
  59       *
  60       * @link http://tools.ietf.org/html/rfc5789
  61       * @var string
  62       */
  63      const PATCH = 'PATCH';
  64  
  65      /**
  66       * Current version of Requests
  67       *
  68       * @var string
  69       */
  70      const VERSION = '1.6';
  71  
  72      /**
  73       * Registered transport classes
  74       *
  75       * @var array
  76       */
  77      protected static $transports = array();
  78  
  79      /**
  80       * Selected transport name
  81       *
  82       * Use {@see get_transport()} instead
  83       *
  84       * @var string|null
  85       */
  86      public static $transport = null;
  87  
  88      /**
  89       * This is a static class, do not instantiate it
  90       *
  91       * @codeCoverageIgnore
  92       */
  93  	private function __construct() {}
  94  
  95      /**
  96       * Autoloader for Requests
  97       *
  98       * Register this with {@see register_autoloader()} if you'd like to avoid
  99       * having to create your own.
 100       *
 101       * (You can also use `spl_autoload_register` directly if you'd prefer.)
 102       *
 103       * @codeCoverageIgnore
 104       *
 105       * @param string $class Class name to load
 106       */
 107  	public static function autoloader($class) {
 108          // Check that the class starts with "Requests"
 109          if (strpos($class, 'Requests') !== 0) {
 110              return;
 111          }
 112  
 113          $file = str_replace('_', '/', $class);
 114          if (file_exists(dirname(__FILE__) . '/' . $file . '.php')) {
 115              require_once(dirname(__FILE__) . '/' . $file . '.php');
 116          }
 117      }
 118  
 119      /**
 120       * Register the built-in autoloader
 121       *
 122       * @codeCoverageIgnore
 123       */
 124  	public static function register_autoloader() {
 125          spl_autoload_register(array('Requests', 'autoloader'));
 126      }
 127  
 128      /**
 129       * Register a transport
 130       *
 131       * @param string $transport Transport class to add, must support the Requests_Transport interface
 132       */
 133  	public static function add_transport($transport) {
 134          if (empty(self::$transports)) {
 135              self::$transports = array(
 136                  'Requests_Transport_cURL',
 137                  'Requests_Transport_fsockopen',
 138              );
 139          }
 140  
 141          self::$transports = array_merge(self::$transports, array($transport));
 142      }
 143  
 144      /**
 145       * Get a working transport
 146       *
 147       * @throws Requests_Exception If no valid transport is found (`notransport`)
 148       * @return Requests_Transport
 149       */
 150  	protected static function get_transport() {
 151          // Caching code, don't bother testing coverage
 152          // @codeCoverageIgnoreStart
 153          if (self::$transport !== null) {
 154              return new self::$transport();
 155          }
 156          // @codeCoverageIgnoreEnd
 157  
 158          if (empty(self::$transports)) {
 159              self::$transports = array(
 160                  'Requests_Transport_cURL',
 161                  'Requests_Transport_fsockopen',
 162              );
 163          }
 164  
 165          // Find us a working transport
 166          foreach (self::$transports as $class) {
 167              if (!class_exists($class))
 168                  continue;
 169  
 170              $result = call_user_func(array($class, 'test'));
 171              if ($result) {
 172                  self::$transport = $class;
 173                  break;
 174              }
 175          }
 176          if (self::$transport === null) {
 177              throw new Requests_Exception('No working transports found', 'notransport', self::$transports);
 178          }
 179  
 180          return new self::$transport();
 181      }
 182  
 183      /**#@+
 184       * @see request()
 185       * @param string $url
 186       * @param array $headers
 187       * @param array $options
 188       * @return Requests_Response
 189       */
 190      /**
 191       * Send a GET request
 192       */
 193  	public static function get($url, $headers = array(), $options = array()) {
 194          return self::request($url, $headers, null, self::GET, $options);
 195      }
 196  
 197      /**
 198       * Send a HEAD request
 199       */
 200  	public static function head($url, $headers = array(), $options = array()) {
 201          return self::request($url, $headers, null, self::HEAD, $options);
 202      }
 203  
 204      /**
 205       * Send a DELETE request
 206       */
 207  	public static function delete($url, $headers = array(), $options = array()) {
 208          return self::request($url, $headers, null, self::DELETE, $options);
 209      }
 210      /**#@-*/
 211  
 212      /**#@+
 213       * @see request()
 214       * @param string $url
 215       * @param array $headers
 216       * @param array $data
 217       * @param array $options
 218       * @return Requests_Response
 219       */
 220      /**
 221       * Send a POST request
 222       */
 223  	public static function post($url, $headers = array(), $data = array(), $options = array()) {
 224          return self::request($url, $headers, $data, self::POST, $options);
 225      }
 226      /**
 227       * Send a PUT request
 228       */
 229  	public static function put($url, $headers = array(), $data = array(), $options = array()) {
 230          return self::request($url, $headers, $data, self::PUT, $options);
 231      }
 232  
 233      /**
 234       * Send a PATCH request
 235       *
 236       * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the
 237       * specification recommends that should send an ETag
 238       *
 239       * @link http://tools.ietf.org/html/rfc5789
 240       */
 241  	public static function patch($url, $headers, $data = array(), $options = array()) {
 242          return self::request($url, $headers, $data, self::PATCH, $options);
 243      }
 244      /**#@-*/
 245  
 246      /**
 247       * Main interface for HTTP requests
 248       *
 249       * This method initiates a request and sends it via a transport before
 250       * parsing.
 251       *
 252       * The `$options` parameter takes an associative array with the following
 253       * options:
 254       *
 255       * - `timeout`: How long should we wait for a response?
 256       *    (integer, seconds, default: 10)
 257       * - `useragent`: Useragent to send to the server
 258       *    (string, default: php-requests/$version)
 259       * - `follow_redirects`: Should we follow 3xx redirects?
 260       *    (boolean, default: true)
 261       * - `redirects`: How many times should we redirect before erroring?
 262       *    (integer, default: 10)
 263       * - `blocking`: Should we block processing on this request?
 264       *    (boolean, default: true)
 265       * - `filename`: File to stream the body to instead.
 266       *    (string|boolean, default: false)
 267       * - `auth`: Authentication handler or array of user/password details to use
 268       *    for Basic authentication
 269       *    (Requests_Auth|array|boolean, default: false)
 270       * - `proxy`: Proxy details to use for proxy by-passing and authentication
 271       *    (Requests_Proxy|array|boolean, default: false)
 272       * - `idn`: Enable IDN parsing
 273       *    (boolean, default: true)
 274       * - `transport`: Custom transport. Either a class name, or a
 275       *    transport object. Defaults to the first working transport from
 276       *    {@see getTransport()}
 277       *    (string|Requests_Transport, default: {@see getTransport()})
 278       * - `hooks`: Hooks handler.
 279       *    (Requests_Hooker, default: new Requests_Hooks())
 280       * - `verify`: Should we verify SSL certificates? Allows passing in a custom
 281       *    certificate file as a string. (Using true uses the system-wide root
 282       *    certificate store instead, but this may have different behaviour
 283       *    across transports.)
 284       *    (string|boolean, default: library/Requests/Transport/cacert.pem)
 285       * - `verifyname`: Should we verify the common name in the SSL certificate?
 286       *    (boolean: default, true)
 287       *
 288       * @throws Requests_Exception On invalid URLs (`nonhttp`)
 289       *
 290       * @param string $url URL to request
 291       * @param array $headers Extra headers to send with the request
 292       * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
 293       * @param string $type HTTP request type (use Requests constants)
 294       * @param array $options Options for the request (see description for more information)
 295       * @return Requests_Response
 296       */
 297  	public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array()) {
 298          if (empty($options['type'])) {
 299              $options['type'] = $type;
 300          }
 301          $options = array_merge(self::get_default_options(), $options);
 302  
 303          self::set_defaults($url, $headers, $data, $type, $options);
 304  
 305          $options['hooks']->dispatch('requests.before_request', array(&$url, &$headers, &$data, &$type, &$options));
 306  
 307          if (!empty($options['transport'])) {
 308              $transport = $options['transport'];
 309  
 310              if (is_string($options['transport'])) {
 311                  $transport = new $transport();
 312              }
 313          }
 314          else {
 315              $transport = self::get_transport();
 316          }
 317          $response = $transport->request($url, $headers, $data, $options);
 318  
 319          $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options));
 320  
 321          return self::parse_response($response, $url, $headers, $data, $options);
 322      }
 323  
 324      /**
 325       * Send multiple HTTP requests simultaneously
 326       *
 327       * The `$requests` parameter takes an associative or indexed array of
 328       * request fields. The key of each request can be used to match up the
 329       * request with the returned data, or with the request passed into your
 330       * `multiple.request.complete` callback.
 331       *
 332       * The request fields value is an associative array with the following keys:
 333       *
 334       * - `url`: Request URL Same as the `$url` parameter to
 335       *    {@see Requests::request}
 336       *    (string, required)
 337       * - `headers`: Associative array of header fields. Same as the `$headers`
 338       *    parameter to {@see Requests::request}
 339       *    (array, default: `array()`)
 340       * - `data`: Associative array of data fields or a string. Same as the
 341       *    `$data` parameter to {@see Requests::request}
 342       *    (array|string, default: `array()`)
 343       * - `type`: HTTP request type (use Requests constants). Same as the `$type`
 344       *    parameter to {@see Requests::request}
 345       *    (string, default: `Requests::GET`)
 346       * - `data`: Associative array of options. Same as the `$options` parameter
 347       *    to {@see Requests::request}
 348       *    (array, default: see {@see Requests::request})
 349       * - `cookies`: Associative array of cookie name to value, or cookie jar.
 350       *    (array|Requests_Cookie_Jar)
 351       *
 352       * If the `$options` parameter is specified, individual requests will
 353       * inherit options from it. This can be used to use a single hooking system,
 354       * or set all the types to `Requests::POST`, for example.
 355       *
 356       * In addition, the `$options` parameter takes the following global options:
 357       *
 358       * - `complete`: A callback for when a request is complete. Takes two
 359       *    parameters, a Requests_Response/Requests_Exception reference, and the
 360       *    ID from the request array (Note: this can also be overridden on a
 361       *    per-request basis, although that's a little silly)
 362       *    (callback)
 363       *
 364       * @param array $requests Requests data (see description for more information)
 365       * @param array $options Global and default options (see {@see Requests::request})
 366       * @return array Responses (either Requests_Response or a Requests_Exception object)
 367       */
 368  	public static function request_multiple($requests, $options = array()) {
 369          $options = array_merge(self::get_default_options(true), $options);
 370  
 371          if (!empty($options['hooks'])) {
 372              $options['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
 373              if (!empty($options['complete'])) {
 374                  $options['hooks']->register('multiple.request.complete', $options['complete']);
 375              }
 376          }
 377  
 378          foreach ($requests as $id => &$request) {
 379              if (!isset($request['headers'])) {
 380                  $request['headers'] = array();
 381              }
 382              if (!isset($request['data'])) {
 383                  $request['data'] = array();
 384              }
 385              if (!isset($request['type'])) {
 386                  $request['type'] = self::GET;
 387              }
 388              if (!isset($request['options'])) {
 389                  $request['options'] = $options;
 390                  $request['options']['type'] = $request['type'];
 391              }
 392              else {
 393                  if (empty($request['options']['type'])) {
 394                      $request['options']['type'] = $request['type'];
 395                  }
 396                  $request['options'] = array_merge($options, $request['options']);
 397              }
 398  
 399              self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']);
 400  
 401              // Ensure we only hook in once
 402              if ($request['options']['hooks'] !== $options['hooks']) {
 403                  $request['options']['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
 404                  if (!empty($request['options']['complete'])) {
 405                      $request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']);
 406                  }
 407              }
 408          }
 409          unset($request);
 410  
 411          if (!empty($options['transport'])) {
 412              $transport = $options['transport'];
 413  
 414              if (is_string($options['transport'])) {
 415                  $transport = new $transport();
 416              }
 417          }
 418          else {
 419              $transport = self::get_transport();
 420          }
 421          $responses = $transport->request_multiple($requests, $options);
 422  
 423          foreach ($responses as $id => &$response) {
 424              // If our hook got messed with somehow, ensure we end up with the
 425              // correct response
 426              if (is_string($response)) {
 427                  $request = $requests[$id];
 428                  self::parse_multiple($response, $request);
 429                  $request['options']['hooks']->dispatch('multiple.request.complete', array(&$response, $id));
 430              }
 431          }
 432  
 433          return $responses;
 434      }
 435  
 436      /**
 437       * Get the default options
 438       *
 439       * @see Requests::request() for values returned by this method
 440       * @param boolean $multirequest Is this a multirequest?
 441       * @return array Default option values
 442       */
 443  	protected static function get_default_options($multirequest = false) {
 444          $defaults = array(
 445              'timeout' => 10,
 446              'useragent' => 'php-requests/' . self::VERSION,
 447              'redirected' => 0,
 448              'redirects' => 10,
 449              'follow_redirects' => true,
 450              'blocking' => true,
 451              'type' => self::GET,
 452              'filename' => false,
 453              'auth' => false,
 454              'proxy' => false,
 455              'cookies' => false,
 456              'idn' => true,
 457              'hooks' => null,
 458              'transport' => null,
 459              'verify' => dirname( __FILE__ ) . '/Requests/Transport/cacert.pem',
 460              'verifyname' => true,
 461          );
 462          if ($multirequest !== false) {
 463              $defaults['complete'] = null;
 464          }
 465          return $defaults;
 466      }
 467  
 468      /**
 469       * Set the default values
 470       *
 471       * @param string $url URL to request
 472       * @param array $headers Extra headers to send with the request
 473       * @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
 474       * @param string $type HTTP request type
 475       * @param array $options Options for the request
 476       * @return array $options
 477       */
 478  	protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) {
 479          if (!preg_match('/^http(s)?:\/\//i', $url)) {
 480              throw new Requests_Exception('Only HTTP requests are handled.', 'nonhttp', $url);
 481          }
 482  
 483          if (empty($options['hooks'])) {
 484              $options['hooks'] = new Requests_Hooks();
 485          }
 486  
 487          if (is_array($options['auth'])) {
 488              $options['auth'] = new Requests_Auth_Basic($options['auth']);
 489          }
 490          if ($options['auth'] !== false) {
 491              $options['auth']->register($options['hooks']);
 492          }
 493  
 494          if (!empty($options['proxy'])) {
 495              $options['proxy'] = new Requests_Proxy_HTTP($options['proxy']);
 496          }
 497          if ($options['proxy'] !== false) {
 498              $options['proxy']->register($options['hooks']);
 499          }
 500  
 501          if (is_array($options['cookies'])) {
 502              $options['cookies'] = new Requests_Cookie_Jar($options['cookies']);
 503          }
 504          elseif (empty($options['cookies'])) {
 505              $options['cookies'] = new Requests_Cookie_Jar();
 506          }
 507          if ($options['cookies'] !== false) {
 508              $options['cookies']->register($options['hooks']);
 509          }
 510  
 511          if ($options['idn'] !== false) {
 512              $iri = new Requests_IRI($url);
 513              $iri->host = Requests_IDNAEncoder::encode($iri->ihost);
 514              $url = $iri->uri;
 515          }
 516      }
 517  
 518      /**
 519       * HTTP response parser
 520       *
 521       * @throws Requests_Exception On missing head/body separator (`requests.no_crlf_separator`)
 522       * @throws Requests_Exception On missing head/body separator (`noversion`)
 523       * @throws Requests_Exception On missing head/body separator (`toomanyredirects`)
 524       *
 525       * @param string $headers Full response text including headers and body
 526       * @param string $url Original request URL
 527       * @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects
 528       * @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects
 529       * @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects
 530       * @return Requests_Response
 531       */
 532  	protected static function parse_response($headers, $url, $req_headers, $req_data, $options) {
 533          $return = new Requests_Response();
 534          if (!$options['blocking']) {
 535              return $return;
 536          }
 537  
 538          $return->raw = $headers;
 539          $return->url = $url;
 540  
 541          if (!$options['filename']) {
 542              if (($pos = strpos($headers, "\r\n\r\n")) === false) {
 543                  // Crap!
 544                  throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator');
 545              }
 546  
 547              $headers = substr($return->raw, 0, $pos);
 548              $return->body = substr($return->raw, $pos + strlen("\n\r\n\r"));
 549          }
 550          else {
 551              $return->body = '';
 552          }
 553          // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
 554          $headers = str_replace("\r\n", "\n", $headers);
 555          // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
 556          $headers = preg_replace('/\n[ \t]/', ' ', $headers);
 557          $headers = explode("\n", $headers);
 558          preg_match('#^HTTP/1\.\d[ \t]+(\d+)#i', array_shift($headers), $matches);
 559          if (empty($matches)) {
 560              throw new Requests_Exception('Response could not be parsed', 'noversion', $headers);
 561          }
 562          $return->status_code = (int) $matches[1];
 563          if ($return->status_code >= 200 && $return->status_code < 300) {
 564              $return->success = true;
 565          }
 566  
 567          foreach ($headers as $header) {
 568              list($key, $value) = explode(':', $header, 2);
 569              $value = trim($value);
 570              preg_replace('#(\s+)#i', ' ', $value);
 571              $return->headers[$key] = $value;
 572          }
 573          if (isset($return->headers['transfer-encoding'])) {
 574              $return->body = self::decode_chunked($return->body);
 575              unset($return->headers['transfer-encoding']);
 576          }
 577          if (isset($return->headers['content-encoding'])) {
 578              $return->body = self::decompress($return->body);
 579          }
 580  
 581          //fsockopen and cURL compatibility
 582          if (isset($return->headers['connection'])) {
 583              unset($return->headers['connection']);
 584          }
 585  
 586          $options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options));
 587  
 588          if ((in_array($return->status_code, array(300, 301, 302, 303, 307)) || $return->status_code > 307 && $return->status_code < 400) && $options['follow_redirects'] === true) {
 589              if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
 590                  if ($return->status_code === 303) {
 591                      $options['type'] = Requests::GET;
 592                  }
 593                  $options['redirected']++;
 594                  $location = $return->headers['location'];
 595                  if (strpos ($location, '/') === 0) {
 596                      // relative redirect, for compatibility make it absolute
 597                      $location = Requests_IRI::absolutize($url, $location);
 598                      $location = $location->uri;
 599                  }
 600                  $redirected = self::request($location, $req_headers, $req_data, false, $options);
 601                  $redirected->history[] = $return;
 602                  return $redirected;
 603              }
 604              elseif ($options['redirected'] >= $options['redirects']) {
 605                  throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return);
 606              }
 607          }
 608  
 609          $return->redirects = $options['redirected'];
 610  
 611          $options['hooks']->dispatch('requests.after_request', array(&$return, $req_headers, $req_data, $options));
 612          return $return;
 613      }
 614  
 615      /**
 616       * Callback for `transport.internal.parse_response`
 617       *
 618       * Internal use only. Converts a raw HTTP response to a Requests_Response
 619       * while still executing a multiple request.
 620       *
 621       * @param string $headers Full response text including headers and body
 622       * @param array $request Request data as passed into {@see Requests::request_multiple()}
 623       * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object
 624       */
 625  	public static function parse_multiple(&$response, $request) {
 626          try {
 627              $response = self::parse_response($response, $request['url'], $request['headers'], $request['data'], $request['options']);
 628          }
 629          catch (Requests_Exception $e) {
 630              $response = $e;
 631          }
 632      }
 633  
 634      /**
 635       * Decoded a chunked body as per RFC 2616
 636       *
 637       * @see http://tools.ietf.org/html/rfc2616#section-3.6.1
 638       * @param string $data Chunked body
 639       * @return string Decoded body
 640       */
 641  	protected static function decode_chunked($data) {
 642          if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($data))) {
 643              return $data;
 644          }
 645  
 646          $decoded = '';
 647          $encoded = $data;
 648  
 649          while (true) {
 650              $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
 651              if (!$is_chunked) {
 652                  // Looks like it's not chunked after all
 653                  return $data;
 654              }
 655  
 656              $length = hexdec(trim($matches[1]));
 657              if ($length === 0) {
 658                  // Ignore trailer headers
 659                  return $decoded;
 660              }
 661  
 662              $chunk_length = strlen($matches[0]);
 663              $decoded .= $part = substr($encoded, $chunk_length, $length);
 664              $encoded = substr($encoded, $chunk_length + $length + 2);
 665  
 666              if (trim($encoded) === '0' || empty($encoded)) {
 667                  return $decoded;
 668              }
 669          }
 670  
 671          // We'll never actually get down here
 672          // @codeCoverageIgnoreStart
 673      }
 674      // @codeCoverageIgnoreEnd
 675  
 676      /**
 677       * Convert a key => value array to a 'key: value' array for headers
 678       *
 679       * @param array $array Dictionary of header values
 680       * @return array List of headers
 681       */
 682  	public static function flatten($array) {
 683          $return = array();
 684          foreach ($array as $key => $value) {
 685              $return[] = "$key: $value";
 686          }
 687          return $return;
 688      }
 689  
 690      /**
 691       * Convert a key => value array to a 'key: value' array for headers
 692       *
 693       * @deprecated Misspelling of {@see Requests::flatten}
 694       * @param array $array Dictionary of header values
 695       * @return array List of headers
 696       */
 697  	public static function flattern($array) {
 698          return self::flatten($array);
 699      }
 700  
 701      /**
 702       * Decompress an encoded body
 703       *
 704       * Implements gzip, compress and deflate. Guesses which it is by attempting
 705       * to decode.
 706       *
 707       * @todo Make this smarter by defaulting to whatever the headers say first
 708       * @param string $data Compressed data in one of the above formats
 709       * @return string Decompressed string
 710       */
 711  	public static function decompress($data) {
 712          if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") {
 713              // Not actually compressed. Probably cURL ruining this for us.
 714              return $data;
 715          }
 716  
 717          if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) {
 718              return $decoded;
 719          }
 720          elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) {
 721              return $decoded;
 722          }
 723          elseif (($decoded = self::compatible_gzinflate($data)) !== false) {
 724              return $decoded;
 725          }
 726          elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) {
 727              return $decoded;
 728          }
 729  
 730          return $data;
 731      }
 732  
 733      /**
 734       * Decompression of deflated string while staying compatible with the majority of servers.
 735       *
 736       * Certain Servers will return deflated data with headers which PHP's gzinflate()
 737       * function cannot handle out of the box. The following function has been created from
 738       * various snippets on the gzinflate() PHP documentation.
 739       *
 740       * Warning: Magic numbers within. Due to the potential different formats that the compressed
 741       * data may be returned in, some "magic offsets" are needed to ensure proper decompression
 742       * takes place. For a simple progmatic way to determine the magic offset in use, see:
 743       * http://core.trac.wordpress.org/ticket/18273
 744       *
 745       * @since 2.8.1
 746       * @link http://core.trac.wordpress.org/ticket/18273
 747       * @link http://au2.php.net/manual/en/function.gzinflate.php#70875
 748       * @link http://au2.php.net/manual/en/function.gzinflate.php#77336
 749       *
 750       * @param string $gzData String to decompress.
 751       * @return string|bool False on failure.
 752       */
 753  	public static function compatible_gzinflate($gzData) {
 754          // Compressed data might contain a full zlib header, if so strip it for
 755          // gzinflate()
 756          if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) {
 757              $i = 10;
 758              $flg = ord( substr($gzData, 3, 1) );
 759              if ( $flg > 0 ) {
 760                  if ( $flg & 4 ) {
 761                      list($xlen) = unpack('v', substr($gzData, $i, 2) );
 762                      $i = $i + 2 + $xlen;
 763                  }
 764                  if ( $flg & 8 )
 765                      $i = strpos($gzData, "\0", $i) + 1;
 766                  if ( $flg & 16 )
 767                      $i = strpos($gzData, "\0", $i) + 1;
 768                  if ( $flg & 2 )
 769                      $i = $i + 2;
 770              }
 771              $decompressed = self::compatible_gzinflate( substr( $gzData, $i ) );
 772              if ( false !== $decompressed ) {
 773                  return $decompressed;
 774              }
 775          }
 776  
 777          // If the data is Huffman Encoded, we must first strip the leading 2
 778          // byte Huffman marker for gzinflate()
 779          // The response is Huffman coded by many compressors such as
 780          // java.util.zip.Deflater, Ruby’s Zlib::Deflate, and .NET's
 781          // System.IO.Compression.DeflateStream.
 782          //
 783          // See http://decompres.blogspot.com/ for a quick explanation of this
 784          // data type
 785          $huffman_encoded = false;
 786  
 787          // low nibble of first byte should be 0x08
 788          list( , $first_nibble )    = unpack( 'h', $gzData );
 789  
 790          // First 2 bytes should be divisible by 0x1F
 791          list( , $first_two_bytes ) = unpack( 'n', $gzData );
 792  
 793          if ( 0x08 == $first_nibble && 0 == ( $first_two_bytes % 0x1F ) )
 794              $huffman_encoded = true;
 795  
 796          if ( $huffman_encoded ) {
 797              if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) )
 798                  return $decompressed;
 799          }
 800  
 801          if ( "\x50\x4b\x03\x04" == substr( $gzData, 0, 4 ) ) {
 802              // ZIP file format header
 803              // Offset 6: 2 bytes, General-purpose field
 804              // Offset 26: 2 bytes, filename length
 805              // Offset 28: 2 bytes, optional field length
 806              // Offset 30: Filename field, followed by optional field, followed
 807              // immediately by data
 808              list( , $general_purpose_flag ) = unpack( 'v', substr( $gzData, 6, 2 ) );
 809  
 810              // If the file has been compressed on the fly, 0x08 bit is set of
 811              // the general purpose field. We can use this to differentiate
 812              // between a compressed document, and a ZIP file
 813              $zip_compressed_on_the_fly = ( 0x08 == (0x08 & $general_purpose_flag ) );
 814  
 815              if ( ! $zip_compressed_on_the_fly ) {
 816                  // Don't attempt to decode a compressed zip file
 817                  return $gzData;
 818              }
 819  
 820              // Determine the first byte of data, based on the above ZIP header
 821              // offsets:
 822              $first_file_start = array_sum( unpack( 'v2', substr( $gzData, 26, 4 ) ) );
 823              if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 30 + $first_file_start ) ) ) ) {
 824                  return $decompressed;
 825              }
 826              return false;
 827          }
 828  
 829          // Finally fall back to straight gzinflate
 830          if ( false !== ( $decompressed = @gzinflate( $gzData ) ) ) {
 831              return $decompressed;
 832          }
 833  
 834          // Fallback for all above failing, not expected, but included for
 835          // debugging and preventing regressions and to track stats
 836          if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) ) {
 837              return $decompressed;
 838          }
 839  
 840          return false;
 841      }
 842  
 843  	public static function match_domain($host, $reference) {
 844          // Check for a direct match
 845          if ($host === $reference) {
 846              return true;
 847          }
 848  
 849          // Calculate the valid wildcard match if the host is not an IP address
 850          // Also validates that the host has 3 parts or more, as per Firefox's
 851          // ruleset.
 852          $parts = explode('.', $host);
 853          if (ip2long($host) === false && count($parts) >= 3) {
 854              $parts[0] = '*';
 855              $wildcard = implode('.', $parts);
 856              if ($wildcard === $reference) {
 857                  return true;
 858              }
 859          }
 860  
 861          return false;
 862      }
 863  }

title

Description

title

Description

title

Description

title

title

Body