Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
   1  <?php
   2  
   3  declare(strict_types=1);
   4  /**
   5   * SimplePie
   6   *
   7   * A PHP-Based RSS and Atom Feed Framework.
   8   * Takes the hard work out of managing a complete RSS/Atom solution.
   9   *
  10   * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
  11   * All rights reserved.
  12   *
  13   * Redistribution and use in source and binary forms, with or without modification, are
  14   * permitted provided that the following conditions are met:
  15   *
  16   * 	 * Redistributions of source code must retain the above copyright notice, this list of
  17   * 	   conditions and the following disclaimer.
  18   *
  19   * 	 * Redistributions in binary form must reproduce the above copyright notice, this list
  20   * 	   of conditions and the following disclaimer in the documentation and/or other materials
  21   * 	   provided with the distribution.
  22   *
  23   * 	 * Neither the name of the SimplePie Team nor the names of its contributors may be used
  24   * 	   to endorse or promote products derived from this software without specific prior
  25   * 	   written permission.
  26   *
  27   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  28   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  29   * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
  30   * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  32   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  34   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35   * POSSIBILITY OF SUCH DAMAGE.
  36   *
  37   * @package SimplePie
  38   * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
  39   * @author Ryan Parman
  40   * @author Sam Sneddon
  41   * @author Ryan McCue
  42   * @link http://simplepie.org/ SimplePie
  43   * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  44   */
  45  
  46  namespace SimplePie;
  47  
  48  /**
  49   * Used for fetching remote files and reading local files
  50   *
  51   * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support
  52   *
  53   * This class can be overloaded with {@see \SimplePie\SimplePie::set_file_class()}
  54   *
  55   * @package SimplePie
  56   * @subpackage HTTP
  57   * @todo Move to properly supporting RFC2616 (HTTP/1.1)
  58   */
  59  class File
  60  {
  61      public $url;
  62      public $useragent;
  63      public $success = true;
  64      public $headers = [];
  65      public $body;
  66      public $status_code = 0;
  67      public $redirects = 0;
  68      public $error;
  69      public $method = \SimplePie\SimplePie::FILE_SOURCE_NONE;
  70      public $permanent_url;
  71  
  72      public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $curl_options = [])
  73      {
  74          if (class_exists('idna_convert')) {
  75              $idn = new \idna_convert();
  76              $parsed = \SimplePie\Misc::parse_url($url);
  77              $url = \SimplePie\Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], null);
  78          }
  79          $this->url = $url;
  80          $this->permanent_url = $url;
  81          $this->useragent = $useragent;
  82          if (preg_match('/^http(s)?:\/\//i', $url)) {
  83              if ($useragent === null) {
  84                  $useragent = ini_get('user_agent');
  85                  $this->useragent = $useragent;
  86              }
  87              if (!is_array($headers)) {
  88                  $headers = [];
  89              }
  90              if (!$force_fsockopen && function_exists('curl_exec')) {
  91                  $this->method = \SimplePie\SimplePie::FILE_SOURCE_REMOTE | \SimplePie\SimplePie::FILE_SOURCE_CURL;
  92                  $fp = curl_init();
  93                  $headers2 = [];
  94                  foreach ($headers as $key => $value) {
  95                      $headers2[] = "$key: $value";
  96                  }
  97                  if (version_compare(\SimplePie\Misc::get_curl_version(), '7.10.5', '>=')) {
  98                      curl_setopt($fp, CURLOPT_ENCODING, '');
  99                  }
 100                  curl_setopt($fp, CURLOPT_URL, $url);
 101                  curl_setopt($fp, CURLOPT_HEADER, 1);
 102                  curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1);
 103                  curl_setopt($fp, CURLOPT_FAILONERROR, 1);
 104                  curl_setopt($fp, CURLOPT_TIMEOUT, $timeout);
 105                  curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout);
 106                  curl_setopt($fp, CURLOPT_REFERER, \SimplePie\Misc::url_remove_credentials($url));
 107                  curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
 108                  curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
 109                  foreach ($curl_options as $curl_param => $curl_value) {
 110                      curl_setopt($fp, $curl_param, $curl_value);
 111                  }
 112  
 113                  $this->headers = curl_exec($fp);
 114                  if (curl_errno($fp) === 23 || curl_errno($fp) === 61) {
 115                      curl_setopt($fp, CURLOPT_ENCODING, 'none');
 116                      $this->headers = curl_exec($fp);
 117                  }
 118                  $this->status_code = curl_getinfo($fp, CURLINFO_HTTP_CODE);
 119                  if (curl_errno($fp)) {
 120                      $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp);
 121                      $this->success = false;
 122                  } else {
 123                      // Use the updated url provided by curl_getinfo after any redirects.
 124                      if ($info = curl_getinfo($fp)) {
 125                          $this->url = $info['url'];
 126                      }
 127                      curl_close($fp);
 128                      $this->headers = \SimplePie\HTTP\Parser::prepareHeaders($this->headers, $info['redirect_count'] + 1);
 129                      $parser = new \SimplePie\HTTP\Parser($this->headers);
 130                      if ($parser->parse()) {
 131                          $this->headers = $parser->headers;
 132                          $this->body = trim($parser->body);
 133                          $this->status_code = $parser->status_code;
 134                          if ((in_array($this->status_code, [300, 301, 302, 303, 307]) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) {
 135                              $this->redirects++;
 136                              $location = \SimplePie\Misc::absolutize_url($this->headers['location'], $url);
 137                              $previousStatusCode = $this->status_code;
 138                              $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
 139                              $this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
 140                              return;
 141                          }
 142                      }
 143                  }
 144              } else {
 145                  $this->method = \SimplePie\SimplePie::FILE_SOURCE_REMOTE | \SimplePie\SimplePie::FILE_SOURCE_FSOCKOPEN;
 146                  $url_parts = parse_url($url);
 147                  $socket_host = $url_parts['host'];
 148                  if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
 149                      $socket_host = "ssl://$url_parts[host]";
 150                      $url_parts['port'] = 443;
 151                  }
 152                  if (!isset($url_parts['port'])) {
 153                      $url_parts['port'] = 80;
 154                  }
 155                  $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout);
 156                  if (!$fp) {
 157                      $this->error = 'fsockopen error: ' . $errstr;
 158                      $this->success = false;
 159                  } else {
 160                      stream_set_timeout($fp, $timeout);
 161                      if (isset($url_parts['path'])) {
 162                          if (isset($url_parts['query'])) {
 163                              $get = "$url_parts[path]?$url_parts[query]";
 164                          } else {
 165                              $get = $url_parts['path'];
 166                          }
 167                      } else {
 168                          $get = '/';
 169                      }
 170                      $out = "GET $get HTTP/1.1\r\n";
 171                      $out .= "Host: $url_parts[host]\r\n";
 172                      $out .= "User-Agent: $useragent\r\n";
 173                      if (extension_loaded('zlib')) {
 174                          $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
 175                      }
 176  
 177                      if (isset($url_parts['user']) && isset($url_parts['pass'])) {
 178                          $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n";
 179                      }
 180                      foreach ($headers as $key => $value) {
 181                          $out .= "$key: $value\r\n";
 182                      }
 183                      $out .= "Connection: Close\r\n\r\n";
 184                      fwrite($fp, $out);
 185  
 186                      $info = stream_get_meta_data($fp);
 187  
 188                      $this->headers = '';
 189                      while (!$info['eof'] && !$info['timed_out']) {
 190                          $this->headers .= fread($fp, 1160);
 191                          $info = stream_get_meta_data($fp);
 192                      }
 193                      if (!$info['timed_out']) {
 194                          $parser = new \SimplePie\HTTP\Parser($this->headers);
 195                          if ($parser->parse()) {
 196                              $this->headers = $parser->headers;
 197                              $this->body = $parser->body;
 198                              $this->status_code = $parser->status_code;
 199                              if ((in_array($this->status_code, [300, 301, 302, 303, 307]) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) {
 200                                  $this->redirects++;
 201                                  $location = \SimplePie\Misc::absolutize_url($this->headers['location'], $url);
 202                                  $previousStatusCode = $this->status_code;
 203                                  $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
 204                                  $this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
 205                                  return;
 206                              }
 207                              if (isset($this->headers['content-encoding'])) {
 208                                  // Hey, we act dumb elsewhere, so let's do that here too
 209                                  switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) {
 210                                      case 'gzip':
 211                                      case 'x-gzip':
 212                                          $decoder = new \SimplePie\Gzdecode($this->body);
 213                                          if (!$decoder->parse()) {
 214                                              $this->error = 'Unable to decode HTTP "gzip" stream';
 215                                              $this->success = false;
 216                                          } else {
 217                                              $this->body = trim($decoder->data);
 218                                          }
 219                                          break;
 220  
 221                                      case 'deflate':
 222                                          if (($decompressed = gzinflate($this->body)) !== false) {
 223                                              $this->body = $decompressed;
 224                                          } elseif (($decompressed = gzuncompress($this->body)) !== false) {
 225                                              $this->body = $decompressed;
 226                                          } elseif (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false) {
 227                                              $this->body = $decompressed;
 228                                          } else {
 229                                              $this->error = 'Unable to decode HTTP "deflate" stream';
 230                                              $this->success = false;
 231                                          }
 232                                          break;
 233  
 234                                      default:
 235                                          $this->error = 'Unknown content coding';
 236                                          $this->success = false;
 237                                  }
 238                              }
 239                          }
 240                      } else {
 241                          $this->error = 'fsocket timed out';
 242                          $this->success = false;
 243                      }
 244                      fclose($fp);
 245                  }
 246              }
 247          } else {
 248              $this->method = \SimplePie\SimplePie::FILE_SOURCE_LOCAL | \SimplePie\SimplePie::FILE_SOURCE_FILE_GET_CONTENTS;
 249              if (empty($url) || !($this->body = trim(file_get_contents($url)))) {
 250                  $this->error = 'file_get_contents could not read the file';
 251                  $this->success = false;
 252              }
 253          }
 254      }
 255  }
 256  
 257  class_alias('SimplePie\File', 'SimplePie_File');