Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  /**
   3   * SimplePie
   4   *
   5   * A PHP-Based RSS and Atom Feed Framework.
   6   * Takes the hard work out of managing a complete RSS/Atom solution.
   7   *
   8   * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
   9   * All rights reserved.
  10   *
  11   * Redistribution and use in source and binary forms, with or without modification, are
  12   * permitted provided that the following conditions are met:
  13   *
  14   * 	 * Redistributions of source code must retain the above copyright notice, this list of
  15   * 	   conditions and the following disclaimer.
  16   *
  17   * 	 * Redistributions in binary form must reproduce the above copyright notice, this list
  18   * 	   of conditions and the following disclaimer in the documentation and/or other materials
  19   * 	   provided with the distribution.
  20   *
  21   * 	 * Neither the name of the SimplePie Team nor the names of its contributors may be used
  22   * 	   to endorse or promote products derived from this software without specific prior
  23   * 	   written permission.
  24   *
  25   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  26   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  27   * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
  28   * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  29   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  30   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  31   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  32   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33   * POSSIBILITY OF SUCH DAMAGE.
  34   *
  35   * @package SimplePie
  36   * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
  37   * @author Ryan Parman
  38   * @author Geoffrey Sneddon
  39   * @author Ryan McCue
  40   * @link http://simplepie.org/ SimplePie
  41   * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  42   */
  43  
  44  /**
  45   * Used for feed auto-discovery
  46   *
  47   *
  48   * This class can be overloaded with {@see SimplePie::set_locator_class()}
  49   *
  50   * @package SimplePie
  51   */
  52  class SimplePie_Locator
  53  {
  54  	 var $useragent;
  55  	 var $timeout;
  56  	 var $file;
  57  	 var $local = array();
  58  	 var $elsewhere = array();
  59  	 var $cached_entities = array();
  60  	 var $http_base;
  61  	 var $base;
  62  	 var $base_location = 0;
  63  	 var $checked_feeds = 0;
  64  	 var $max_checked_feeds = 10;
  65  	 var $force_fsockopen = false;
  66  	 var $curl_options = array();
  67  	 protected $registry;
  68  
  69  	public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10, $force_fsockopen = false, $curl_options = array())
  70  	 {
  71  	 	 $this->file = $file;
  72  	 	 $this->useragent = $useragent;
  73  	 	 $this->timeout = $timeout;
  74  	 	 $this->max_checked_feeds = $max_checked_feeds;
  75  	 	 $this->force_fsockopen = $force_fsockopen;
  76  	 	 $this->curl_options = $curl_options;
  77  
  78  	 	 if (class_exists('DOMDocument'))
  79  	 	 {
  80  	 	 	 $this->dom = new DOMDocument();
  81  
  82  	 	 	 set_error_handler(array('SimplePie_Misc', 'silence_errors'));
  83  	 	 	 $this->dom->loadHTML($this->file->body);
  84  	 	 	 restore_error_handler();
  85  	 	 }
  86  	 	 else
  87  	 	 {
  88  	 	 	 $this->dom = null;
  89  	 	 }
  90  	 }
  91  
  92  	public function set_registry(SimplePie_Registry $registry)
  93  	 {
  94  	 	 $this->registry = $registry;
  95  	 }
  96  
  97  	public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
  98  	 {
  99  	 	 if ($this->is_feed($this->file))
 100  	 	 {
 101  	 	 	 return $this->file;
 102  	 	 }
 103  
 104  	 	 if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
 105  	 	 {
 106  	 	 	 $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file));
 107  	 	 	 if ($sniffer->get_type() !== 'text/html')
 108  	 	 	 {
 109  	 	 	 	 return null;
 110  	 	 	 }
 111  	 	 }
 112  
 113  	 	 if ($type & ~SIMPLEPIE_LOCATOR_NONE)
 114  	 	 {
 115  	 	 	 $this->get_base();
 116  	 	 }
 117  
 118  	 	 if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery())
 119  	 	 {
 120  	 	 	 return $working[0];
 121  	 	 }
 122  
 123  	 	 if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links())
 124  	 	 {
 125  	 	 	 if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local))
 126  	 	 	 {
 127  	 	 	 	 return $working[0];
 128  	 	 	 }
 129  
 130  	 	 	 if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local))
 131  	 	 	 {
 132  	 	 	 	 return $working[0];
 133  	 	 	 }
 134  
 135  	 	 	 if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere))
 136  	 	 	 {
 137  	 	 	 	 return $working[0];
 138  	 	 	 }
 139  
 140  	 	 	 if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere))
 141  	 	 	 {
 142  	 	 	 	 return $working[0];
 143  	 	 	 }
 144  	 	 }
 145  	 	 return null;
 146  	 }
 147  
 148  	public function is_feed($file, $check_html = false)
 149  	 {
 150  	 	 if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE)
 151  	 	 {
 152  	 	 	 $sniffer = $this->registry->create('Content_Type_Sniffer', array($file));
 153  	 	 	 $sniffed = $sniffer->get_type();
 154  	 	 	 $mime_types = array('application/rss+xml', 'application/rdf+xml',
 155  	 	 	                     'text/rdf', 'application/atom+xml', 'text/xml',
 156  	 	 	                     'application/xml', 'application/x-rss+xml');
 157  	 	 	 if ($check_html)
 158  	 	 	 {
 159  	 	 	 	 $mime_types[] = 'text/html';
 160  	 	 	 }
 161  
 162  	 	 	 return in_array($sniffed, $mime_types);
 163  	 	 }
 164  	 	 elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL)
 165  	 	 {
 166  	 	 	 return true;
 167  	 	 }
 168  	 	 else
 169  	 	 {
 170  	 	 	 return false;
 171  	 	 }
 172  	 }
 173  
 174  	public function get_base()
 175  	 {
 176  	 	 if ($this->dom === null)
 177  	 	 {
 178  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
 179  	 	 }
 180  	 	 $this->http_base = $this->file->url;
 181  	 	 $this->base = $this->http_base;
 182  	 	 $elements = $this->dom->getElementsByTagName('base');
 183  	 	 foreach ($elements as $element)
 184  	 	 {
 185  	 	 	 if ($element->hasAttribute('href'))
 186  	 	 	 {
 187  	 	 	 	 $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base));
 188  	 	 	 	 if ($base === false)
 189  	 	 	 	 {
 190  	 	 	 	 	 continue;
 191  	 	 	 	 }
 192  	 	 	 	 $this->base = $base;
 193  	 	 	 	 $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0;
 194  	 	 	 	 break;
 195  	 	 	 }
 196  	 	 }
 197  	 }
 198  
 199  	public function autodiscovery()
 200  	 {
 201  	 	 $done = array();
 202  	 	 $feeds = array();
 203  	 	 $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds));
 204  	 	 $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds));
 205  	 	 $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds));
 206  
 207  	 	 if (!empty($feeds))
 208  	 	 {
 209  	 	 	 return array_values($feeds);
 210  	 	 }
 211  
 212  	 	 return null;
 213  	 }
 214  
 215  	protected function search_elements_by_tag($name, &$done, $feeds)
 216  	 {
 217  	 	 if ($this->dom === null)
 218  	 	 {
 219  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
 220  	 	 }
 221  
 222  	 	 $links = $this->dom->getElementsByTagName($name);
 223  	 	 foreach ($links as $link)
 224  	 	 {
 225  	 	 	 if ($this->checked_feeds === $this->max_checked_feeds)
 226  	 	 	 {
 227  	 	 	 	 break;
 228  	 	 	 }
 229  	 	 	 if ($link->hasAttribute('href') && $link->hasAttribute('rel'))
 230  	 	 	 {
 231  	 	 	 	 $rel = array_unique($this->registry->call('Misc', 'space_separated_tokens', array(strtolower($link->getAttribute('rel')))));
 232  	 	 	 	 $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1;
 233  
 234  	 	 	 	 if ($this->base_location < $line)
 235  	 	 	 	 {
 236  	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
 237  	 	 	 	 }
 238  	 	 	 	 else
 239  	 	 	 	 {
 240  	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
 241  	 	 	 	 }
 242  	 	 	 	 if ($href === false)
 243  	 	 	 	 {
 244  	 	 	 	 	 continue;
 245  	 	 	 	 }
 246  
 247  	 	 	 	 if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('text/html', 'application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href]))
 248  	 	 	 	 {
 249  	 	 	 	 	 $this->checked_feeds++;
 250  	 	 	 	 	 $headers = array(
 251  	 	 	 	 	 	 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
 252  	 	 	 	 	 );
 253  	 	 	 	 	 $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
 254  	 	 	 	 	 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed, true))
 255  	 	 	 	 	 {
 256  	 	 	 	 	 	 $feeds[$href] = $feed;
 257  	 	 	 	 	 }
 258  	 	 	 	 }
 259  	 	 	 	 $done[] = $href;
 260  	 	 	 }
 261  	 	 }
 262  
 263  	 	 return $feeds;
 264  	 }
 265  
 266  	 public function get_links()
 267  	 {
 268  	 	 if ($this->dom === null)
 269  	 	 {
 270  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use locator');
 271  	 	 }
 272  
 273  	 	 $links = $this->dom->getElementsByTagName('a');
 274  	 	 foreach ($links as $link)
 275  	 	 {
 276  	 	 	 if ($link->hasAttribute('href'))
 277  	 	 	 {
 278  	 	 	 	 $href = trim($link->getAttribute('href'));
 279  	 	 	 	 $parsed = $this->registry->call('Misc', 'parse_url', array($href));
 280  	 	 	 	 if ($parsed['scheme'] === '' || preg_match('/^(https?|feed)?$/i', $parsed['scheme']))
 281  	 	 	 	 {
 282  	 	 	 	 	 if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo())
 283  	 	 	 	 	 {
 284  	 	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base));
 285  	 	 	 	 	 }
 286  	 	 	 	 	 else
 287  	 	 	 	 	 {
 288  	 	 	 	 	 	 $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base));
 289  	 	 	 	 	 }
 290  	 	 	 	 	 if ($href === false)
 291  	 	 	 	 	 {
 292  	 	 	 	 	 	 continue;
 293  	 	 	 	 	 }
 294  
 295  	 	 	 	 	 $current = $this->registry->call('Misc', 'parse_url', array($this->file->url));
 296  
 297  	 	 	 	 	 if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority'])
 298  	 	 	 	 	 {
 299  	 	 	 	 	 	 $this->local[] = $href;
 300  	 	 	 	 	 }
 301  	 	 	 	 	 else
 302  	 	 	 	 	 {
 303  	 	 	 	 	 	 $this->elsewhere[] = $href;
 304  	 	 	 	 	 }
 305  	 	 	 	 }
 306  	 	 	 }
 307  	 	 }
 308  	 	 $this->local = array_unique($this->local);
 309  	 	 $this->elsewhere = array_unique($this->elsewhere);
 310  	 	 if (!empty($this->local) || !empty($this->elsewhere))
 311  	 	 {
 312  	 	 	 return true;
 313  	 	 }
 314  	 	 return null;
 315  	 }
 316  
 317  	 public function get_rel_link($rel)
 318  	 {
 319  	 	 if ($this->dom === null)
 320  	 	 {
 321  	 	 	 throw new SimplePie_Exception('DOMDocument not found, unable to use '.
 322  	 	 	                               'locator');
 323  	 	 }
 324  	 	 if (!class_exists('DOMXpath'))
 325  	 	 {
 326  	 	 	 throw new SimplePie_Exception('DOMXpath not found, unable to use '.
 327  	 	 	                               'get_rel_link');
 328  	 	 }
 329  
 330  	 	 $xpath = new DOMXpath($this->dom);
 331  	 	 $query = '//a[@rel and @href] | //link[@rel and @href]';
 332  	 	 foreach ($xpath->query($query) as $link)
 333  	 	 {
 334  	 	 	 $href = trim($link->getAttribute('href'));
 335  	 	 	 $parsed = $this->registry->call('Misc', 'parse_url', array($href));
 336  	 	 	 if ($parsed['scheme'] === '' ||
 337  	 	 	     preg_match('/^https?$/i', $parsed['scheme']))
 338  	 	 	 {
 339  	 	 	 	 if (method_exists($link, 'getLineNo') &&
 340  	 	 	 	     $this->base_location < $link->getLineNo())
 341  	 	 	 	 {
 342  	 	 	 	 	 $href =
 343  	 	 	 	 	 	 $this->registry->call('Misc', 'absolutize_url',
 344  	 	 	 	 	 	                       array(trim($link->getAttribute('href')),
 345  	 	 	 	 	 	                             $this->base));
 346  	 	 	 	 }
 347  	 	 	 	 else
 348  	 	 	 	 {
 349  	 	 	 	 	 $href =
 350  	 	 	 	 	 	 $this->registry->call('Misc', 'absolutize_url',
 351  	 	 	 	 	 	                       array(trim($link->getAttribute('href')),
 352  	 	 	 	 	 	                             $this->http_base));
 353  	 	 	 	 }
 354  	 	 	 	 if ($href === false)
 355  	 	 	 	 {
 356  	 	 	 	 	 return null;
 357  	 	 	 	 }
 358  	 	 	 	 $rel_values = explode(' ', strtolower($link->getAttribute('rel')));
 359  	 	 	 	 if (in_array($rel, $rel_values))
 360  	 	 	 	 {
 361  	 	 	 	 	 return $href;
 362  	 	 	 	 }
 363  	 	 	 }
 364  	 	 }
 365  	 	 return null;
 366  	 }
 367  
 368  	 public function extension(&$array)
 369  	 {
 370  	 	 foreach ($array as $key => $value)
 371  	 	 {
 372  	 	 	 if ($this->checked_feeds === $this->max_checked_feeds)
 373  	 	 	 {
 374  	 	 	 	 break;
 375  	 	 	 }
 376  	 	 	 if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml')))
 377  	 	 	 {
 378  	 	 	 	 $this->checked_feeds++;
 379  
 380  	 	 	 	 $headers = array(
 381  	 	 	 	 	 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
 382  	 	 	 	 );
 383  	 	 	 	 $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options));
 384  	 	 	 	 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
 385  	 	 	 	 {
 386  	 	 	 	 	 return array($feed);
 387  	 	 	 	 }
 388  	 	 	 	 else
 389  	 	 	 	 {
 390  	 	 	 	 	 unset($array[$key]);
 391  	 	 	 	 }
 392  	 	 	 }
 393  	 	 }
 394  	 	 return null;
 395  	 }
 396  
 397  	public function body(&$array)
 398  	 {
 399  	 	 foreach ($array as $key => $value)
 400  	 	 {
 401  	 	 	 if ($this->checked_feeds === $this->max_checked_feeds)
 402  	 	 	 {
 403  	 	 	 	 break;
 404  	 	 	 }
 405  	 	 	 if (preg_match('/(feed|rss|rdf|atom|xml)/i', $value))
 406  	 	 	 {
 407  	 	 	 	 $this->checked_feeds++;
 408  	 	 	 	 $headers = array(
 409  	 	 	 	 	 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1',
 410  	 	 	 	 );
 411  	 	 	 	 $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen, $this->curl_options));
 412  	 	 	 	 if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed))
 413  	 	 	 	 {
 414  	 	 	 	 	 return array($feed);
 415  	 	 	 	 }
 416  	 	 	 	 else
 417  	 	 	 	 {
 418  	 	 	 	 	 unset($array[$key]);
 419  	 	 	 	 }
 420  	 	 	 }
 421  	 	 }
 422  	 	 return null;
 423  	 }
 424  }