1<?php
2
3/**
4 * Moodle - Modular Object-Oriented Dynamic Learning Environment
5 *          http://moodle.org
6 * Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 *
21 * @package    moodle
22 * @subpackage lib
23 * @author     Dan Poltawski <talktodan@gmail.com>
24 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 *
26 * Customised version of SimplePie for Moodle
27 */
28
29require_once($CFG->libdir.'/filelib.php');
30
31// PLEASE NOTE: we use the simplepie class _unmodified_
32// through the joys of OO. Distros are free to use their stock
33// version of this file.
34require_once($CFG->libdir.'/simplepie/autoloader.php');
35
36/**
37 * Moodle Customised version of the SimplePie class
38 *
39 * This class extends the stock SimplePie class
40 * in order to make sensible configuration choices,
41 * such as using the Moodle cache directory and
42 * curl functions/proxy config for making http
43 * requests in line with moodle configuration.
44 *
45 * @copyright 2009 Dan Poltawski <talktodan@gmail.com>
46 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
47 * @since     Moodle 2.0
48 */
49class moodle_simplepie extends SimplePie {
50    /**
51     * Constructor - creates an instance of the SimplePie class
52     * with Moodle defaults.
53     *
54     * @param string $feedurl optional URL of the feed
55     * @param int $timeout how many seconds requests should wait for server response
56     */
57    public function __construct($feedurl = null, $timeout = 2) {
58        $cachedir = moodle_simplepie::get_cache_directory();
59        check_dir_exists($cachedir);
60
61        parent::__construct();
62
63        // Use the Moodle class for http requests
64        $this->set_file_class('moodle_simplepie_file');
65
66        // Use html purifier for text cleaning.
67        $this->set_sanitize_class('moodle_simplepie_sanitize');
68        $this->sanitize = new moodle_simplepie_sanitize();
69
70        // Match moodle encoding
71        $this->set_output_encoding('UTF-8');
72
73        // default to a short timeout as most operations will be interactive
74        $this->set_timeout($timeout);
75
76        // 1 hour default cache
77        $this->set_cache_location($cachedir);
78        $this->set_cache_duration(3600);
79
80        // init the feed url if passed in constructor
81        if ($feedurl !== null) {
82            $this->set_feed_url($feedurl);
83            $this->init();
84        }
85    }
86
87    /**
88     * Get path for feed cache directory
89     *
90     * @return string absolute path to cache directory
91     */
92    private static function get_cache_directory() {
93        global $CFG;
94
95        return $CFG->cachedir.'/simplepie/';
96    }
97
98    /**
99     * Reset RSS cache
100     *
101     * @return boolean success if cache clear or didn't exist
102     */
103    public static function reset_cache() {
104
105        $cachedir = moodle_simplepie::get_cache_directory();
106
107        return remove_dir($cachedir);
108    }
109}
110
111/**
112 * Moodle Customised version of the SimplePie_File class
113 *
114 * This class extends the stock SimplePie class
115 * in order to utilise Moodles own curl class for making
116 * http requests. By using the moodle curl class
117 * we ensure that the correct proxy configuration is used.
118 */
119class moodle_simplepie_file extends SimplePie_File {
120
121    /**
122     * The constructor is a copy of the stock simplepie File class which has
123     * been modified to add in use the Moodle curl class rather than php curl
124     * functions.
125     */
126    public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) {
127        $this->url = $url;
128        $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
129
130        $curl = new curl();
131        $curl->setopt( array(
132                'CURLOPT_HEADER' => true,
133                'CURLOPT_TIMEOUT' => $timeout,
134                'CURLOPT_CONNECTTIMEOUT' => $timeout ));
135
136
137        if ($headers !== null) {
138            // translate simplepie headers to those class curl expects
139            foreach($headers as $headername => $headervalue){
140                $headerstr = "{$headername}: {$headervalue}";
141                $curl->setHeader($headerstr);
142            }
143        }
144
145        $this->headers = curl::strip_double_headers($curl->get($url));
146
147        if ($curl->error) {
148            $this->error = 'cURL Error: '.$curl->error;
149            $this->success = false;
150            return false;
151        }
152
153        $parser = new SimplePie_HTTP_Parser($this->headers);
154
155        if ($parser->parse()) {
156            $this->headers = $parser->headers;
157            $this->body = trim($parser->body);
158            $this->status_code = $parser->status_code;
159
160
161            if (($this->status_code == 300 || $this->status_code == 301 || $this->status_code == 302 || $this->status_code == 303
162                    || $this->status_code == 307 || $this->status_code > 307 && $this->status_code < 400)
163                    && isset($this->headers['location']) && $this->redirects < $redirects) {
164                $this->redirects++;
165                $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
166                return $this->__construct($location, $timeout, $redirects, $headers);
167            }
168        }
169    }
170}
171
172
173/**
174 * Customised feed sanitization using HTMLPurifier.
175 */
176class moodle_simplepie_sanitize extends SimplePie_Sanitize {
177    public function sanitize($data, $type, $base = '') {
178        $data = trim($data);
179
180        if ($data === '') {
181            return '';
182        }
183
184        if ($type & SIMPLEPIE_CONSTRUCT_BASE64){
185            $data = base64_decode($data);
186        }
187
188        if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) {
189            if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) {
190                $type |= SIMPLEPIE_CONSTRUCT_HTML;
191            } else {
192                $type |= SIMPLEPIE_CONSTRUCT_TEXT;
193            }
194        }
195
196        if ($type & SIMPLEPIE_CONSTRUCT_IRI) {
197            $absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base));
198            if ($absolute !== false) {
199                $data = $absolute;
200            }
201            $data = clean_param($data, PARAM_URL);
202        }
203
204        if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) {
205            $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
206        }
207
208        $data = purify_html($data);
209
210        if ($this->remove_div) {
211            $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data);
212            $data = preg_replace('/<\/div>$/', '', $data);
213        } else {
214            $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
215        }
216
217        if ($this->output_encoding !== 'UTF-8') {
218            core_text::convert($data, 'UTF-8', $this->output_encoding);
219        }
220
221        return $data;
222    }
223}
224